From 75aca56c0117ccfd21e5d6d27655c3df3e88b879 Mon Sep 17 00:00:00 2001 From: Nastya Date: Mon, 27 Oct 2025 22:56:02 +0300 Subject: [PATCH] fix utm visual selector --- .../QuizInstallationCard/QuizMarkCreate.tsx | 4 +- src/pages/startPage/Components/OverTime.tsx | 128 ++++++++++++------ 2 files changed, 87 insertions(+), 45 deletions(-) diff --git a/src/pages/InstallQuiz/QuizInstallationCard/QuizMarkCreate.tsx b/src/pages/InstallQuiz/QuizInstallationCard/QuizMarkCreate.tsx index 254d3a61..4aa1060f 100644 --- a/src/pages/InstallQuiz/QuizInstallationCard/QuizMarkCreate.tsx +++ b/src/pages/InstallQuiz/QuizInstallationCard/QuizMarkCreate.tsx @@ -125,9 +125,7 @@ export default function QuizMarkCreate() { fullWidth size="small" sx={{ - - width: "100%", - maxWidth: "118px", + width: "137px", height: "24px", }} > diff --git a/src/pages/startPage/Components/OverTime.tsx b/src/pages/startPage/Components/OverTime.tsx index 38a1c896..aaa8760b 100644 --- a/src/pages/startPage/Components/OverTime.tsx +++ b/src/pages/startPage/Components/OverTime.tsx @@ -26,21 +26,36 @@ const OverTime = () => { Math.max(0, (initialOverTime.endsAt || 0) - Date.now()) ); + const [isEditing, setIsEditing] = useState(false); + const daysRef = useRef(null); const hoursRef = useRef(null); const minutesRef = useRef(null); const secondsRef = useRef(null); + const isAnyTimeInputFocused = (): boolean => { + const active = document.activeElement as HTMLElement | null; + return [daysRef.current, hoursRef.current, minutesRef.current, secondsRef.current].some( + (ref) => ref !== null && ref === active + ); + }; + const toDigits = (value: string): string => (value || "").replace(/\D+/g, ""); - const clampTwoDigits = (value: string): string => { - const digits = toDigits(value).slice(0, 2); - if (digits.length === 0) return ""; - const num = Number(digits); - if (isNaN(num)) return ""; - if (num > 60) return "60"; - return digits; - }; - const clampTwoDigitsDays = (value: string): string => toDigits(value).slice(0, 2); + const clampTwoDigits = (value: string): string => { + const digits = toDigits(value).slice(0, 2); + if (digits.length === 0) return ""; + const num = Number(digits); + if (isNaN(num)) return ""; + return num > 60 ? "60" : digits; + }; + const clampTwoDigitsDays = (value: string): string => { + return toDigits(value).slice(0, 2); + }; + + const pad2 = (value: string): string => { + const d = toDigits(value); + return (d.length === 0 ? "00" : d.padStart(2, "0").slice(-2)); + }; const allowControlKey = (e: React.KeyboardEvent) => { const allowed = ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "Tab", "Home", "End", "Enter"]; @@ -95,16 +110,6 @@ const OverTime = () => { const cfgEndsAt = (quiz as any)?.config?.overTime?.endsAt ?? 0; const ms = Math.max(0, cfgEndsAt - Date.now()); setRemainingMs(ms); - if (ms <= 0) { - // выключаем флаг - setEnabled(false); - updateQuiz(quiz!.id, (q) => { - const cfg: any = (q as any).config; - if (!cfg.overTime) cfg.overTime = { enabled: false, endsAt: 0, description: "" }; - cfg.overTime.enabled = false; - cfg.overTime.endsAt = 0; - }); - } }; // первый расчёт по requestAnimationFrame для мгновенного обновления rafId = window.requestAnimationFrame(() => tick()); @@ -135,6 +140,19 @@ const OverTime = () => { return { d, h, m, s }; }, [remainingMs]); + // Показываем обратный отсчёт в полях ввода, когда счётчик включён + useEffect(() => { + if (!enabled || isEditing) return; + const dStr = String(rem.d).padStart(2, "0").slice(-2); + const hStr = fmt(rem.h).slice(-2); + const mStr = fmt(rem.m).slice(-2); + const sStr = fmt(rem.s).slice(-2); + setDays(dStr); + setHours(hStr); + setMinutes(mStr); + setSeconds(sStr); + }, [enabled, rem, isEditing]); + if (!quiz) return null; return ( @@ -204,14 +222,22 @@ const OverTime = () => { { - const next = clampTwoDigitsDays(e.target.value); + onChange={(e) => { + const next = clampTwoDigitsDays(e.target.value); setDays(next); - persistEndsAt(next, hours, minutes, seconds); }} onKeyDown={handleTwoDigitKeyDown(days) as any} inputRef={daysRef} - onFocus={() => daysRef.current?.select()} + onFocus={() => { setIsEditing(true); daysRef.current?.select(); }} + onBlur={() => setTimeout(() => { + setIsEditing(isAnyTimeInputFocused()); + const nd = pad2(days); + const nh = pad2(hours); + const nm = pad2(minutes); + const ns = pad2(seconds); + setDays(nd); + persistEndsAt(nd, nh, nm, ns); + }, 0)} onClick={() => daysRef.current?.select()} InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }} sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }} @@ -231,14 +257,22 @@ const OverTime = () => { { - const next = clampTwoDigits(e.target.value); + onChange={(e) => { + const next = clampTwoDigits(e.target.value); setHours(next); - persistEndsAt(days, next, minutes, seconds); }} onKeyDown={handleTwoDigitKeyDown(hours) as any} inputRef={hoursRef} - onFocus={() => hoursRef.current?.select()} + onFocus={() => { setIsEditing(true); hoursRef.current?.select(); }} + onBlur={() => setTimeout(() => { + setIsEditing(isAnyTimeInputFocused()); + const nd = pad2(days); + const nh = pad2(hours); + const nm = pad2(minutes); + const ns = pad2(seconds); + setHours(nh); + persistEndsAt(nd, nh, nm, ns); + }, 0)} onClick={() => hoursRef.current?.select()} InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }} sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }} @@ -258,14 +292,22 @@ const OverTime = () => { { - const next = clampTwoDigits(e.target.value); + onChange={(e) => { + const next = clampTwoDigits(e.target.value); setMinutes(next); - persistEndsAt(days, hours, next, seconds); }} onKeyDown={handleTwoDigitKeyDown(minutes) as any} inputRef={minutesRef} - onFocus={() => minutesRef.current?.select()} + onFocus={() => { setIsEditing(true); minutesRef.current?.select(); }} + onBlur={() => setTimeout(() => { + setIsEditing(isAnyTimeInputFocused()); + const nd = pad2(days); + const nh = pad2(hours); + const nm = pad2(minutes); + const ns = pad2(seconds); + setMinutes(nm); + persistEndsAt(nd, nh, nm, ns); + }, 0)} onClick={() => minutesRef.current?.select()} InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }} sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }} @@ -285,14 +327,22 @@ const OverTime = () => { { - const next = clampTwoDigits(e.target.value); + onChange={(e) => { + const next = clampTwoDigits(e.target.value); setSeconds(next); - persistEndsAt(days, hours, minutes, next); }} onKeyDown={handleTwoDigitKeyDown(seconds) as any} inputRef={secondsRef} - onFocus={() => secondsRef.current?.select()} + onFocus={() => { setIsEditing(true); secondsRef.current?.select(); }} + onBlur={() => setTimeout(() => { + setIsEditing(isAnyTimeInputFocused()); + const nd = pad2(days); + const nh = pad2(hours); + const nm = pad2(minutes); + const ns = pad2(seconds); + setSeconds(ns); + persistEndsAt(nd, nh, nm, ns); + }, 0)} onClick={() => secondsRef.current?.select()} InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }} sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }} @@ -309,13 +359,7 @@ const OverTime = () => { - {enabled && ( - - - До конца: {rem.d} д {fmt(rem.h)}:{fmt(rem.m)}:{fmt(rem.s)} - - - )} + {/* Удалён маленький таймер с обратным отсчётом */} )