From 3d51aae6a9795a652d4bda3d5992e791bb9a3cdf Mon Sep 17 00:00:00 2001 From: Nastya Date: Wed, 22 Oct 2025 21:07:27 +0300 Subject: [PATCH] pin --- lib/components/QuizAnswerer.tsx | 25 +- .../StartPageViewPublication/OverTime.tsx | 213 ++++++++++++++++++ .../StartPageViewPublication/index.tsx | 80 +++++-- lib/model/settingsData.ts | 5 + .../themes/Publication/genericPublication.ts | 8 + .../themes/Publication/themePublication.ts | 81 ++++++- lib/utils/themes/generic.ts | 7 + lib/utils/themes/mui.d.ts | 10 +- public/locales/en.json | 8 +- public/locales/ru.json | 8 +- public/locales/uz.json | 8 +- src/i18n/i18nWidget.ts | 18 ++ src/types/mui-palette.d.ts | 14 ++ 13 files changed, 464 insertions(+), 21 deletions(-) create mode 100644 lib/components/ViewPublicationPage/StartPageViewPublication/OverTime.tsx create mode 100644 src/types/mui-palette.d.ts diff --git a/lib/components/QuizAnswerer.tsx b/lib/components/QuizAnswerer.tsx index 19dc1e9..8a8c040 100644 --- a/lib/components/QuizAnswerer.tsx +++ b/lib/components/QuizAnswerer.tsx @@ -13,7 +13,7 @@ import { handleComponentError } from "@utils/handleComponentError"; import lightTheme from "@utils/themes/light"; import moment from "moment"; import { SnackbarProvider } from "notistack"; -import { startTransition, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; +import React, { startTransition, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { ErrorBoundary } from "react-error-boundary"; import { ApologyPage } from "./ViewPublicationPage/ApologyPage"; import ViewPublicationPage from "./ViewPublicationPage/ViewPublicationPage"; @@ -51,6 +51,16 @@ function QuizAnswererInner({ const yandexMetrics = useYandexMetricsGoals(quizSettings?.settings.cfg.yandexMetricsNumber); const r = useQuizStore(); const { settings, questions } = useQuizStore(); + const [currentTime, setCurrentTime] = React.useState(moment()); + + // Реактивный таймер для отслеживания времени + React.useEffect(() => { + const interval = setInterval(() => { + setCurrentTime(moment()); + }, 1000); + + return () => clearInterval(interval); + }, []); useEffect(() => { addquizid(quizId); @@ -114,6 +124,19 @@ function QuizAnswererInner({ return ; } + // Проверяем, истекло ли время для overTime + const overTimeConfig = settings?.cfg?.overTime; + const isOverTimeEnabled = overTimeConfig?.enabled; + const isTimeExpired = + isOverTimeEnabled && overTimeConfig?.endsAt + ? currentTime.isAfter(moment(overTimeConfig.endsAt)) || currentTime.isSame(moment(overTimeConfig.endsAt)) + : false; + + // Если время истекло и нет стартовой страницы, показываем ApologyPage + if (isTimeExpired && settings?.cfg?.noStartPage) { + return ; + } + const quizContainer = ( ; +}; + +export const OverTime = ({ sx }: OverTimeProps) => { + const theme = useTheme(); + const { settings } = useQuizStore(); + const { t } = useTranslation(); + const [currentTime, setCurrentTime] = React.useState(moment()); + + // Реактивный таймер с useEffect + React.useEffect(() => { + const interval = setInterval(() => { + setCurrentTime(moment()); + }, 1000); + + return () => clearInterval(interval); + }, []); + + // Проверяем, включен ли overTime + const overTimeConfig = settings?.cfg?.overTime; + const isEnabled = overTimeConfig?.enabled; + + // Если не включен, не показываем карточку + if (!isEnabled) { + return null; + } + + // Функция для расчета времени до окончания + const calculateTimeLeft = (now: moment.Moment) => { + // Для тестирования: добавляем 2 часа к текущему времени + const testEndsAt = moment().add(2, "hours"); + const endsAt = overTimeConfig?.endsAt ? moment(overTimeConfig.endsAt) : testEndsAt; + + if (endsAt.isBefore(now) || endsAt.isSame(now)) { + return { days: 0, hours: 0, minutes: 0, seconds: 0 }; + } + + const duration = moment.duration(endsAt.diff(now)); + + return { + days: Math.floor(duration.asDays()), + hours: duration.hours(), + minutes: duration.minutes(), + seconds: duration.seconds(), + }; + }; + + const { days, hours, minutes, seconds } = calculateTimeLeft(currentTime); + + return ( + + + {overTimeConfig?.description || t("Quiz will become unavailable in")} + + + + + {days} + + + {t("days")} + + + : + + + {hours} + + + {t("hours")} + + + : + + + {minutes} + + + {t("minutes")} + + + : + + + {seconds} + + + {t("seconds")} + + + + + ); +}; diff --git a/lib/components/ViewPublicationPage/StartPageViewPublication/index.tsx b/lib/components/ViewPublicationPage/StartPageViewPublication/index.tsx index 5985654..5c5b127 100644 --- a/lib/components/ViewPublicationPage/StartPageViewPublication/index.tsx +++ b/lib/components/ViewPublicationPage/StartPageViewPublication/index.tsx @@ -1,6 +1,9 @@ import { Box, Button, ButtonBase, Link, Paper, Typography, useTheme } from "@mui/material"; +import moment from "moment"; +import React from "react"; import { QuizPreviewLayoutByType } from "./QuizPreviewLayoutByType"; +import { OverTime } from "./OverTime"; import { useQuizStore } from "@/stores/useQuizStore"; import { useRootContainerSize } from "@contexts/RootContainerWidthContext"; @@ -24,6 +27,16 @@ export const StartPageViewPublication = () => { const { settings, show_badge, quizId, questions } = useQuizStore(); const { isMobileDevice } = useUADevice(); const setCurrentQuizStep = useQuizViewStore((state) => state.setCurrentQuizStep); + const [currentTime, setCurrentTime] = React.useState(moment()); + + // Реактивный таймер для обновления времени + React.useEffect(() => { + const interval = setInterval(() => { + setCurrentTime(moment()); + }, 1000); + + return () => clearInterval(interval); + }, []); const size = useRootContainerSize(); const isMobile = size < 700; @@ -39,15 +52,6 @@ export const StartPageViewPublication = () => { yandexMetrics.phoneNumberOpened(); }; - console.log("------------------------------------------------"); - console.log("Background type:", settings.cfg.startpage.background.type); - console.log("Is image type:", settings.cfg.startpage.background.type === "image"); - console.log("Is video type:", settings.cfg.startpage.background.type === "video"); - console.log("Video URL:", settings.cfg.startpage.background.video); - console.log("Desktop background:", settings.cfg.startpage.background.desktop); - console.log("Startpage type:", settings.cfg.startpageType); - console.log("------------------------------------------------"); - const background = settings.cfg.startpage.background.type === "image" ? ( { ) : settings.cfg.startpage.background.type === "video" ? ( settings.cfg.startpage.background.video ? ( (() => { - console.log("Rendering QuizVideo with URL:", settings.cfg.startpage.background.video); return ( { { {settings.cfg.info.orgname} + {((settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position !== "center") || + (isMobile && settings.cfg.startpageType === "expanded")) && } ); @@ -159,12 +170,12 @@ export const StartPageViewPublication = () => { alignItems: "center", gap: "7px", textDecoration: "none", - marginLeft: + mr: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && !isTablet && !isMobile - ? "61px" + ? "27px" : undefined, }} > @@ -182,6 +193,14 @@ export const StartPageViewPublication = () => { (question) => question.type !== null && question.type !== "result" ).length; + // Проверяем, истекло ли время для overTime + const overTimeConfig = settings?.cfg?.overTime; + const isOverTimeEnabled = overTimeConfig?.enabled; + const isTimeExpired = + isOverTimeEnabled && overTimeConfig?.endsAt + ? currentTime.isAfter(moment(overTimeConfig.endsAt)) || currentTime.isSame(moment(overTimeConfig.endsAt)) + : false; + const onQuizStart = () => { setCurrentQuizStep("question"); @@ -289,7 +308,7 @@ export const StartPageViewPublication = () => {