логика овертайм
This commit is contained in:
parent
378eeb0068
commit
639929d825
@ -1,4 +1,5 @@
|
|||||||
1.0.13 _ 2025-10-18 _ новый визуал таймера с логикой + визуал овертайм
|
1.0.14 _ 2025-10-20 _ utm
|
||||||
|
1.0.13 _ 2025-10-18 _ Визуал utm + логика
|
||||||
1.0.12 _ 2025-10-12 _ ютм с дизайном и беком, но без логики
|
1.0.12 _ 2025-10-12 _ ютм с дизайном и беком, но без логики
|
||||||
1.0.11 _ 2025-10-06 _ Merge branch 'staging'
|
1.0.11 _ 2025-10-06 _ Merge branch 'staging'
|
||||||
1.0.10 _ 2025-10-05 _ utm
|
1.0.10 _ 2025-10-05 _ utm
|
||||||
|
|||||||
BIN
src.zip
Normal file
BIN
src.zip
Normal file
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
import { FC, useEffect, useMemo, useState } from "react";
|
import { FC, useEffect, useMemo, useState } from "react";
|
||||||
import { Box, IconButton, List, ListItem, Typography, useTheme, useMediaQuery, Pagination } from "@mui/material";
|
import { Box, IconButton, List, ListItem, Typography, useTheme, useMediaQuery } from "@mui/material";
|
||||||
import TrashIcon from "@/assets/icons/TrashIcon";
|
import TrashIcon from "@/assets/icons/TrashIcon";
|
||||||
import { type UtmRecord } from "@/api/utm";
|
import { type UtmRecord } from "@/api/utm";
|
||||||
import CopyIcon from "@/assets/icons/CopyIcon";
|
import CopyIcon from "@/assets/icons/CopyIcon";
|
||||||
@ -156,7 +156,7 @@ export const UtmList: FC<UtmListProps> = ({ items, loading, error, onDelete, qui
|
|||||||
)}
|
)}
|
||||||
{items.length > perPage && (
|
{items.length > perPage && (
|
||||||
<Box sx={{ display: "flex", justifyContent: "end", mt: 1 }}>
|
<Box sx={{ display: "flex", justifyContent: "end", mt: 1 }}>
|
||||||
<CustomPagination countPagination={totalPages} page={page} onChange={(e, v) => setPage(v)} size="small" />
|
<CustomPagination countPagination={totalPages} page={page} onChange={(e, v) => setPage(v)} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -1,17 +1,156 @@
|
|||||||
import { useCurrentQuiz } from "@/stores/quizes/hooks";
|
import { useCurrentQuiz } from "@/stores/quizes/hooks";
|
||||||
|
import { updateQuiz } from "@/stores/quizes/actions";
|
||||||
import CustomTextField from "@/ui_kit/CustomTextField"
|
import CustomTextField from "@/ui_kit/CustomTextField"
|
||||||
import { Box, Typography, useTheme } from "@mui/material"
|
import { Box, Typography, useTheme } from "@mui/material"
|
||||||
import CustomizedSwitch from "@ui_kit/CustomSwitch";
|
import CustomizedSwitch from "@ui_kit/CustomSwitch";
|
||||||
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
const OverTime = () => {
|
const OverTime = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
|
||||||
|
const initialOverTime = useMemo(() => {
|
||||||
|
const cfg = (quiz as any)?.config ?? {};
|
||||||
|
const ot = cfg.overTime ?? { enabled: false, endsAt: 0, description: "" };
|
||||||
|
return ot as { enabled: boolean; endsAt: number; description: string };
|
||||||
|
}, [(quiz as any)?.config?.overTime]);
|
||||||
|
|
||||||
|
const [enabled, setEnabled] = useState<boolean>(Boolean(initialOverTime.enabled));
|
||||||
|
const [description, setDescription] = useState<string>(initialOverTime.description || "");
|
||||||
|
const [days, setDays] = useState<string>("00");
|
||||||
|
const [hours, setHours] = useState<string>("00");
|
||||||
|
const [minutes, setMinutes] = useState<string>("00");
|
||||||
|
const [seconds, setSeconds] = useState<string>("00");
|
||||||
|
|
||||||
|
const [remainingMs, setRemainingMs] = useState<number>(
|
||||||
|
Math.max(0, (initialOverTime.endsAt || 0) - Date.now())
|
||||||
|
);
|
||||||
|
|
||||||
|
const daysRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
const hoursRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
const minutesRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
const secondsRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
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 allowControlKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
const allowed = ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "Tab", "Home", "End", "Enter"];
|
||||||
|
return allowed.includes(e.key);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDigitKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (allowControlKey(e)) return;
|
||||||
|
if (!/^[0-9]$/.test(e.key)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTwoDigitKeyDown = (value: string) => (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (allowControlKey(e)) return;
|
||||||
|
const isDigit = /^[0-9]$/.test(e.key);
|
||||||
|
if (!isDigit) {
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = e.currentTarget;
|
||||||
|
const selectionStart = target.selectionStart ?? 0;
|
||||||
|
const selectionEnd = target.selectionEnd ?? 0;
|
||||||
|
const selectionLength = Math.max(0, selectionEnd - selectionStart);
|
||||||
|
const currentLength = toDigits(value).length;
|
||||||
|
if (selectionLength === 0 && currentLength >= 2) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const persistEndsAt = (dStr: string, hStr: string, mStr: string, sStr: string) => {
|
||||||
|
const d = Number(toDigits(dStr) || 0);
|
||||||
|
const h = Number(toDigits(hStr) || 0);
|
||||||
|
const m = Number(toDigits(mStr) || 0);
|
||||||
|
const s = Number(toDigits(sStr) || 0);
|
||||||
|
const totalMs = (((d * 24 + h) * 60 + m) * 60 + s) * 1000;
|
||||||
|
const endsAt = Date.now() + Math.max(0, totalMs);
|
||||||
|
setRemainingMs(Math.max(0, endsAt - Date.now()));
|
||||||
|
updateQuiz(quiz!.id, (q) => {
|
||||||
|
const cfg: any = (q as any).config;
|
||||||
|
cfg.overTime = cfg.overTime || { enabled: false, endsAt: 0, description: "" };
|
||||||
|
cfg.overTime.endsAt = endsAt;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Тикер обратного отсчёта
|
||||||
|
useEffect(() => {
|
||||||
|
if (!enabled) return;
|
||||||
|
let rafId: number | null = null;
|
||||||
|
let timerId: ReturnType<typeof setInterval> | null = null;
|
||||||
|
const tick = () => {
|
||||||
|
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());
|
||||||
|
timerId = setInterval(tick, 1000);
|
||||||
|
return () => {
|
||||||
|
if (rafId) cancelAnimationFrame(rafId);
|
||||||
|
if (timerId) clearInterval(timerId);
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [enabled, (quiz as any)?.config?.overTime?.endsAt]);
|
||||||
|
|
||||||
|
// Синхронизация, если config.overTime обновился извне
|
||||||
|
useEffect(() => {
|
||||||
|
const cfg = (quiz as any)?.config?.overTime;
|
||||||
|
if (!cfg) return;
|
||||||
|
setEnabled(Boolean(cfg.enabled));
|
||||||
|
setDescription(cfg.description || "");
|
||||||
|
setRemainingMs(Math.max(0, (cfg.endsAt || 0) - Date.now()));
|
||||||
|
}, [(quiz as any)?.config?.overTime]);
|
||||||
|
|
||||||
|
const fmt = (n: number) => (n < 10 ? `0${n}` : String(n));
|
||||||
|
const rem = useMemo(() => {
|
||||||
|
const totalSec = Math.floor(remainingMs / 1000);
|
||||||
|
const d = Math.floor(totalSec / (24 * 3600));
|
||||||
|
const h = Math.floor((totalSec % (24 * 3600)) / 3600);
|
||||||
|
const m = Math.floor((totalSec % 3600) / 60);
|
||||||
|
const s = totalSec % 60;
|
||||||
|
return { d, h, m, s };
|
||||||
|
}, [remainingMs]);
|
||||||
|
|
||||||
if (!quiz) return null;
|
if (!quiz) return null;
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", mt: "20px", padding: "15px", backgroundColor: "#F2F3F7", width: "100%", maxWidth: "357px", height: "283px", borderRadius: "8px" }}>
|
<Box sx={{ display: "flex", flexDirection: "column", mt: "20px", padding: "15px", backgroundColor: "#F2F3F7", width: "100%", maxWidth: "357px", height: "295px", borderRadius: "8px" }}>
|
||||||
<Box sx={{ display: "inline-flex", alignItems: "center", height: "31px", gap: "20px" }}>
|
<Box sx={{ display: "inline-flex", alignItems: "center", height: "31px", gap: "20px" }}>
|
||||||
<CustomizedSwitch/>
|
<CustomizedSwitch
|
||||||
|
checked={enabled}
|
||||||
|
onChange={(e) => {
|
||||||
|
const checked = e.target.checked;
|
||||||
|
setEnabled(checked);
|
||||||
|
updateQuiz(quiz!.id, (q) => {
|
||||||
|
const cfg: any = (q as any).config;
|
||||||
|
cfg.overTime = cfg.overTime || { enabled: false, endsAt: 0, description: "" };
|
||||||
|
cfg.overTime.enabled = checked;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
@ -32,9 +171,20 @@ const OverTime = () => {
|
|||||||
>
|
>
|
||||||
Введите описание
|
Введите описание
|
||||||
</Typography>
|
</Typography>
|
||||||
<CustomTextField sx={{ height: "48px", width: "100%", maxWidth: "327px", mt: "9px" }}
|
<CustomTextField
|
||||||
|
sx={{ height: "48px", width: "100%", maxWidth: "327px", mt: "9px", backgroundColor: "white" }}
|
||||||
placeholder="Квиз будет недоступен через:"
|
placeholder="Квиз будет недоступен через:"
|
||||||
maxLength={40}
|
maxLength={40}
|
||||||
|
value={description}
|
||||||
|
onChange={(e) => {
|
||||||
|
const next = e.target.value;
|
||||||
|
setDescription(next);
|
||||||
|
updateQuiz(quiz!.id, (q) => {
|
||||||
|
const cfg: any = (q as any).config;
|
||||||
|
cfg.overTime = cfg.overTime || { enabled: false, endsAt: 0, description: "" };
|
||||||
|
cfg.overTime.description = next;
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -51,8 +201,20 @@ const OverTime = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: "inline-flex", gap: "10px", pt: "8px" }}>
|
<Box sx={{ display: "inline-flex", gap: "10px", pt: "8px" }}>
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "3px" }}>
|
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "3px" }}>
|
||||||
<CustomTextField sx={{ height: "48px", width: "100%", maxWidth: "51px" }}
|
<CustomTextField
|
||||||
placeholder="00"
|
placeholder="00"
|
||||||
|
value={days}
|
||||||
|
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()}
|
||||||
|
onClick={() => daysRef.current?.select()}
|
||||||
|
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
|
||||||
|
sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
@ -66,8 +228,20 @@ const OverTime = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "3px" }}>
|
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "3px" }}>
|
||||||
<CustomTextField sx={{ height: "48px", width: "100%", maxWidth: "51px" }}
|
<CustomTextField
|
||||||
placeholder="00"
|
placeholder="00"
|
||||||
|
value={hours}
|
||||||
|
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()}
|
||||||
|
onClick={() => hoursRef.current?.select()}
|
||||||
|
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
|
||||||
|
sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
@ -81,8 +255,20 @@ const OverTime = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "3px" }}>
|
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "3px" }}>
|
||||||
<CustomTextField sx={{ height: "48px", width: "100%", maxWidth: "51px" }}
|
<CustomTextField
|
||||||
placeholder="00"
|
placeholder="00"
|
||||||
|
value={minutes}
|
||||||
|
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()}
|
||||||
|
onClick={() => minutesRef.current?.select()}
|
||||||
|
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
|
||||||
|
sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
@ -96,8 +282,20 @@ const OverTime = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "3px" }}>
|
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "3px" }}>
|
||||||
<CustomTextField sx={{ height: "48px", width: "100%", maxWidth: "51px" }}
|
<CustomTextField
|
||||||
placeholder="00"
|
placeholder="00"
|
||||||
|
value={seconds}
|
||||||
|
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()}
|
||||||
|
onClick={() => secondsRef.current?.select()}
|
||||||
|
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
|
||||||
|
sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
@ -111,6 +309,13 @@ const OverTime = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
{enabled && (
|
||||||
|
<Box sx={{ mt: "10px" }}>
|
||||||
|
<Typography sx={{ fontWeight: 500, color: theme.palette.grey3.main, fontSize: "16px" }}>
|
||||||
|
До конца: {rem.d} д {fmt(rem.h)}:{fmt(rem.m)}:{fmt(rem.s)}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -175,7 +175,7 @@ const Timer = () => {
|
|||||||
onKeyDown={handleMinutesKeyDown as any}
|
onKeyDown={handleMinutesKeyDown as any}
|
||||||
onPaste={handleMinutesPaste as any}
|
onPaste={handleMinutesPaste as any}
|
||||||
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
|
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
|
||||||
sx={{ height: "48px", width: "100%", maxWidth: "51px" }}
|
sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }}
|
||||||
inputRef={minutesRef}
|
inputRef={minutesRef}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
@ -206,7 +206,7 @@ const Timer = () => {
|
|||||||
onKeyDown={handleDigitKeyDown as any}
|
onKeyDown={handleDigitKeyDown as any}
|
||||||
onPaste={handleSecondsPaste as any}
|
onPaste={handleSecondsPaste as any}
|
||||||
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
|
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
|
||||||
sx={{ height: "48px", width: "100%", maxWidth: "51px" }}
|
sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }}
|
||||||
inputRef={secondsRef}
|
inputRef={secondsRef}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
|
|||||||
@ -21,7 +21,16 @@ export default function BackBlockedWithTooltip() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ClickAwayListener onClickAway={handleTooltipClose}>
|
<ClickAwayListener onClickAway={handleTooltipClose}>
|
||||||
<Box sx={{ position: "relative", display: "flex", gap: "20px", alignItems: "center", mt: "20px" }}>
|
<Box sx={{
|
||||||
|
position: "relative",
|
||||||
|
display: "flex",
|
||||||
|
gap: "20px",
|
||||||
|
alignItems: "center",
|
||||||
|
mt: "20px",
|
||||||
|
backgroundColor: "#f2f3f7",
|
||||||
|
borderRadius: "8px",
|
||||||
|
p: "18px 15px"
|
||||||
|
}}>
|
||||||
{enabled && (
|
{enabled && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
|||||||
@ -859,25 +859,14 @@ export default function StartPageSettings() {
|
|||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
/>
|
/>
|
||||||
<Extra />
|
<Extra />
|
||||||
<Box sx={{ display: "flex", gap: "20px", alignItems: "center" }}>
|
<Box sx={{
|
||||||
<CustomizedSwitch
|
display: "flex",
|
||||||
checked={quiz.config.antifraud}
|
gap: "20px",
|
||||||
onChange={(e) => {
|
alignItems: "center",
|
||||||
updateQuiz(quiz.id, (quiz) => {
|
backgroundColor: "#f2f3f7",
|
||||||
quiz.config.antifraud = e.target.checked;
|
borderRadius: "8px",
|
||||||
});
|
p: "18px 15px",
|
||||||
}}
|
}}>
|
||||||
/>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontWeight: 500,
|
|
||||||
color: theme.palette.grey3.main,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Включить антифрод
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ display: "flex", gap: "20px", alignItems: "center", mt: "20px" }}>
|
|
||||||
<CustomizedSwitch
|
<CustomizedSwitch
|
||||||
checked={quiz.config?.isUnSc}
|
checked={quiz.config?.isUnSc}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@ -896,8 +885,44 @@ export default function StartPageSettings() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<BackBlockedWithTooltip />
|
<BackBlockedWithTooltip />
|
||||||
<Timer/>
|
|
||||||
<OverTime/>
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "20px",
|
||||||
|
alignItems: "center",
|
||||||
|
backgroundColor: "#f2f3f7",
|
||||||
|
borderRadius: "8px",
|
||||||
|
p: "18px 15px",
|
||||||
|
mt: "20px",
|
||||||
|
}}>
|
||||||
|
<CustomizedSwitch
|
||||||
|
checked={quiz.config.antifraud}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.antifraud = e.target.checked;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontWeight: 500,
|
||||||
|
color: theme.palette.grey3.main,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Включить антифрод
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "inline-flex",
|
||||||
|
gap: "12px",
|
||||||
|
flexDirection: isTablet ? "column" : "row"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Timer />
|
||||||
|
<OverTime />
|
||||||
|
|
||||||
|
</Box>
|
||||||
{!isSmallMonitor && <SwitchAI />}
|
{!isSmallMonitor && <SwitchAI />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user