Merge branch 'staging'

This commit is contained in:
Nastya 2025-10-06 01:10:50 +03:00
commit 7367c35be1
26 changed files with 149 additions and 151 deletions

@ -11,7 +11,6 @@ const getLanguageFromUrlFallback = (): string => {
}; };
export const NameplateLogo: FC<SVGProps<SVGSVGElement>> = (props) => { export const NameplateLogo: FC<SVGProps<SVGSVGElement>> = (props) => {
console.log("props", props);
let lang = "ru"; // fallback let lang = "ru"; // fallback
try { try {

@ -51,12 +51,12 @@ function QuizAnswererInner({
const yandexMetrics = useYandexMetricsGoals(quizSettings?.settings.cfg.yandexMetricsNumber); const yandexMetrics = useYandexMetricsGoals(quizSettings?.settings.cfg.yandexMetricsNumber);
const r = useQuizStore(); const r = useQuizStore();
const { settings, questions } = useQuizStore(); const { settings, questions } = useQuizStore();
console.log("settings", settings);
console.log("questions", questions);
useEffect(() => { useEffect(() => {
addquizid(quizId); addquizid(quizId);
}, []); // Устанавливаем changeFaviconAndTitle в store
useQuizStore.setState({ changeFaviconAndTitle });
}, [changeFaviconAndTitle]);
useEffect(() => { useEffect(() => {
setTimeout(() => { setTimeout(() => {

@ -25,7 +25,7 @@ export const ApologyPage = ({ error }: Props) => {
color: "text.primary", color: "text.primary",
}} }}
> >
{/* {t(message.toLowerCase())} */} {t(message.toLowerCase())}
</Typography> </Typography>
</Box> </Box>
); );

@ -29,8 +29,9 @@ type InputsProps = {
}; };
}; };
const iscrutch = "/cc006b40-ccbd-4600-a1d3-f902f85aa0a0";
const pathOnly = window.location.pathname; const pathOnly = window.location.pathname;
const iscrutch =
pathOnly === "/cc006b40-ccbd-4600-a1d3-f902f85aa0a0" || pathOnly === "/28525cd7-9ddf-4c4a-a55b-e3d2f7d47583";
export const Inputs = ({ export const Inputs = ({
name, name,
@ -58,11 +59,7 @@ export const Inputs = ({
<CustomInput <CustomInput
onChange={({ target }) => setName(target.value)} onChange={({ target }) => setName(target.value)}
id={name} id={name}
title={ title={iscrutch ? "Введите имя и фамилию" : FC["name"].innerText || `${t("Enter")} ${t("Name").toLowerCase()}`}
pathOnly === iscrutch
? "Введите имя и фамилию"
: FC["name"].innerText || `${t("Enter")} ${t("Name").toLowerCase()}`
}
desc={FC["name"].text || t("Name")} desc={FC["name"].text || t("Name")}
Icon={NameIcon} Icon={NameIcon}
/> />

@ -76,7 +76,7 @@ const StandartLayout = ({ alignType, quizHeaderBlock, quizMainBlock, backgroundB
{quizHeaderBlock} {quizHeaderBlock}
{quizMainBlock} {quizMainBlock}
</Box> </Box>
{settings.cfg.startpage.background.desktop && ( {(settings.cfg.startpage.background.desktop || settings.cfg.startpage.background.type === "video") && (
<Box sx={{ width: "60%", overflow: "hidden" }}> <Box sx={{ width: "60%", overflow: "hidden" }}>
<Box <Box
sx={{ sx={{
@ -195,7 +195,8 @@ const CenteredLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock }: Lay
}} }}
> >
{quizHeaderBlock} {quizHeaderBlock}
{backgroundBlock && settings.cfg.startpage.background.desktop && ( {backgroundBlock &&
(settings.cfg.startpage.background.desktop || settings.cfg.startpage.background.type === "video") && (
<Box <Box
sx={{ sx={{
width: "100%", width: "100%",

@ -60,7 +60,7 @@ const StandartMobileLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock
}} }}
> >
<Box sx={{ marginBottom: "13px" }}>{quizHeaderBlock}</Box> <Box sx={{ marginBottom: "13px" }}>{quizHeaderBlock}</Box>
{settings.cfg.startpage.background.desktop && ( {(settings.cfg.startpage.background.desktop || settings.cfg.startpage.background.type === "video") && (
<Box sx={{ width: "100%", overflow: "hidden" }}> <Box sx={{ width: "100%", overflow: "hidden" }}>
<Box <Box
sx={{ sx={{
@ -200,7 +200,7 @@ const CenteredMobileLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock
}} }}
> >
{quizHeaderBlock} {quizHeaderBlock}
{settings.cfg.startpage.background.desktop && ( {(settings.cfg.startpage.background.desktop || settings.cfg.startpage.background.type === "video") && (
<Box <Box
sx={{ sx={{
width: "100%", width: "100%",

@ -39,6 +39,15 @@ export const StartPageViewPublication = () => {
yandexMetrics.phoneNumberOpened(); 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 = const background =
settings.cfg.startpage.background.type === "image" ? ( settings.cfg.startpage.background.type === "image" ? (
<img <img
@ -56,6 +65,9 @@ export const StartPageViewPublication = () => {
/> />
) : settings.cfg.startpage.background.type === "video" ? ( ) : settings.cfg.startpage.background.type === "video" ? (
settings.cfg.startpage.background.video ? ( settings.cfg.startpage.background.video ? (
(() => {
console.log("Rendering QuizVideo with URL:", settings.cfg.startpage.background.video);
return (
<QuizVideo <QuizVideo
videoUrl={settings.cfg.startpage.background.video} videoUrl={settings.cfg.startpage.background.video}
containerSX={{ containerSX={{
@ -75,6 +87,8 @@ export const StartPageViewPublication = () => {
}, },
}} }}
/> />
);
})()
) : null ) : null
) : null; ) : null;

@ -57,12 +57,20 @@ export default function ViewPublicationPage() {
useEffect( useEffect(
function setFaviconAndTitle() { function setFaviconAndTitle() {
if (!changeFaviconAndTitle) return; if (!changeFaviconAndTitle) {
return;
}
const link = document.querySelector('link[rel="icon"]'); const link = document.querySelector('link[rel="icon"]');
// Для localhost:5173 устанавливаем фавиконку из настроек квиза
if (window.location.hostname === "localhost" && window.location.port === "5173") {
if (link && settings.cfg.startpage.favIcon) { if (link && settings.cfg.startpage.favIcon) {
link.setAttribute("href", settings.cfg.startpage.favIcon); link.setAttribute("href", settings.cfg.startpage.favIcon);
} }
} else if (link && settings.cfg.startpage.favIcon) {
link.setAttribute("href", settings.cfg.startpage.favIcon);
}
document.title = settings.name; document.title = settings.name;
}, },
@ -74,7 +82,6 @@ export default function ViewPublicationPage() {
// Обработка noStartPage - обновляем состояние в store // Обработка noStartPage - обновляем состояние в store
useEffect(() => { useEffect(() => {
if (currentQuizStep === "startpage" && settings.cfg.noStartPage) { if (currentQuizStep === "startpage" && settings.cfg.noStartPage) {
console.log("🔄 noStartPage: Setting currentQuizStep to 'question'");
setCurrentQuizStep("question"); setCurrentQuizStep("question");
} }
}, [currentQuizStep, settings.cfg.noStartPage, setCurrentQuizStep]); }, [currentQuizStep, settings.cfg.noStartPage, setCurrentQuizStep]);

@ -45,7 +45,8 @@ const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputP
const ownVariants = useQuizViewStore((state) => state.ownVariants); const ownVariants = useQuizViewStore((state) => state.ownVariants);
const { updateOwnVariant } = useQuizViewStore((state) => state); const { updateOwnVariant } = useQuizViewStore((state) => state);
const ownAnswer = ownVariants[ownVariants.findIndex((v: OwnVariant) => v.id === variant.id)]?.variant.answer || ""; const ownVariantIndex = ownVariants.findIndex((v: OwnVariant) => v.id === variant.id);
const ownAnswer = ownVariantIndex >= 0 ? ownVariants[ownVariantIndex]?.variant.answer || "" : "";
return largeCheck ? ( return largeCheck ? (
<Box sx={{ overflow: "auto" }}> <Box sx={{ overflow: "auto" }}>

@ -28,18 +28,12 @@ interface OwnInputProps {
} }
const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputProps) => { const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputProps) => {
const theme = useTheme(); const theme = useTheme();
console.log("здравствуй, я овн вопрос. Вот инфо обо мне: ");
console.log("questionId ", questionId);
console.log("variant", variant);
console.log("largeCheck", largeCheck);
console.log("ownPlaceholder", ownPlaceholder);
console.log("--------------------------");
const ownVariants = useQuizViewStore((state) => state.ownVariants); const ownVariants = useQuizViewStore((state) => state.ownVariants);
const { updateOwnVariant } = useQuizViewStore((state) => state); const { updateOwnVariant } = useQuizViewStore((state) => state);
const ownAnswer = ownVariants[ownVariants.findIndex((v) => v.id === variant.id)]?.variant.answer || ""; const ownVariantIndex = ownVariants.findIndex((v) => v.id === variant.id);
console.log("Вот каков стор в данный момент времени:", ownVariants); const ownAnswer = ownVariantIndex >= 0 ? ownVariants[ownVariantIndex]?.variant.answer || "" : "";
return largeCheck ? ( return largeCheck ? (
<TextareaAutosize <TextareaAutosize
@ -66,7 +60,6 @@ const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputP
value={ownAnswer} value={ownAnswer}
onClick={(e: React.MouseEvent<HTMLTextAreaElement>) => e.stopPropagation()} onClick={(e: React.MouseEvent<HTMLTextAreaElement>) => e.stopPropagation()}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => { onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
console.log("клик", e.target.value);
updateOwnVariant(variant.id, e.target.value); updateOwnVariant(variant.id, e.target.value);
}} }}
/> />

@ -31,7 +31,8 @@ const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputP
const ownVariants = useQuizViewStore((state) => state.ownVariants); const ownVariants = useQuizViewStore((state) => state.ownVariants);
const { updateOwnVariant } = useQuizViewStore((state) => state); const { updateOwnVariant } = useQuizViewStore((state) => state);
const ownAnswer = ownVariants[ownVariants.findIndex((v) => v.id === variant.id)]?.variant.answer || ""; const ownVariantIndex = ownVariants.findIndex((v) => v.id === variant.id);
const ownAnswer = ownVariantIndex >= 0 ? ownVariants[ownVariantIndex]?.variant.answer || "" : "";
return largeCheck ? ( return largeCheck ? (
<TextareaAutosize <TextareaAutosize

@ -56,7 +56,11 @@ export const Select = ({
<MuiSelect <MuiSelect
displayEmpty displayEmpty
renderValue={(value) => renderValue={(value) =>
value ? items[Number(value)] : <Typography sx={{ color: colorPlaceholder }}>{placeholder}</Typography> value ? (
<Typography sx={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>{items[Number(value)]}</Typography>
) : (
<Typography sx={{ color: colorPlaceholder }}>{placeholder}</Typography>
)
} }
id="display-select" id="display-select"
variant="outlined" variant="outlined"
@ -123,7 +127,7 @@ export const Select = ({
padding: "10px", padding: "10px",
borderRadius: "5px", borderRadius: "5px",
color: colorPlaceholder, color: colorPlaceholder,
whiteSpace: "normal", whiteSpace: "pre-wrap",
wordBreak: "break-word", wordBreak: "break-word",
}} }}
> >

@ -99,8 +99,6 @@ export const createQuizViewStore = () =>
updateOwnVariant(id, answer, extendedText, originalImageUrl, localImageUrl) { updateOwnVariant(id, answer, extendedText, originalImageUrl, localImageUrl) {
set( set(
(state) => { (state) => {
console.log("!!! STORE !!! ___ Случился вызов варианта свой ответ. Вот что я получил:");
console.log(id, answer, extendedText, originalImageUrl, localImageUrl);
const index = state.ownVariants.findIndex((variant) => variant.id === id); const index = state.ownVariants.findIndex((variant) => variant.id === id);
if (index < 0) { if (index < 0) {
@ -108,7 +106,7 @@ export const createQuizViewStore = () =>
id, id,
variant: { variant: {
id: id, id: id,
answer: answer === null ? state.ownVariants[index].variant.answer : answer, answer: answer || "",
extendedText: extendedText || "", extendedText: extendedText || "",
hints: "", hints: "",
originalImageUrl: originalImageUrl || "", originalImageUrl: originalImageUrl || "",

@ -11,10 +11,20 @@ type VideoIframeProps = {
}; };
export default function QuizVideo({ videoUrl, containerSX }: VideoIframeProps) { export default function QuizVideo({ videoUrl, containerSX }: VideoIframeProps) {
console.log("=== QuizVideo component called ===");
console.log("QuizVideo component called with URL:", videoUrl);
console.log("ContainerSX:", containerSX);
const { data: videoData, error, isLoading } = useSWR(["video", videoUrl], (params) => getVideo(params[1])); const { data: videoData, error, isLoading } = useSWR(["video", videoUrl], (params) => getVideo(params[1]));
console.log("Video data:", videoData);
console.log("Error:", error);
console.log("Is loading:", isLoading);
console.log("=== End QuizVideo component ===");
return ( return (
<Box <Box
id="test-video-container"
sx={{ sx={{
width: "100%", width: "100%",
height: "100%", height: "100%",
@ -28,7 +38,19 @@ export default function QuizVideo({ videoUrl, containerSX }: VideoIframeProps) {
) : !videoData || error ? ( ) : !videoData || error ? (
<ApologyPage error={error ?? new Error()} /> <ApologyPage error={error ?? new Error()} />
) : videoData.sourceName === "custom" || videoData.sourceName === "yandex" ? ( ) : videoData.sourceName === "custom" || videoData.sourceName === "yandex" ? (
<Box component="video" sx={{ width: "100%", height: "100%" }} autoPlay controls muted src={videoData.url} /> <Box
component="video"
sx={{ width: "100%", height: "100%" }}
autoPlay
controls
muted
loop
playsInline
onError={(e) => console.error("Video error:", e)}
onLoadStart={() => console.log("Video load started")}
onCanPlay={() => console.log("Video can play")}
src={videoData.url}
/>
) : ( ) : (
<Box <Box
component="iframe" component="iframe"

@ -1,6 +1,6 @@
import axios from "axios"; import axios from "axios";
type SourceType = "youtube" | "short" | "vk" | "google" | "yandex" | "mail" | "tiktok" | "custom"; type SourceType = "youtube" | "short" | "vk" | "google" | "yandex" | "mail" | "tiktok" | "s3" | "custom";
export type VideoUrlResult = { export type VideoUrlResult = {
sourceName: SourceType; sourceName: SourceType;
@ -68,6 +68,7 @@ const FILTER_SITES_REGEX: Record<Exclude<SourceType, "short" | "custom">, RegExp
google: /^(https?:\/\/)?(www.)?drive\.google\..+$/, google: /^(https?:\/\/)?(www.)?drive\.google\..+$/,
yandex: /^(https?:\/\/)?disk\.yandex\..+$/, yandex: /^(https?:\/\/)?disk\.yandex\..+$/,
mail: /^(https?:\/\/)?cloud\.mail\..+$/, mail: /^(https?:\/\/)?cloud\.mail\..+$/,
s3: /^(https?:\/\/)?.*\.s3\..+$/,
}; };
const EXTRACT_VIDEO_ID_REGEX: Record<Exclude<SourceType, "custom">, RegExp> = { const EXTRACT_VIDEO_ID_REGEX: Record<Exclude<SourceType, "custom">, RegExp> = {
@ -78,9 +79,12 @@ const EXTRACT_VIDEO_ID_REGEX: Record<Exclude<SourceType, "custom">, RegExp> = {
yandex: /(?<=i\/)[\w-]+/, yandex: /(?<=i\/)[\w-]+/,
mail: /$/, mail: /$/,
tiktok: /(?<=video\/|\.com\/)[\w-]+/, tiktok: /(?<=video\/|\.com\/)[\w-]+/,
s3: /$/,
}; };
export const getVideo = async (videoUrl: string): Promise<VideoUrlResult> => { export const getVideo = async (videoUrl: string): Promise<VideoUrlResult> => {
console.log("getVideo called with URL:", videoUrl);
if (videoUrl.match(FILTER_SITES_REGEX.youtube)?.[0]) { if (videoUrl.match(FILTER_SITES_REGEX.youtube)?.[0]) {
if (videoUrl.includes("youtube.com/shorts")) { if (videoUrl.includes("youtube.com/shorts")) {
const videoId = videoUrl.match(EXTRACT_VIDEO_ID_REGEX.short)?.[0]; const videoId = videoUrl.match(EXTRACT_VIDEO_ID_REGEX.short)?.[0];
@ -161,5 +165,11 @@ export const getVideo = async (videoUrl: string): Promise<VideoUrlResult> => {
return { sourceName: "mail", url: videoUrl }; return { sourceName: "mail", url: videoUrl };
} }
if (videoUrl.match(FILTER_SITES_REGEX.s3)) {
console.log("S3 URL detected, returning as custom video");
return { sourceName: "custom", url: videoUrl };
}
console.log("No matching source found, returning custom:", videoUrl);
return { sourceName: "custom", url: videoUrl }; return { sourceName: "custom", url: videoUrl };
}; };

@ -15,14 +15,6 @@ export function useAIQuiz() {
//Получаем инфо о квизе и список вопросов. //Получаем инфо о квизе и список вопросов.
const { settings, questions, quizId, cnt, quizStep, preview } = useQuizStore(); const { settings, questions, quizId, cnt, quizStep, preview } = useQuizStore();
// Отладочная информация о настройках таймера
console.log("🤖 AI Quiz settings:", {
questionTimerEnabled: settings.questionTimerEnabled,
time_of_passing: settings.cfg.time_of_passing,
timerEnabled: Boolean(settings.questionTimerEnabled),
timerSeconds: settings.cfg.time_of_passing ?? 0,
});
//Список ответов на вопрос. Мы записываем ответы локально, параллельно отправляя на бек информацию о ответах //Список ответов на вопрос. Мы записываем ответы локально, параллельно отправляя на бек информацию о ответах
const answers = useQuizViewStore((state) => state.answers); const answers = useQuizViewStore((state) => state.answers);
@ -113,7 +105,6 @@ export function useAIQuiz() {
preview, preview,
currentQuestion, currentQuestion,
onNext: () => { onNext: () => {
console.log("🤖 AI Quiz: Timer triggered moveToNextQuestion");
moveToNextQuestion(); moveToNextQuestion();
}, },
}); });

@ -12,18 +12,9 @@ import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoa
import { useQuestionTimer } from "./useQuestionTimer"; import { useQuestionTimer } from "./useQuestionTimer";
export function useBranchingQuiz() { export function useBranchingQuiz() {
console.log("РАБОТАЮ Я, УПРАВЛЯТОР КВИЗА ВЕТВЛЕНИЯ");
//Получаем инфо о квизе и список вопросов. //Получаем инфо о квизе и список вопросов.
const { settings, questions, quizId, cnt, preview } = useQuizStore(); const { settings, questions, quizId, cnt, preview } = useQuizStore();
// Отладочная информация о настройках таймера
console.log("🌳 Branching Quiz settings:", {
questionTimerEnabled: settings.questionTimerEnabled,
time_of_passing: settings.cfg.time_of_passing,
timerEnabled: Boolean(settings.questionTimerEnabled),
timerSeconds: settings.cfg.time_of_passing ?? 0,
});
//Когда квиз линейный, не ветвящийся, мы идём по вопросам по их порядковому номеру. Это их page. //Когда квиз линейный, не ветвящийся, мы идём по вопросам по их порядковому номеру. Это их page.
//За корректность page отвечает конструктор квизов. Интересный факт, если в конструкторе удалить из середины вопрос, то случится куча запросов изменения вопросов с изменением этого page //За корректность page отвечает конструктор квизов. Интересный факт, если в конструкторе удалить из середины вопрос, то случится куча запросов изменения вопросов с изменением этого page
const sortedQuestions = useMemo(() => { const sortedQuestions = useMemo(() => {
@ -58,16 +49,11 @@ export function useBranchingQuiz() {
if (settings.cfg.haveRoot) { if (settings.cfg.haveRoot) {
// Если есть ветвление, то settings.cfg.haveRoot будет заполнен // Если есть ветвление, то settings.cfg.haveRoot будет заполнен
//Если заполнен, то дерево растёт с root и это 1 вопрос :) //Если заполнен, то дерево растёт с root и это 1 вопрос :)
console.log("Существует запись о корне: " + settings.cfg.haveRoot);
const nextQuestion = sortedQuestions.find( const nextQuestion = sortedQuestions.find(
//Функция ищет первое совпадение по массиву //Функция ищет первое совпадение по массиву
(question) => question.id === settings.cfg.haveRoot || question.content.id === settings.cfg.haveRoot (question) => question.id === settings.cfg.haveRoot || question.content.id === settings.cfg.haveRoot
); );
console.log("___nextQuestion____");
console.log(nextQuestion);
console.log("___sortedQuestions____");
console.log(sortedQuestions);
if (!nextQuestion) return null; if (!nextQuestion) return null;
return nextQuestion.id; return nextQuestion.id;
} }
@ -189,7 +175,6 @@ export function useBranchingQuiz() {
preview, preview,
currentQuestion, currentQuestion,
onNext: () => { onNext: () => {
console.log("🌳 Branching Quiz: Timer triggered moveToNextQuestion");
moveToNextQuestion(); moveToNextQuestion();
}, },
}); });

@ -15,14 +15,6 @@ export function useLinearQuiz() {
//Получаем инфо о квизе и список вопросов. //Получаем инфо о квизе и список вопросов.
const { settings, questions, quizId, cnt, preview } = useQuizStore(); const { settings, questions, quizId, cnt, preview } = useQuizStore();
// Отладочная информация о настройках таймера
console.log("📏 Linear Quiz settings:", {
questionTimerEnabled: settings.questionTimerEnabled,
time_of_passing: settings.cfg.time_of_passing,
timerEnabled: Boolean(settings.questionTimerEnabled),
timerSeconds: settings.cfg.time_of_passing ?? 0,
});
//Когда квиз линейный, не ветвящийся, мы идём по вопросам по их порядковому номеру. Это их page. //Когда квиз линейный, не ветвящийся, мы идём по вопросам по их порядковому номеру. Это их page.
//За корректность page отвечает конструктор квизов. Интересный факт, если в конструкторе удалить из середины вопрос, то случится куча запросов изменения вопросов с изменением этого page //За корректность page отвечает конструктор квизов. Интересный факт, если в конструкторе удалить из середины вопрос, то случится куча запросов изменения вопросов с изменением этого page
const sortedQuestions = useMemo(() => { const sortedQuestions = useMemo(() => {
@ -183,7 +175,6 @@ export function useLinearQuiz() {
preview, preview,
currentQuestion, currentQuestion,
onNext: () => { onNext: () => {
console.log("📏 Linear Quiz: Timer triggered moveToNextQuestion");
// Программный переход к следующему вопросу // Программный переход к следующему вопросу
moveToNextQuestion(); moveToNextQuestion();
}, },

@ -20,44 +20,24 @@ export function useQuestionTimer({ enabled, seconds, quizId, preview, currentQue
const isFirstQuestionRef = useRef<boolean>(true); const isFirstQuestionRef = useRef<boolean>(true);
useEffect(() => { useEffect(() => {
console.log("🕐 useQuestionTimer useEffect triggered", {
enabled,
seconds,
quizId,
preview,
currentQuestionId: currentQuestion?.id,
currentQuestionType: currentQuestion?.type,
currentQuizStep,
hasCurrentQuestion: !!currentQuestion,
timestamp: new Date().toISOString(),
});
if (!enabled) { if (!enabled) {
console.log("❌ Timer disabled");
return; return;
} }
if (!seconds || seconds <= 0) { if (!seconds || seconds <= 0) {
console.log("❌ Invalid seconds:", seconds);
return; return;
} }
if (!currentQuestion) { if (!currentQuestion) {
console.log("❌ No current question");
return; return;
} }
if (currentQuizStep !== "question") { if (currentQuizStep !== "question") {
console.log("❌ Not on question step:", currentQuizStep);
return; return;
} }
if (currentQuestion.type === "result") { if (currentQuestion.type === "result") {
console.log("❌ Question is result type");
return; return;
} }
console.log("✅ Starting timer for", seconds, "seconds");
// Сбрасываем предыдущий таймер // Сбрасываем предыдущий таймер
if (timeoutRef.current) { if (timeoutRef.current) {
console.log("🔄 Clearing previous timer");
clearTimeout(timeoutRef.current); clearTimeout(timeoutRef.current);
timeoutRef.current = null; timeoutRef.current = null;
} }
@ -67,27 +47,21 @@ export function useQuestionTimer({ enabled, seconds, quizId, preview, currentQue
const startDelay = isFirstQuestion ? 2000 : 100; // 2 секунды для первого вопроса, 100ms для остальных const startDelay = isFirstQuestion ? 2000 : 100; // 2 секунды для первого вопроса, 100ms для остальных
if (isFirstQuestion) { if (isFirstQuestion) {
console.log("🔄 First question detected, adding 2s delay to prevent navigation conflicts");
isFirstQuestionRef.current = false; isFirstQuestionRef.current = false;
} }
timeoutRef.current = window.setTimeout( timeoutRef.current = window.setTimeout(
async () => { async () => {
console.log("⏰ Timer expired! Auto-advancing to next question");
try { try {
if (!preview) { if (!preview) {
console.log("📤 Sending empty answer for question:", currentQuestion.id);
// Отправляем пустую строку в ответе (questionAnswer === undefined) // Отправляем пустую строку в ответе (questionAnswer === undefined)
await sendQuestionAnswer(quizId, currentQuestion, undefined, ownVariants); await sendQuestionAnswer(quizId, currentQuestion, undefined, ownVariants);
console.log("✅ Empty answer sent successfully");
} else { } else {
console.log("👀 Preview mode - skipping answer send");
} }
} catch (e) { } catch (e) {
console.error("❌ Error sending empty timed answer", e); console.error("❌ Error sending empty timed answer", e);
enqueueSnackbar("Ошибка при отправке ответа по таймеру"); enqueueSnackbar("Ошибка при отправке ответа по таймеру");
} finally { } finally {
console.log("➡️ Calling onNext()");
onNext(); onNext();
} }
}, },
@ -95,7 +69,6 @@ export function useQuestionTimer({ enabled, seconds, quizId, preview, currentQue
); );
return () => { return () => {
console.log("🧹 Cleaning up timer");
if (timeoutRef.current) { if (timeoutRef.current) {
clearTimeout(timeoutRef.current); clearTimeout(timeoutRef.current);
timeoutRef.current = null; timeoutRef.current = null;

@ -29,7 +29,6 @@ function analyicStatus({ status, haveRoot }: StatusData) {
} }
export const initDataManager = (data: StatusData) => { export const initDataManager = (data: StatusData) => {
console.log("🔧 Initializing DataManager with:", data);
analyicStatus(data); analyicStatus(data);
switch (statusOfQuiz) { switch (statusOfQuiz) {
case "line": case "line":
@ -43,7 +42,6 @@ export const initDataManager = (data: StatusData) => {
break; break;
} }
isInitialized = true; isInitialized = true;
console.log("✅ DataManager initialized with type:", statusOfQuiz);
}; };
// Главный хук (интерфейс для потребителей) // Главный хук (интерфейс для потребителей)
@ -52,7 +50,6 @@ export const useQuestionFlowControl = () => {
// Попытка автоматической инициализации на основе текущих настроек // Попытка автоматической инициализации на основе текущих настроек
const { settings } = useQuizStore.getState(); const { settings } = useQuizStore.getState();
if (settings && settings.status) { if (settings && settings.status) {
console.log("🔄 Auto-initializing DataManager with settings:", settings);
initDataManager({ initDataManager({
status: settings.status, status: settings.status,
haveRoot: settings.cfg.haveRoot, haveRoot: settings.cfg.haveRoot,
@ -66,7 +63,6 @@ export const useQuestionFlowControl = () => {
// Функция для сброса состояния (полезна для HMR) // Функция для сброса состояния (полезна для HMR)
export const resetDataManager = () => { export const resetDataManager = () => {
console.log("🔄 Resetting DataManager");
cachedManager = null as any; cachedManager = null as any;
isInitialized = false; isInitialized = false;
statusOfQuiz = null as any; statusOfQuiz = null as any;

@ -11,7 +11,6 @@ export async function sendQuestionAnswer(
ownVariants: OwnVariant[] ownVariants: OwnVariant[]
) { ) {
if (!questionAnswer) { if (!questionAnswer) {
console.log("📤 sendQuestionAnswer: Sending empty answer for question", question.id);
return sendAnswer({ return sendAnswer({
questionId: question.id, questionId: question.id,
body: "", body: "",
@ -101,9 +100,7 @@ export async function sendQuestionAnswer(
return; return;
} }
case "images": { case "images": {
console.log("Работает отправщик ответа для типа КАРТИНКИ");
if (question.content.multi) { if (question.content.multi) {
console.log("Этот вопрос есть МУЛЬТИ");
const answer = questionAnswer.answer; const answer = questionAnswer.answer;
const ownAnswer = Array.isArray(answer) const ownAnswer = Array.isArray(answer)
? ownVariants[ownVariants.findIndex((variant) => answer.some((a: string) => a === variant.id))]?.variant ? ownVariants[ownVariants.findIndex((variant) => answer.some((a: string) => a === variant.id))]?.variant
@ -151,7 +148,6 @@ export async function sendQuestionAnswer(
qid: quizId, qid: quizId,
}); });
} }
console.log("Этот вопрос НЕ есть МУЛЬТИ");
const variant = question.content.variants.find((v) => v.id === questionAnswer.answer); const variant = question.content.variants.find((v) => v.id === questionAnswer.answer);
@ -160,9 +156,6 @@ export async function sendQuestionAnswer(
// Берем fileId из ownVariants для own вариантов // Берем fileId из ownVariants для own вариантов
const ownVariantData = ownVariants.find((v) => v.id === variant.id)?.variant; const ownVariantData = ownVariants.find((v) => v.id === variant.id)?.variant;
console.log("Был выбран вариант ", variant);
console.log("Был выбран ownVariant ", ownVariantData);
let imageValue = variant.extendedText; let imageValue = variant.extendedText;
if (variant.isOwn) { if (variant.isOwn) {
if (ownVariantData?.originalImageUrl) { if (ownVariantData?.originalImageUrl) {
@ -175,11 +168,6 @@ export async function sendQuestionAnswer(
} }
} }
console.log("В конечном итоге я планирую отправить вот эти данные: ", {
Image: imageValue,
Description: ownVariantData ? ownVariantData.answer : variant.answer,
});
const body = { const body = {
Image: imageValue, Image: imageValue,
Description: ownVariantData ? ownVariantData.answer : variant.answer, Description: ownVariantData ? ownVariantData.answer : variant.answer,

@ -20,7 +20,16 @@ const theme = createTheme({
}, },
components: { components: {
MuiCssBaseline: { MuiCssBaseline: {
styleOverrides: fontFaces, styleOverrides: {
// Если fontFaces это CSSObject
...(typeof fontFaces === "string" ? {} : fontFaces),
// Добавляем стиль для Typography
".MuiTypography-root": {
whiteSpace: "pre-wrap !important",
},
// Если fontFaces это строка, добавляем её как @font-face
...(typeof fontFaces === "string" ? { "@font-face": fontFaces } : {}),
},
}, },
MuiScopedCssBaseline: { MuiScopedCssBaseline: {
styleOverrides: { styleOverrides: {
@ -28,6 +37,11 @@ const theme = createTheme({
}, },
}, },
MuiTypography: { MuiTypography: {
styleOverrides: {
root: {
whiteSpace: "pre-wrap",
},
},
defaultProps: { defaultProps: {
variantMapping: { variantMapping: {
p1: "p", p1: "p",

@ -26,7 +26,7 @@
"preview": "vite preview", "preview": "vite preview",
"cypress:open": "cypress open", "cypress:open": "cypress open",
"prepublishOnly": "npm run build:package", "prepublishOnly": "npm run build:package",
"deploy": "echo '🚀 Начало процесса деплоя...' && docker login gitea.pena && if [ $? -eq 0 ]; then echo '✅ Успешный логин в Docker registry'; else echo '❌ Ошибка логина в Docker registry'; exit 1; fi && echo '🏗️ Сборка Docker образа...' && docker build -t gitea.pena/squiz/frontanswerer/$(git branch --show-current):latest . && if [ $? -eq 0 ]; then echo '✅ Docker образ успешно собран'; else echo '❌ Ошибка сборки Docker образа'; exit 1; fi && echo '📤 Пуш образа в registry...' && docker push gitea.pena/squiz/frontanswerer/$(git branch --show-current):latest && if [ $? -eq 0 ]; then echo '✅ Образ успешно загружен в registry'; echo '🎉 Деплой завершен успешно!'; else echo '❌ Ошибка загрузки образа в registry'; exit 1; fi", "deploy": "docker login gitea.pena && docker build -t gitea.pena/squiz/frontanswerer/$(git branch --show-current):latest . && docker push gitea.pena/squiz/frontanswerer/$(git branch --show-current):latest",
"prepare": "husky", "prepare": "husky",
"test": "cypress open" "test": "cypress open"
}, },

@ -17,7 +17,10 @@ export default function App() {
height: "100dvh", height: "100dvh",
}} }}
> >
<QuizAnswerer quizId={quizId || currentUrl || ""} /> <QuizAnswerer
quizId={quizId || currentUrl || ""}
changeFaviconAndTitle={true}
/>
</Box> </Box>
); );
} }

@ -10,7 +10,7 @@ const widget = {
create({ create({
selector, selector,
quizId, quizId,
changeFaviconAndTitle = true, changeFaviconAndTitle = false,
}: { }: {
selector: string; selector: string;
quizId: string; quizId: string;

@ -4,7 +4,13 @@ import { Dialog, IconButton, Slide, SlideProps, SxProps, Theme } from "@mui/mate
import { forwardRef } from "react"; import { forwardRef } from "react";
const SlideTransition = forwardRef<unknown, SlideProps>((props, ref) => { const SlideTransition = forwardRef<unknown, SlideProps>((props, ref) => {
return <Slide direction="up" ref={ref} {...props} />; return (
<Slide
direction="up"
ref={ref}
{...props}
/>
);
}); });
interface Props { interface Props {
@ -46,7 +52,11 @@ export default function QuizDialog({
], ],
}} }}
> >
<QuizAnswerer quizId={quizId} changeFaviconAndTitle={false} disableGlobalCss /> <QuizAnswerer
quizId={quizId}
changeFaviconAndTitle={false}
disableGlobalCss
/>
<IconButton <IconButton
onClick={onClose} onClick={onClose}
sx={{ sx={{