diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 1afafe7c..2512de6b 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -160,6 +160,8 @@ export default { access: 'Access', answers: 'Answers', answer: 'Answer', + webinars: "Webinars", + 'webinars.screenSaves': 'Screen saves', 'menu.Webinars': 'Webinars', 'menu.NewWebinar': 'New webinar', 'menu.Courses': 'Courses', diff --git a/src/locales/pl-PL.ts b/src/locales/pl-PL.ts index a3e450b2..190d2a8d 100644 --- a/src/locales/pl-PL.ts +++ b/src/locales/pl-PL.ts @@ -167,7 +167,12 @@ export default { 'menu.Other activities.StationaryEvents': 'Wydarzenia stacjonarne', 'menu.Other activities.Questionnaire Form': 'Ankiety', 'menu.Other activities.Dictionary': 'Słownik', + 'menu.Other activities.Word': 'Słowo', 'stationary_event.edit': 'Formularz Wydarzenia stacjonarne', + 'menu.My Profile': 'Mój profil', + 'menu.My Profile.My Profile': 'Mój profil', + Webinars: "Webinary", + 'webinars.screenSaves': 'Zapis Ekranu', stationary_event: 'Wydarzenie stacjonarne', 'menu.reset': 'reset', finished_at: 'Data zakończenia', @@ -235,6 +240,7 @@ export default { recording_will_be_deleted: 'Po tym czasie zostanie ono skasowane', engagement_rating: 'Ocena zaangażowania', ai_analysis_average: 'Średni wynik analizy AI dla całego nagrania {modelType}.', + ai_warn_video_buffer: "Video nagrania jest obecnie przetwarzane, zdjęcia podglądu nagrania na wykresie w poszczególnych przedziałach czasowych mogą być obecnie niedostępne. Spróbuj ponowanie za kilka minut.", consultationFragment: "konsultacji", webinarFragment: "webinaru", resolution: 'Rozdzielczość danych', diff --git a/src/pages/Consultations/components/EffectivenessAnalysis.tsx b/src/pages/Consultations/components/EffectivenessAnalysis.tsx index c2c80dab..e7692465 100644 --- a/src/pages/Consultations/components/EffectivenessAnalysis.tsx +++ b/src/pages/Consultations/components/EffectivenessAnalysis.tsx @@ -262,7 +262,7 @@ export const EffectivenessAnalysis = ({ page: current, date_from, date_to, - ...createTableOrderObject(sort, 'term'), + ...createTableOrderObject(sort, 'id'), }); setLoading(false); @@ -282,10 +282,6 @@ export const EffectivenessAnalysis = ({ } }} columns={columns} - pagination={{ - pageSize: 10, - showSizeChanger: true, - }} /> ); }; diff --git a/src/pages/Consultations/components/EffectivenessAnalysisDetails.tsx b/src/pages/Consultations/components/EffectivenessAnalysisDetails.tsx index b1817f65..4345e7cc 100644 --- a/src/pages/Consultations/components/EffectivenessAnalysisDetails.tsx +++ b/src/pages/Consultations/components/EffectivenessAnalysisDetails.tsx @@ -7,7 +7,7 @@ import type { } from '@/pages/Consultations/components/types'; import { getAnalyticsChartFrames, getModelAnalytics } from '@/services/escola-lms/consultations'; import { - ANALYSIS_COLORS, EmotionKey, formatRating, getLabelColorByValue, + ANALYSIS_COLORS, EmotionKey, formatRating, getRatingLabelColorByValue } from '@/utils/utils'; import { PageContainer } from '@ant-design/pro-components'; @@ -15,12 +15,15 @@ import { Card, Col, Select, Space, Typography, message } from 'antd'; import React, { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import { FormattedMessage, Link, useParams, useSelectedRoutes } from 'umi'; +import {WarningOutlined} from "@ant-design/icons"; const { Text } = Typography; const PageWrapper = styled.div` padding: 0; min-height: 100vh; + background: ${ANALYSIS_COLORS.bgLight}; + border-radius: 10px; `; const StyledCard = styled(Card)` @@ -65,6 +68,20 @@ const SectionTitle = styled(Text)` font-size: 16px; `; +const VideoScreenWarning = styled.div` + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; + color: ${ANALYSIS_COLORS.orange}; +`; + +const VideoScreenWarningText = styled(Text)` + margin: 0; + color: ${ANALYSIS_COLORS.orange}; +`; + + const TIME_OPTIONS = [ { value: 15, label: }, { value: 30, label: }, @@ -79,17 +96,11 @@ const EffectivenessAnalysisDetails = () => { const [analysisMeta, setAnalysisMeta] = useState(null); const [chartData, setChartData] = useState([]); const routes = useSelectedRoutes(); - const color = useMemo( - () => getLabelColorByValue(analysisMeta?.rating ? analysisMeta.rating : 0), - [analysisMeta?.rating], - ); const ratingColor = useMemo( () => getRatingLabelColorByValue(analysisMeta?.rating ? analysisMeta.rating : 0), [analysisMeta?.rating], ); - console.log(color, 'color'); - const modelType = useMemo(() => { const currentRoute = routes[routes.length - 1]?.route as any; return currentRoute?.modelType; @@ -220,7 +231,14 @@ const EffectivenessAnalysisDetails = () => { - + {analysisMeta?.processing_video && ( + + + + + + + )} {chartData && ( [] = [ - { - title: , - dataIndex: 'consultation_term_id', - hideInSearch: true, - sorter: true, - width: '80px', - }, - { - title: , - dataIndex: 'user', - render: (_, item) => `${item.user?.first_name} ${item.user?.last_name} ${item.user?.email}`, - sorter: true, - }, - { - title: , - dataIndex: 'date', - sorter: true, - render: (_, item) => moment(item.date).format('YYYY-MM-DD HH:mm'), - }, - { - title: , - dataIndex: 'status', - sorter: true, - render: (_, item) => ( - } - /> - ), - }, -]; +interface Props { + consultation?: number; + webinar?: number; + webinarTimestamp?: number; +} -const ScreenSaves: React.FC<{ consultation: number }> = ({ consultation }) => { +const ScreenSaves: React.FC = ({ consultation, webinar, webinarTimestamp }) => { const [loading, setLoading] = useState(false); const [appointments, setAppointments] = useState([]); + const [webinarUsers, setWebinarUsers] = useState([]); const intl = useIntl(); + const resourceId = consultation || webinar; + + const consultationColumns = useMemo( + (): ProColumns[] => [ + { + title: , + dataIndex: 'consultation_term_id', + hideInSearch: true, + sorter: true, + width: 80, + }, + { + title: , + dataIndex: 'user', + sorter: true, + render: (_, item) => + `${item.user?.first_name ?? ''} ${item.user?.last_name ?? ''} ${item.user?.email ?? ''}`, + }, + { + title: , + dataIndex: 'date', + sorter: true, + render: (_, item) => moment(item.date).format('YYYY-MM-DD HH:mm'), + }, + { + title: , + dataIndex: 'status', + sorter: true, + render: (_, item) => ( + } + /> + ), + }, + { + title: 'Analiza obrazu', + dataIndex: 'files', + render: (_, item) => ( + + ), + }, + ], + [resourceId], + ); + + const webinarColumns = useMemo( + (): ProColumns[] => [ + { + title: , + dataIndex: 'id', + hideInSearch: true, + sorter: true, + width: 80, + }, + { + title: , + dataIndex: 'user', + sorter: true, + render: (_, item) => + `${item.first_name ?? ''} ${item.last_name ?? ''} ${item.email ?? ''}`, + }, + { + title: , + dataIndex: 'active_to', + render: () => + webinarTimestamp + ? moment.unix(webinarTimestamp).format('YYYY-MM-DD HH:mm') + : '-', + }, + { + title: 'Analiza obrazu', + dataIndex: 'files', + render: (_, item) => ( + + ), + }, + ], + [resourceId], + ); + + const fetchData = useCallback(() => { + if (!resourceId) return; - const fetchAppointments = useCallback(() => { setLoading(true); - getSchedule(consultation) + + const promise = consultation + ? getConsultationSchedule(consultation) + : getWebinarUsers(webinar as number); + + promise .then((response) => { - if (response.success) { - setAppointments(response.data); + if (!response?.success) return; + if (consultation) { + setAppointments(response.data as API.ConsultationAppointment[]); + } else { + setWebinarUsers(response.data as API.UserItem[]); } }) .catch(() => { message.error(); }) .finally(() => setLoading(false)); - }, [consultation]); + }, [consultation, webinar, resourceId]); useEffect(() => { - fetchAppointments(); - }, [consultation]); - - return ( - - {loading ? ( - - ) : ( - - headerTitle={intl.formatMessage({ - id: 'Consultations', - defaultMessage: 'Consultations', - })} - loading={loading} - rowKey="consultation_term_id" - search={false} - request={async ({}, sort) => { - const sortArr = sort && Object.entries(sort)[0]; - let newArray = appointments.filter((item) => item.status === 'approved'); - - if (sortArr) { - newArray = sortArrayByKey( - newArray, - sortArr[0], - sortArr[1] === 'ascend' ? false : true, - ); - } - return { - data: newArray, - total: newArray.length, - success: true, - }; - }} - columns={[ - ...TableColumns, - - { - title: 'Analiza obrazu', - dataIndex: 'status', - sorter: true, - render: (_, item) => ( -
- -
- ), - }, - ]} - /> - )} -
- ); + fetchData(); + }, [fetchData]); + + if (loading) { + return ; + } + + if (consultation) { + return ( + + headerTitle={intl.formatMessage({ + id: 'Consultations', + defaultMessage: 'Consultations', + })} + loading={loading} + rowKey="consultation_term_id" + search={false} + options={{ + reload: false, + }} + dataSource={appointments} + request={async (_params, sort) => { + const sortArr = sort && Object.entries(sort)[0]; + let filteredData = appointments.filter((item) => item.status === 'approved'); + + if (sortArr) { + filteredData = sortArrayByKey( + filteredData, + sortArr[0], + sortArr[1] !== 'ascend', + ); + } + return { + data: filteredData, + total: filteredData.length, + success: true, + }; + }} + columns={consultationColumns} + /> + ); + } + + if (webinar) { + return ( + + headerTitle={intl.formatMessage({ + id: 'Webinars', + defaultMessage: 'Webinars', + })} + loading={loading} + rowKey="id" + search={false} + options={{ + reload: false, + }} + dataSource={webinarUsers} + request={async (_params, sort) => { + const sortArr = sort && Object.entries(sort)[0]; + let filteredData = [...webinarUsers]; + + if (sortArr) { + filteredData = sortArrayByKey( + filteredData, + sortArr[0], + sortArr[1] !== 'ascend', + ); + } + return { + data: filteredData, + total: filteredData.length, + success: true, + }; + }} + columns={webinarColumns} + /> + ); + } + + return null; }; export default ScreenSaves; diff --git a/src/pages/Consultations/components/types.ts b/src/pages/Consultations/components/types.ts index f4997378..4e52e500 100644 --- a/src/pages/Consultations/components/types.ts +++ b/src/pages/Consultations/components/types.ts @@ -4,6 +4,7 @@ export interface AnalysisMeta { rating: number; url: string | null; url_expiration_time_millis: number | null; + processing_video?: boolean; } export interface AnalysisData { diff --git a/src/pages/Webinars/form.tsx b/src/pages/Webinars/form.tsx index 2d6501f2..b44601bb 100644 --- a/src/pages/Webinars/form.tsx +++ b/src/pages/Webinars/form.tsx @@ -20,6 +20,9 @@ import { PageContainer } from '@ant-design/pro-layout'; import { Alert, Button, Col, Row, Spin, message } from 'antd'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { FormattedMessage, history, useIntl, useParams } from 'umi'; +import ScreenSaves from "@/pages/Consultations/components/ScreenSaves"; +import {settings} from "@/services/escola-lms/settings"; +import moment from "moment"; enum TabNames { ATTRIBUTES = 'attributes', @@ -28,6 +31,7 @@ enum TabNames { TAGS = 'tags', BRANDING = 'branding', USER_SUBMISSION = 'user_submission', + SCREENSAVES = 'screensaves', } const WebinarForm = () => { @@ -37,10 +41,15 @@ const WebinarForm = () => { const isNew = webinar === 'new'; const [data, setData] = useState>(); const { manageCourseEdit, setManageCourseEdit, validateCourseEdit } = useValidateFormEdit(); - + const [showScreenSaves, setShowScreenSaves] = useState(false); const [form] = ProForm.useForm(); const fetchData = useCallback(async () => { + const config = await settings({ per_page: -1 }); + + if ('data' in config) { + setShowScreenSaves(config.data.find((c) => c.key === 'show_screen_saves')?.value === '1'); + } const response = await getWebinar(Number(webinar)); if (response.success) { if (tab === TabNames.ATTRIBUTES) { @@ -445,6 +454,14 @@ const WebinarForm = () => { {webinar && } )} + {!isNew && showScreenSaves && ( + } + > + + + )} {/* CONFIRM MODAL COMPONENT */} >( + `/api/admin/webinars/${webinarId}/users`, + { + method: 'GET', + ...(options || {}), + }, + ); +}