логика отображения времени
This commit is contained in:
parent
2ea14d81f8
commit
0d57508966
@ -25,6 +25,8 @@ import { type ReactNode } from "react";
|
||||
import { isProduction } from "@/utils/defineDomain";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { CustomCircularTimer } from "@/ui_kit/timer/CircularTimer";
|
||||
import { useQuestionTimer } from "@/utils/hooks/FlowControlLogic/useQuestionTimer";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
type Props = {
|
||||
currentQuestion: RealTypedQuizQuestion;
|
||||
@ -42,7 +44,38 @@ export const Question = ({
|
||||
questionSelect,
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const { settings, show_badge, quizId } = useQuizStore();
|
||||
const { settings, show_badge, quizId, preview } = useQuizStore();
|
||||
|
||||
// Состояние для отслеживания оставшегося времени
|
||||
const [remainingTime, setRemainingTime] = useState<number>(0);
|
||||
const [isTimerActive, setIsTimerActive] = useState<boolean>(false);
|
||||
|
||||
// Получаем настройки таймера
|
||||
const timerEnabled = Boolean(settings.questionTimerEnabled);
|
||||
const timerDuration = settings.cfg.time_of_passing ?? 0;
|
||||
|
||||
// Эффект для обновления таймера
|
||||
useEffect(() => {
|
||||
if (timerEnabled && timerDuration > 0) {
|
||||
setRemainingTime(timerDuration);
|
||||
setIsTimerActive(true);
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setRemainingTime((prev) => {
|
||||
if (prev <= 1) {
|
||||
setIsTimerActive(false);
|
||||
return 0;
|
||||
}
|
||||
return prev - 1;
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
} else {
|
||||
setIsTimerActive(false);
|
||||
setRemainingTime(0);
|
||||
}
|
||||
}, [timerEnabled, timerDuration, currentQuestion.id]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -105,12 +138,15 @@ export const Question = ({
|
||||
gap: "13px",
|
||||
}}
|
||||
>
|
||||
{
|
||||
{timerEnabled && isTimerActive && (
|
||||
<CustomCircularTimer
|
||||
duration={60}
|
||||
remaining={30}
|
||||
duration={timerDuration}
|
||||
remaining={remainingTime}
|
||||
showTime={true}
|
||||
size={76}
|
||||
thickness={4}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
<Link
|
||||
target="_blank"
|
||||
href={`https://${isProduction ? "" : "s"}quiz.pena.digital/answer/v1.0.0/logo?q=${quizId}`}
|
||||
|
||||
@ -68,7 +68,12 @@ export default function ViewPublicationPage() {
|
||||
);
|
||||
|
||||
if (settings.cfg.antifraud && recentlyCompleted) throw new Error("Quiz already completed");
|
||||
if (currentQuizStep === "startpage" && settings.cfg.noStartPage) currentQuizStep = "question";
|
||||
if (currentQuizStep === "startpage" && settings.cfg.noStartPage) {
|
||||
// Обновляем состояние в store, а не только локальную переменную
|
||||
const setCurrentQuizStep = useQuizViewStore((state) => state.setCurrentQuizStep);
|
||||
setCurrentQuizStep("question");
|
||||
currentQuizStep = "question";
|
||||
}
|
||||
|
||||
if (!currentQuestion) {
|
||||
return (
|
||||
|
||||
@ -1,5 +1,15 @@
|
||||
import { CircularProgress, Box, Typography, useTheme, styled } from "@mui/material";
|
||||
|
||||
// Типизация для пропсов таймера
|
||||
export interface CircularTimerProps {
|
||||
duration: number; // Общая длительность в секундах
|
||||
remaining: number; // Оставшееся время в секундах
|
||||
showTime?: boolean; // Показывать ли время в формате mm:ss
|
||||
size?: number; // Размер таймера
|
||||
thickness?: number; // Толщина линии прогресса
|
||||
color?: string; // Цвет прогресса
|
||||
}
|
||||
|
||||
const StyledCircularProgress = styled(CircularProgress)(({ theme }) => ({
|
||||
"& .MuiCircularProgress-circle": {
|
||||
strokeLinecap: "round",
|
||||
@ -7,19 +17,33 @@ const StyledCircularProgress = styled(CircularProgress)(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export const CustomCircularTimer: React.FC<CircularTimerProps> = ({ duration, remaining }) => {
|
||||
// Функция для форматирования времени в mm:ss
|
||||
const formatTime = (seconds: number): string => {
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const remainingSeconds = Math.floor(seconds % 60);
|
||||
return `${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
export const CustomCircularTimer: React.FC<CircularTimerProps> = ({
|
||||
duration,
|
||||
remaining,
|
||||
showTime = true,
|
||||
size = 76,
|
||||
thickness = 4,
|
||||
color,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const progress = (remaining / duration) * 100;
|
||||
|
||||
return (
|
||||
<Box sx={{ position: "relative", display: "inline-flex", width: 76, height: 76 }}>
|
||||
<Box sx={{ position: "relative", display: "inline-flex", width: size, height: size }}>
|
||||
{/* Серый фон */}
|
||||
<Box
|
||||
sx={{
|
||||
border: "#9A9AAF solid 1px",
|
||||
position: "absolute",
|
||||
height: "72px",
|
||||
width: "72px",
|
||||
height: `${size - 4}px`,
|
||||
width: `${size - 4}px`,
|
||||
borderRadius: "100%",
|
||||
top: "2px",
|
||||
left: "2px",
|
||||
@ -30,15 +54,16 @@ export const CustomCircularTimer: React.FC<CircularTimerProps> = ({ duration, re
|
||||
<StyledCircularProgress
|
||||
variant="determinate"
|
||||
value={progress}
|
||||
size={76}
|
||||
thickness={4}
|
||||
size={size}
|
||||
thickness={thickness}
|
||||
sx={{
|
||||
color: "linear-gradient(135deg, #FC712F 0%, #7E2AEA 100%)",
|
||||
color: color || "linear-gradient(135deg, #FC712F 0%, #7E2AEA 100%)",
|
||||
position: "absolute",
|
||||
|
||||
"& .MuiCircularProgress-circle": {
|
||||
strokeLinecap: "round",
|
||||
stroke: "url(#timer-gradient)", // ← правильное использование
|
||||
stroke: color ? undefined : "url(#timer-gradient)",
|
||||
strokeDasharray: color ? undefined : undefined,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
@ -74,8 +99,8 @@ export const CustomCircularTimer: React.FC<CircularTimerProps> = ({ duration, re
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 56,
|
||||
height: 56,
|
||||
width: `${size - 20}px`,
|
||||
height: `${size - 20}px`,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
@ -85,12 +110,14 @@ export const CustomCircularTimer: React.FC<CircularTimerProps> = ({ duration, re
|
||||
variant="body1"
|
||||
fontWeight="bold"
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
fontWeight: 400,
|
||||
fontSize: size > 60 ? "16px" : "12px",
|
||||
fontWeight: 600,
|
||||
color: theme.palette.text.primary,
|
||||
textAlign: "center",
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
{remaining}
|
||||
{showTime ? formatTime(remaining) : remaining}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@ -28,6 +28,7 @@ export function useQuestionTimer({ enabled, seconds, quizId, preview, currentQue
|
||||
currentQuestionType: currentQuestion?.type,
|
||||
currentQuizStep,
|
||||
hasCurrentQuestion: !!currentQuestion,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
if (!enabled) {
|
||||
|
||||
@ -2,6 +2,7 @@ import { useBranchingQuiz } from "./FlowControlLogic/useBranchingQuiz";
|
||||
import { useLinearQuiz } from "./FlowControlLogic/useLinearQuiz";
|
||||
import { useAIQuiz } from "./FlowControlLogic/useAIQuiz";
|
||||
import { Status } from "@/model/settingsData";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
interface StatusData {
|
||||
status: Status;
|
||||
@ -11,6 +12,7 @@ interface StatusData {
|
||||
// выбор способа управления в зависимости от статуса
|
||||
let cachedManager: () => ReturnType<typeof useLinearQuiz>;
|
||||
export let statusOfQuiz: "line" | "branch" | "ai";
|
||||
let isInitialized = false;
|
||||
|
||||
function analyicStatus({ status, haveRoot }: StatusData) {
|
||||
if (status === "ai") {
|
||||
@ -27,6 +29,7 @@ function analyicStatus({ status, haveRoot }: StatusData) {
|
||||
}
|
||||
|
||||
export const initDataManager = (data: StatusData) => {
|
||||
console.log("🔧 Initializing DataManager with:", data);
|
||||
analyicStatus(data);
|
||||
switch (statusOfQuiz) {
|
||||
case "line":
|
||||
@ -39,12 +42,32 @@ export const initDataManager = (data: StatusData) => {
|
||||
cachedManager = useAIQuiz;
|
||||
break;
|
||||
}
|
||||
isInitialized = true;
|
||||
console.log("✅ DataManager initialized with type:", statusOfQuiz);
|
||||
};
|
||||
|
||||
// Главный хук (интерфейс для потребителей)
|
||||
export const useQuestionFlowControl = () => {
|
||||
if (!cachedManager) {
|
||||
if (!cachedManager || !isInitialized) {
|
||||
// Попытка автоматической инициализации на основе текущих настроек
|
||||
const { settings } = useQuizStore.getState();
|
||||
if (settings && settings.status) {
|
||||
console.log("🔄 Auto-initializing DataManager with settings:", settings);
|
||||
initDataManager({
|
||||
status: settings.status,
|
||||
haveRoot: settings.cfg.haveRoot,
|
||||
});
|
||||
} else {
|
||||
throw new Error("DataManager not initialized! Call initDataManager() first.");
|
||||
}
|
||||
}
|
||||
return cachedManager();
|
||||
};
|
||||
|
||||
// Функция для сброса состояния (полезна для HMR)
|
||||
export const resetDataManager = () => {
|
||||
console.log("🔄 Resetting DataManager");
|
||||
cachedManager = null as any;
|
||||
isInitialized = false;
|
||||
statusOfQuiz = null as any;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user