Merge branch 'main' into staging
This commit is contained in:
commit
3a8865713a
@ -2,7 +2,7 @@ import useSWR from "swr";
|
||||
import { getQuizData } from "./quizRelase";
|
||||
|
||||
export function useQuizData(quizId: string, preview: boolean = false) {
|
||||
return useSWR(preview ? null : ["quizData", quizId], (params) => getQuizData(params[1]), {
|
||||
return useSWR(preview ? null : ["quizData", quizId], (params) => getQuizData({ quizId: params[1] }), {
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
shouldRetryOnError: false,
|
||||
|
@ -72,60 +72,129 @@ export const publicationMakeRequest = ({ url, body }: PublicationMakeRequestPara
|
||||
});
|
||||
};
|
||||
|
||||
export async function getData(quizId: string): Promise<{
|
||||
// Глобальные переменные для хранения состояния между вызовами
|
||||
let globalStatus: string | null = null;
|
||||
let isFirstRequest = true;
|
||||
|
||||
export async function getData({ quizId, page }: { quizId: string; page?: number }): Promise<{
|
||||
data: GetQuizDataResponse | null;
|
||||
isRecentlyCompleted: boolean;
|
||||
error?: AxiosError;
|
||||
}> {
|
||||
try {
|
||||
const { data, headers } = await axios<GetQuizDataResponse>(
|
||||
domain + `/answer/v1.0.0/settings${window.location.search}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-Sessionkey": SESSIONS,
|
||||
"Content-Type": "application/json",
|
||||
DeviceType: DeviceType,
|
||||
Device: Device,
|
||||
OS: OSDevice,
|
||||
Browser: userAgent,
|
||||
},
|
||||
data: {
|
||||
quiz_id: quizId,
|
||||
limit: 100,
|
||||
page: 0,
|
||||
need_config: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
const sessions = JSON.parse(localStorage.getItem("sessions") || "{}");
|
||||
// Первый запрос: 1 вопрос + конфиг
|
||||
if (isFirstRequest) {
|
||||
const { data, headers } = await axios<GetQuizDataResponse>(
|
||||
domain + `/answer/v1.0.0/settings${window.location.search}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-Sessionkey": SESSIONS,
|
||||
"Content-Type": "application/json",
|
||||
DeviceType: DeviceType,
|
||||
Device: Device,
|
||||
OS: OSDevice,
|
||||
Browser: userAgent,
|
||||
},
|
||||
data: {
|
||||
quiz_id: quizId,
|
||||
limit: 1,
|
||||
page: 0,
|
||||
need_config: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
//Тут ещё проверка на антифрод без парса конфига. Нам не интересно время если не нужно запрещать проходить чаще чем в сутки
|
||||
if (typeof sessions[quizId] === "number" && data.settings.cfg.includes('antifraud":true')) {
|
||||
// unix время. Если меньше суток прошло - выводить ошибку, иначе пустить дальше
|
||||
if (Date.now() - sessions[quizId] < 86400000) {
|
||||
return { data, isRecentlyCompleted: true };
|
||||
globalStatus = data.settings.status;
|
||||
isFirstRequest = false;
|
||||
SESSIONS = headers["x-sessionkey"] || SESSIONS;
|
||||
|
||||
// Проверка антифрода
|
||||
const sessions = JSON.parse(localStorage.getItem("sessions") || "{}");
|
||||
if (typeof sessions[quizId] === "number" && data.settings.cfg.includes('antifraud":true')) {
|
||||
if (Date.now() - sessions[quizId] < 86400000) {
|
||||
return { data, isRecentlyCompleted: true };
|
||||
}
|
||||
}
|
||||
|
||||
// Если статус не AI - сразу делаем запрос за всеми вопросами
|
||||
if (globalStatus !== "ai") {
|
||||
const secondResponse = await axios<GetQuizDataResponse>(
|
||||
domain + `/answer/v1.0.0/settings${window.location.search}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-Sessionkey": SESSIONS,
|
||||
"Content-Type": "application/json",
|
||||
DeviceType: DeviceType,
|
||||
Device: Device,
|
||||
OS: OSDevice,
|
||||
Browser: userAgent,
|
||||
},
|
||||
data: {
|
||||
quiz_id: quizId,
|
||||
limit: 100,
|
||||
page: 0,
|
||||
need_config: false,
|
||||
},
|
||||
}
|
||||
);
|
||||
return {
|
||||
data: { ...data, items: secondResponse.data.items },
|
||||
isRecentlyCompleted: false,
|
||||
};
|
||||
}
|
||||
|
||||
return { data, isRecentlyCompleted: false };
|
||||
}
|
||||
|
||||
SESSIONS = headers["x-sessionkey"] ? headers["x-sessionkey"] : SESSIONS;
|
||||
// Последующие запросы
|
||||
const response = await axios<GetQuizDataResponse>(domain + `/answer/v1.0.0/settings${window.location.search}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-Sessionkey": SESSIONS,
|
||||
"Content-Type": "application/json",
|
||||
DeviceType: DeviceType,
|
||||
Device: Device,
|
||||
OS: OSDevice,
|
||||
Browser: userAgent,
|
||||
},
|
||||
data: {
|
||||
quiz_id: quizId,
|
||||
limit: 1,
|
||||
page: page,
|
||||
need_config: false,
|
||||
},
|
||||
});
|
||||
|
||||
return { data, isRecentlyCompleted: false };
|
||||
} catch (nativeError) {
|
||||
const error = nativeError as AxiosError;
|
||||
|
||||
return { data: null, isRecentlyCompleted: false, error: error };
|
||||
return {
|
||||
data: response.data,
|
||||
isRecentlyCompleted: false,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
data: null,
|
||||
isRecentlyCompleted: false,
|
||||
error: error as AxiosError,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function getQuizData(quizId: string) {
|
||||
export async function getQuizData({ quizId, status = "" }: { quizId: string; status?: string }) {
|
||||
if (!quizId) throw new Error("No quiz id");
|
||||
|
||||
const response = await getData(quizId);
|
||||
const response = await getData({ quizId });
|
||||
const quizDataResponse = response.data;
|
||||
|
||||
if (response.error) {
|
||||
throw response.error;
|
||||
const axiosError = response.error as AxiosError;
|
||||
if (axiosError.response?.data) {
|
||||
throw new Error(
|
||||
typeof axiosError.response.data === "string"
|
||||
? axiosError.response.data
|
||||
: JSON.stringify(axiosError.response.data)
|
||||
);
|
||||
}
|
||||
throw axiosError;
|
||||
}
|
||||
if (!quizDataResponse) {
|
||||
throw new Error("Quiz not found");
|
||||
@ -142,6 +211,124 @@ export async function getQuizData(quizId: string) {
|
||||
return res;
|
||||
}
|
||||
|
||||
let page = 1;
|
||||
|
||||
export async function getQuizDataAI(quizId: string) {
|
||||
console.log("[getQuizDataAI] Starting with quizId:", quizId); // Добавлено
|
||||
let maxRetries = 50;
|
||||
|
||||
if (!quizId) {
|
||||
console.error("[getQuizDataAI] Error: No quiz id provided");
|
||||
throw new Error("No quiz id");
|
||||
}
|
||||
|
||||
let lastError: Error | null = null;
|
||||
let responseData: any = null;
|
||||
|
||||
// Первый цикл - обработка result вопросов
|
||||
console.log("[getQuizDataAI] Starting result retries loop"); // Добавлено
|
||||
let resultRetryCount = 0;
|
||||
while (resultRetryCount < maxRetries) {
|
||||
try {
|
||||
console.log(`[getQuizDataAI] Attempt ${resultRetryCount + 1} for result questions, page: ${page}`);
|
||||
const response = await getData({ quizId, page });
|
||||
console.log("[getQuizDataAI] Response from getData:", response);
|
||||
|
||||
if (response.error) {
|
||||
console.error("[getQuizDataAI] Error in response:", response.error);
|
||||
throw response.error;
|
||||
}
|
||||
if (!response.data) {
|
||||
console.error("[getQuizDataAI] Error: Quiz not found");
|
||||
throw new Error("Quiz not found");
|
||||
}
|
||||
|
||||
const hasAiResult = response.data.items.some((item) => item.typ === "result");
|
||||
console.log("[getQuizDataAI] Has AI result:", hasAiResult);
|
||||
|
||||
if (hasAiResult) {
|
||||
page++;
|
||||
resultRetryCount++;
|
||||
console.log(`[getQuizDataAI] Found result question, incrementing page to ${page}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
responseData = response;
|
||||
console.log("[getQuizDataAI] Found non-result questions, breaking loop");
|
||||
break;
|
||||
} catch (error) {
|
||||
lastError = error as Error;
|
||||
resultRetryCount++;
|
||||
console.error(`[getQuizDataAI] Error in attempt ${resultRetryCount}:`, error);
|
||||
|
||||
if (resultRetryCount >= maxRetries) {
|
||||
console.error("[getQuizDataAI] Max retries reached for result questions");
|
||||
break;
|
||||
}
|
||||
|
||||
const delay = 1500 * resultRetryCount;
|
||||
console.log(`[getQuizDataAI] Waiting ${delay}ms before next retry`);
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
}
|
||||
}
|
||||
|
||||
if (!responseData) {
|
||||
console.error("[getQuizDataAI] Failed after result retries, throwing error");
|
||||
throw lastError || new Error("Failed to get quiz data after result retries");
|
||||
}
|
||||
|
||||
// Второй цикл - обработка пустого массива
|
||||
console.log("[getQuizDataAI] Starting empty items retry loop"); // Добавлено
|
||||
let isEmpty = !responseData.data?.items.length;
|
||||
let emptyRetryCount = 0;
|
||||
|
||||
while (isEmpty && emptyRetryCount < maxRetries) {
|
||||
try {
|
||||
console.log(`[getQuizDataAI] Empty items retry ${emptyRetryCount + 1}`);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
const response = await getData({ quizId, page });
|
||||
|
||||
if (response.error) {
|
||||
console.error("[getQuizDataAI] Error in empty items check:", response.error);
|
||||
throw response.error;
|
||||
}
|
||||
if (!response.data) {
|
||||
console.error("[getQuizDataAI] Error: Quiz not found in empty check");
|
||||
throw new Error("Quiz not found");
|
||||
}
|
||||
|
||||
isEmpty = !response.data.items.length;
|
||||
console.log("[getQuizDataAI] Is items empty:", isEmpty);
|
||||
|
||||
if (!isEmpty) {
|
||||
responseData = response;
|
||||
console.log("[getQuizDataAI] Found non-empty items, updating responseData");
|
||||
}
|
||||
emptyRetryCount++;
|
||||
} catch (error) {
|
||||
lastError = error as Error;
|
||||
emptyRetryCount++;
|
||||
console.error(`[getQuizDataAI] Error in empty check attempt ${emptyRetryCount}:`, error);
|
||||
|
||||
if (emptyRetryCount >= maxRetries) {
|
||||
console.error("[getQuizDataAI] Max empty retries reached");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmpty) {
|
||||
console.error("[getQuizDataAI] Items still empty after retries");
|
||||
throw new Error("Items array is empty after maximum retries");
|
||||
}
|
||||
|
||||
// Финальная обработка
|
||||
console.log("[getQuizDataAI] Processing final response data");
|
||||
|
||||
console.log("[getQuizDataAI] Final response before return:", responseData);
|
||||
return responseData.data.items;
|
||||
}
|
||||
|
||||
type SendAnswerProps = {
|
||||
questionId: string;
|
||||
body: string | string[];
|
||||
@ -150,6 +337,8 @@ type SendAnswerProps = {
|
||||
};
|
||||
|
||||
export function sendAnswer({ questionId, body, qid, preview = false }: SendAnswerProps) {
|
||||
console.log("qid");
|
||||
console.log(qid);
|
||||
if (preview) return;
|
||||
const formData = new FormData();
|
||||
|
||||
|
77
lib/api/useQuizGetNext.ts
Normal file
77
lib/api/useQuizGetNext.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { useState } from "react";
|
||||
import { getQuizDataAI } from "./quizRelase";
|
||||
import { addQuestion, useQuizStore } from "@/stores/useQuizStore";
|
||||
import { AnyTypedQuizQuestion } from "..";
|
||||
|
||||
function qparse(q: { desc: string; id: string; req: boolean; title: string; typ: string }) {
|
||||
return {
|
||||
description: q.desc,
|
||||
id: q.id,
|
||||
required: q.req,
|
||||
title: q.title,
|
||||
type: q.typ,
|
||||
page: 0,
|
||||
content: {
|
||||
answerType: "single",
|
||||
autofill: false,
|
||||
back: "",
|
||||
hint: { text: "", video: "" },
|
||||
id: "",
|
||||
innerName: "",
|
||||
innerNameCheck: false,
|
||||
onlyNumbers: false,
|
||||
originalBack: "",
|
||||
placeholder: "",
|
||||
required: false,
|
||||
rule: {
|
||||
children: [],
|
||||
default: "",
|
||||
main: [],
|
||||
parentId: "",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const useQuizGetNext = () => {
|
||||
const { quizId, settings } = useQuizStore();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
const loadMoreQuestions = async () => {
|
||||
console.log("STATUS loadMoreQuestions");
|
||||
console.log(settings);
|
||||
console.log(settings.status);
|
||||
if (settings.status === "ai") {
|
||||
console.log("STATUS after IF");
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
console.log("STATUS after TRY TRY TRY");
|
||||
const data = await getQuizDataAI(quizId);
|
||||
console.log("data");
|
||||
console.log(data);
|
||||
const newQuestion = qparse(data[0]);
|
||||
console.log("newQuestion");
|
||||
console.log(newQuestion);
|
||||
if (newQuestion) {
|
||||
newQuestion.page = currentPage;
|
||||
//@ts-ignore
|
||||
addQuestion(newQuestion as AnyTypedQuizQuestion);
|
||||
setCurrentPage((old) => old++);
|
||||
console.log("newQuestion + page");
|
||||
console.log(newQuestion);
|
||||
return newQuestion;
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return { loadMoreQuestions, isLoading, error, currentPage };
|
||||
};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -3,7 +3,6 @@ import { QuizViewContext, createQuizViewStore } from "@/stores/quizView";
|
||||
import LoadingSkeleton from "@/ui_kit/LoadingSkeleton";
|
||||
import { useVkMetricsGoals } from "@/utils/hooks/metrics/useVkMetricsGoals";
|
||||
import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoals";
|
||||
import { QuizSettingsContext } from "@contexts/QuizDataContext";
|
||||
import { RootContainerWidthContext } from "@contexts/RootContainerWidthContext";
|
||||
import type { QuizSettings } from "@model/settingsData";
|
||||
import { Box, CssBaseline, ScopedCssBaseline, ThemeProvider } from "@mui/material";
|
||||
@ -14,13 +13,14 @@ import { handleComponentError } from "@utils/handleComponentError";
|
||||
import lightTheme from "@utils/themes/light";
|
||||
import moment from "moment";
|
||||
import { SnackbarProvider } from "notistack";
|
||||
import { startTransition, useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { startTransition, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { ApologyPage } from "./ViewPublicationPage/ApologyPage";
|
||||
import ViewPublicationPage from "./ViewPublicationPage/ViewPublicationPage";
|
||||
import { HelmetProvider } from "react-helmet-async";
|
||||
|
||||
import "moment/dist/locale/ru";
|
||||
import { useQuizStore, setQuizData, addquizid } from "@/stores/useQuizStore";
|
||||
moment.locale("ru");
|
||||
const localeText = ruRU.components.MuiLocalizationProvider.defaultProps.localeText;
|
||||
|
||||
@ -32,7 +32,16 @@ type Props = {
|
||||
className?: string;
|
||||
disableGlobalCss?: boolean;
|
||||
};
|
||||
|
||||
function isQuizSettingsValid(data: any): data is QuizSettings {
|
||||
return (
|
||||
data &&
|
||||
Array.isArray(data.questions) &&
|
||||
data.settings &&
|
||||
typeof data.cnt === "number" &&
|
||||
typeof data.recentlyCompleted === "boolean" &&
|
||||
typeof data.show_badge === "boolean"
|
||||
);
|
||||
}
|
||||
function QuizAnswererInner({
|
||||
quizSettings,
|
||||
quizId,
|
||||
@ -47,6 +56,19 @@ function QuizAnswererInner({
|
||||
const { data, error, isLoading } = useQuizData(quizId, preview);
|
||||
const vkMetrics = useVkMetricsGoals(quizSettings?.settings.cfg.vkMetricsNumber);
|
||||
const yandexMetrics = useYandexMetricsGoals(quizSettings?.settings.cfg.yandexMetricsNumber);
|
||||
const r = useQuizStore();
|
||||
const { settings, questions } = useQuizStore();
|
||||
|
||||
useEffect(() => {
|
||||
addquizid(quizId);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(settings);
|
||||
console.log(questions);
|
||||
console.log("r");
|
||||
console.log(r);
|
||||
}, [questions, settings]);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
@ -55,6 +77,18 @@ function QuizAnswererInner({
|
||||
}, 4000);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
console.log("got data");
|
||||
console.log(quizSettings);
|
||||
console.log(data);
|
||||
const quiz = quizSettings || data;
|
||||
console.log("quiz");
|
||||
console.log(quiz);
|
||||
if (quiz !== undefined) {
|
||||
console.log("is not undefined");
|
||||
setQuizData(quiz);
|
||||
}
|
||||
}, [quizSettings, data]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (rootContainerRef.current) setRootContainerWidth(rootContainerRef.current.clientWidth);
|
||||
@ -73,15 +107,15 @@ function QuizAnswererInner({
|
||||
};
|
||||
}, []);
|
||||
|
||||
console.log("settings");
|
||||
console.log(settings);
|
||||
if (isLoading) return <LoadingSkeleton />;
|
||||
if (error) return <ApologyPage error={error} />;
|
||||
// if (!data) return <LoadingSkeleton />;
|
||||
quizSettings ??= data;
|
||||
if (!quizSettings) return <ApologyPage error={new Error("quiz data is null")} />;
|
||||
|
||||
if (quizSettings.questions.length === 1 && quizSettings?.settings.cfg.noStartPage)
|
||||
return <ApologyPage error={new Error("quiz is empty")} />;
|
||||
// if (quizSettings.questions.length === 1) return <ApologyPage error={new Error("no questions found")} />;
|
||||
if (Object.keys(settings).length == 0) return <ApologyPage error={new Error("quiz data is null")} />;
|
||||
if (questions.length === 0) return <ApologyPage error={new Error("No questions found")} />;
|
||||
|
||||
if (questions.length === 1 && settings.cfg.noStartPage) return <ApologyPage error={new Error("quiz is empty")} />;
|
||||
if (!quizId) return <ApologyPage error={new Error("no quiz id")} />;
|
||||
|
||||
const quizContainer = (
|
||||
@ -106,21 +140,19 @@ function QuizAnswererInner({
|
||||
return (
|
||||
<QuizViewContext.Provider value={quizViewStore}>
|
||||
<RootContainerWidthContext.Provider value={rootContainerWidth}>
|
||||
<QuizSettingsContext.Provider value={{ ...quizSettings, quizId, preview, changeFaviconAndTitle }}>
|
||||
{disableGlobalCss ? (
|
||||
<ScopedCssBaseline
|
||||
sx={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: "transparent",
|
||||
}}
|
||||
>
|
||||
{quizContainer}
|
||||
</ScopedCssBaseline>
|
||||
) : (
|
||||
<CssBaseline>{quizContainer}</CssBaseline>
|
||||
)}
|
||||
</QuizSettingsContext.Provider>
|
||||
{disableGlobalCss ? (
|
||||
<ScopedCssBaseline
|
||||
sx={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: "transparent",
|
||||
}}
|
||||
>
|
||||
{quizContainer}
|
||||
</ScopedCssBaseline>
|
||||
) : (
|
||||
<CssBaseline>{quizContainer}</CssBaseline>
|
||||
)}
|
||||
</RootContainerWidthContext.Provider>
|
||||
</QuizViewContext.Provider>
|
||||
);
|
||||
|
@ -6,7 +6,11 @@ type Props = Partial<FallbackProps>;
|
||||
|
||||
export const ApologyPage = ({ error }: Props) => {
|
||||
let message = error.message || error.response?.data;
|
||||
console.log("message");
|
||||
console.log(message.toLowerCase());
|
||||
const { t } = useTranslation();
|
||||
console.log("t");
|
||||
console.log(t(message.toLowerCase()));
|
||||
|
||||
return (
|
||||
<Box
|
||||
|
@ -8,7 +8,6 @@ import { Inputs } from "@/components/ViewPublicationPage/ContactForm/Inputs/Inpu
|
||||
import { ContactTextBlock } from "./ContactTextBlock";
|
||||
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
|
||||
import { sendFC, SendFCParams } from "@api/quizRelase";
|
||||
|
||||
@ -25,6 +24,7 @@ import type { FormContactFieldData, FormContactFieldName } from "@model/settings
|
||||
import type { QuizQuestionResult } from "@model/questionTypes/result";
|
||||
import type { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||
import { isProduction } from "@/utils/defineDomain";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
@ -36,7 +36,7 @@ const isDisableEmail = window.location.pathname.includes("/377c7570-1bee-4320-ac
|
||||
|
||||
export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
const theme = useTheme();
|
||||
const { settings, questions, quizId, show_badge, preview } = useQuizSettings();
|
||||
const { settings, questions, quizId, show_badge, preview } = useQuizStore();
|
||||
|
||||
const [ready, setReady] = useState(false);
|
||||
const [name, setName] = useState("");
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { Box, InputAdornment, TextField as MuiTextField, TextFieldProps, Typography, useTheme } from "@mui/material";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext.ts";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext.ts";
|
||||
import { useIMask, IMask } from "react-imask";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication.ts";
|
||||
import { ChangeEvent, FC, HTMLInputTypeAttribute, useEffect, useState } from "react";
|
||||
import { CountrySelector } from "@/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx";
|
||||
import { phoneMasksByCountry } from "@utils/phoneMasksByCountry.tsx";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
type InputProps = {
|
||||
title: string;
|
||||
@ -37,7 +37,7 @@ function phoneChange(e: ChangeEvent<HTMLInputElement>, mask: string) {
|
||||
export const CustomInput = ({ title, desc, Icon, onChange, onChangePhone, isPhone, type, value }: InputProps) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 600;
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const [mask, setMask] = useState(phoneMasksByCountry["RU"][1]);
|
||||
// const { ref } = useIMask({ mask });
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext.ts";
|
||||
import NameIcon from "@icons/ContactFormIcon/NameIcon.tsx";
|
||||
import EmailIcon from "@icons/ContactFormIcon/EmailIcon.tsx";
|
||||
import TextIcon from "@icons/ContactFormIcon/TextIcon.tsx";
|
||||
@ -7,6 +6,7 @@ import { Dispatch, SetStateAction } from "react";
|
||||
import { CustomInput } from "@/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx";
|
||||
import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon.tsx";
|
||||
import PhoneInput from "react-phone-number-input";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type InputsProps = {
|
||||
@ -25,6 +25,9 @@ type InputsProps = {
|
||||
};
|
||||
};
|
||||
|
||||
const iscrutch = "/cc006b40-ccbd-4600-a1d3-f902f85aa0a0";
|
||||
const pathOnly = window.location.pathname;
|
||||
|
||||
export const Inputs = ({
|
||||
name,
|
||||
setName,
|
||||
@ -38,7 +41,7 @@ export const Inputs = ({
|
||||
setAdress,
|
||||
crutch,
|
||||
}: InputsProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const { t } = useTranslation();
|
||||
const FC = settings.cfg.formContact.fields;
|
||||
|
||||
@ -47,7 +50,11 @@ export const Inputs = ({
|
||||
<CustomInput
|
||||
onChange={({ target }) => setName(target.value)}
|
||||
id={name}
|
||||
title={FC["name"].innerText || `${t("Enter")} ${t("Name").toLowerCase()}`}
|
||||
title={
|
||||
pathOnly === iscrutch
|
||||
? "Введите имя и фамилию"
|
||||
: FC["name"].innerText || `${t("Enter")} ${t("Name").toLowerCase()}`
|
||||
}
|
||||
desc={FC["name"].text || t("Name")}
|
||||
Icon={NameIcon}
|
||||
/>
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { ReactNode } from "react";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
|
||||
import Stepper from "@ui_kit/Stepper";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
type FooterProps = {
|
||||
stepNumber: number | null;
|
||||
@ -14,7 +13,7 @@ type FooterProps = {
|
||||
|
||||
export const Footer = ({ stepNumber, nextButton, prevButton }: FooterProps) => {
|
||||
const theme = useTheme();
|
||||
const { questions, settings } = useQuizSettings();
|
||||
const { questions, settings } = useQuizStore();
|
||||
const questionsAmount = questions.filter(({ type }) => type !== "result").length;
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -40,7 +39,7 @@ export const Footer = ({ stepNumber, nextButton, prevButton }: FooterProps) => {
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
{stepNumber !== null && (
|
||||
{stepNumber !== null && settings.status !== "ai" && (
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Typography sx={{ color: theme.palette.text.primary }}>
|
||||
{t("Step")} {stepNumber} {t("of")} {questionsAmount}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { IncorrectAnswer } from "@/assets/icons/IncorrectAnswer";
|
||||
import { CorrectAnswer } from "@/assets/icons/CorrectAnswer";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { useQuizSettings } from "@/contexts/QuizDataContext";
|
||||
import { useQuizViewStore } from "@/stores/quizView";
|
||||
import { AnyTypedQuizQuestion, QuizQuestionVariant } from "@/index";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
export const PointSystemResultList = () => {
|
||||
const theme = useTheme();
|
||||
const { questions } = useQuizSettings();
|
||||
const { questions } = useQuizStore();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
@ -15,7 +15,6 @@ import { Varimg } from "./questions/Varimg";
|
||||
|
||||
import type { RealTypedQuizQuestion } from "../../model/questionTypes/shared";
|
||||
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { NameplateLogoFQ } from "@icons/NameplateLogoFQ";
|
||||
import { NameplateLogoFQDark } from "@icons/NameplateLogoFQDark";
|
||||
import { notReachable } from "@utils/notReachable";
|
||||
@ -24,6 +23,7 @@ import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
import { DESIGN_LIST } from "@/utils/designList";
|
||||
import { type ReactNode } from "react";
|
||||
import { isProduction } from "@/utils/defineDomain";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
type Props = {
|
||||
currentQuestion: RealTypedQuizQuestion;
|
||||
@ -41,7 +41,7 @@ export const Question = ({
|
||||
questionSelect,
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const { settings, show_badge, quizId } = useQuizSettings();
|
||||
const { settings, show_badge, quizId } = useQuizStore();
|
||||
|
||||
return (
|
||||
<Box
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useQuizSettings } from "@/contexts/QuizDataContext";
|
||||
import { AnyTypedQuizQuestion } from "@/model/questionTypes/shared";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { Box, FormControl, MenuItem, Select as MuiSelect, useTheme } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@ -10,7 +10,7 @@ interface Props {
|
||||
|
||||
export default function QuestionSelect({ selectedQuestion, setQuestion }: Props) {
|
||||
const theme = useTheme();
|
||||
const { questions, preview } = useQuizSettings();
|
||||
const { questions, preview } = useQuizStore();
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!preview) return null;
|
||||
@ -38,6 +38,8 @@ export default function QuestionSelect({ selectedQuestion, setQuestion }: Props)
|
||||
variant="outlined"
|
||||
value={selectedQuestion.id}
|
||||
placeholder={t("Question title")}
|
||||
//(*.*)
|
||||
// placeholder={t("Question title")}
|
||||
onChange={({ target }) => {
|
||||
setQuestion(target.value);
|
||||
}}
|
||||
|
@ -4,7 +4,6 @@ import { Box, Button, Link, Typography, useTheme } from "@mui/material";
|
||||
import { useQuizViewStore } from "@/stores/quizView";
|
||||
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
|
||||
import { useVkMetricsGoals } from "@/utils/hooks/metrics/useVkMetricsGoals";
|
||||
import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoals";
|
||||
@ -19,6 +18,7 @@ import { PointSystemResultList } from "./PointSystemResultList";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { sendFC, sendResult } from "@/api/quizRelase";
|
||||
import { isProduction } from "@/utils/defineDomain";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type ResultFormProps = {
|
||||
@ -29,7 +29,7 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const isTablet = useRootContainerSize() < 1000;
|
||||
const { settings, show_badge, quizId, questions, preview } = useQuizSettings();
|
||||
const { settings, show_badge, quizId, questions, preview } = useQuizStore();
|
||||
const setCurrentQuizStep = useQuizViewStore((state) => state.setCurrentQuizStep);
|
||||
//Список засчитанных баллов для балловых квизов
|
||||
const pointsSum = useQuizViewStore((state) => state.pointsSum);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
import { notReachable } from "@utils/notReachable";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
@ -22,7 +22,7 @@ type LayoutProps = Omit<StartPageDesktopProps, "startpageType">;
|
||||
const StandartLayout = ({ alignType, quizHeaderBlock, quizMainBlock, backgroundBlock }: LayoutProps) => {
|
||||
const size = useRootContainerSize();
|
||||
const isTablet = size >= 700 && size < 1100;
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -170,7 +170,7 @@ const ExpandedLayout = ({ alignType, quizHeaderBlock, quizMainBlock, backgroundB
|
||||
|
||||
const CenteredLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock }: LayoutProps) => {
|
||||
const isTablet = useRootContainerSize() < 1100;
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
import { notReachable } from "@utils/notReachable";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
@ -18,7 +18,7 @@ type StartPageMobileProps = {
|
||||
type MobileLayoutProps = Omit<StartPageMobileProps, "startpageType">;
|
||||
|
||||
const StandartMobileLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock }: MobileLayoutProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -163,7 +163,7 @@ const ExpandedMobileLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock
|
||||
);
|
||||
|
||||
const CenteredMobileLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock }: MobileLayoutProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -2,7 +2,7 @@ import { Box, Button, ButtonBase, Link, Paper, Typography, useTheme } from "@mui
|
||||
|
||||
import { QuizPreviewLayoutByType } from "./QuizPreviewLayoutByType";
|
||||
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
|
||||
import { useUADevice } from "@utils/hooks/useUADevice";
|
||||
@ -20,7 +20,7 @@ import { isProduction } from "@/utils/defineDomain";
|
||||
|
||||
export const StartPageViewPublication = () => {
|
||||
const theme = useTheme();
|
||||
const { settings, show_badge, quizId, questions } = useQuizSettings();
|
||||
const { settings, show_badge, quizId, questions } = useQuizStore();
|
||||
const { isMobileDevice } = useUADevice();
|
||||
const setCurrentQuizStep = useQuizViewStore((state) => state.setCurrentQuizStep);
|
||||
|
||||
|
@ -3,7 +3,6 @@ import { extractImageLinksFromQuestion } from "@/utils/extractImageLinks";
|
||||
import { useVKMetrics } from "@/utils/hooks/metrics/useVKMetrics";
|
||||
import { useYandexMetrics } from "@/utils/hooks/metrics/useYandexMetrics";
|
||||
import { sendQuestionAnswer } from "@/utils/sendQuestionAnswer";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { ThemeProvider, Typography } from "@mui/material";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { useQuestionFlowControl } from "@utils/hooks/useQuestionFlowControl";
|
||||
@ -19,9 +18,10 @@ import { StartPageViewPublication } from "./StartPageViewPublication";
|
||||
import NextButton from "./tools/NextButton";
|
||||
import PrevButton from "./tools/PrevButton";
|
||||
import unscreen from "@/ui_kit/unscreen";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
export default function ViewPublicationPage() {
|
||||
const { settings, recentlyCompleted, quizId, preview, changeFaviconAndTitle, questions } = useQuizSettings();
|
||||
const { settings, recentlyCompleted, quizId, preview, changeFaviconAndTitle } = useQuizStore();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const ownVariants = useQuizViewStore((state) => state.ownVariants);
|
||||
let currentQuizStep = useQuizViewStore((state) => state.currentQuizStep);
|
||||
@ -106,16 +106,15 @@ export default function ViewPublicationPage() {
|
||||
}
|
||||
nextButton={
|
||||
<NextButton
|
||||
isNextButtonEnabled={isNextButtonEnabled}
|
||||
moveToNextQuestion={() => {
|
||||
isNextButtonEnabled={settings.status === "ai" || isNextButtonEnabled}
|
||||
moveToNextQuestion={async () => {
|
||||
if (!preview) {
|
||||
await sendQuestionAnswer(quizId, currentQuestion, currentAnswer, ownVariants)?.catch((e) => {
|
||||
enqueueSnackbar("Ошибка при отправке ответа");
|
||||
console.error("Error sending answer", e);
|
||||
});
|
||||
}
|
||||
moveToNextQuestion();
|
||||
|
||||
if (preview) return;
|
||||
|
||||
sendQuestionAnswer(quizId, currentQuestion, currentAnswer, ownVariants)?.catch((e) => {
|
||||
enqueueSnackbar("Ошибка при отправке ответа");
|
||||
console.error("Error sending answer", e);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useQuizViewStore } from "@/stores/quizView";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import CalendarIcon from "@icons/CalendarIcon";
|
||||
import type { QuizQuestionDate } from "@model/questionTypes/date";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
@ -13,7 +13,7 @@ type DateProps = {
|
||||
};
|
||||
|
||||
export default ({ currentQuestion }: DateProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const theme = useTheme();
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { useQuizSettings } from "@/contexts/QuizDataContext";
|
||||
import { useQuizViewStore } from "@/stores/quizView";
|
||||
import type { QuizQuestionDate } from "@model/questionTypes/date";
|
||||
import { DateCalendar } from "@mui/x-date-pickers";
|
||||
@ -7,6 +6,7 @@ import type { Moment } from "moment";
|
||||
import moment from "moment";
|
||||
import { Box, Paper, TextField, useTheme } from "@mui/material";
|
||||
import { useRootContainerSize } from "@/contexts/RootContainerWidthContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type DateProps = {
|
||||
@ -17,7 +17,7 @@ export default ({ currentQuestion }: DateProps) => {
|
||||
const theme = useTheme();
|
||||
const today = moment();
|
||||
const isMobile = useRootContainerSize() < 690;
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { QuestionVariant } from "@/model/questionTypes/shared";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import {
|
||||
Box,
|
||||
Checkbox,
|
||||
@ -105,7 +105,7 @@ export const EmojiVariant = ({
|
||||
questionLargeCheck,
|
||||
ownPlaceholder,
|
||||
}: EmojiVariantProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state);
|
||||
const theme = useTheme();
|
||||
|
@ -3,7 +3,6 @@ import { Box, ButtonBase, Skeleton, Typography, useTheme } from "@mui/material";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { sendAnswer, sendFile } from "@api/quizRelase";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
@ -18,6 +17,7 @@ import UploadIcon from "@icons/UploadIcon";
|
||||
|
||||
import type { QuizQuestionFile } from "@model/questionTypes/file";
|
||||
import type { ModalWarningType } from "./index";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type UploadFileProps = {
|
||||
@ -28,7 +28,7 @@ type UploadFileProps = {
|
||||
};
|
||||
|
||||
export const UploadFile = ({ currentQuestion, setModalWarningType, isSending, setIsSending }: UploadFileProps) => {
|
||||
const { quizId, preview } = useQuizSettings();
|
||||
const { quizId, preview } = useQuizStore();
|
||||
const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState<boolean>(false);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Box, IconButton, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import CloseBold from "@icons/CloseBold";
|
||||
|
||||
import type { QuizQuestionFile } from "@model/questionTypes/file";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type UploadedFileProps = {
|
||||
@ -15,7 +15,7 @@ type UploadedFileProps = {
|
||||
};
|
||||
|
||||
export const UploadedFile = ({ currentQuestion, setIsSending }: UploadedFileProps) => {
|
||||
const { quizId, preview } = useQuizSettings();
|
||||
const { quizId, preview } = useQuizStore();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const theme = useTheme();
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { CheckboxIcon } from "@/assets/icons/Checkbox";
|
||||
import type { QuestionVariant, QuestionVariantWithEditedImages } from "@/model/questionTypes/shared";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { Box, Checkbox, FormControlLabel, Input, Radio, TextareaAutosize, Typography, useTheme } from "@mui/material";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
@ -8,6 +7,7 @@ import RadioIcon from "@ui_kit/RadioIcon";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
import { useMemo, type MouseEvent, useRef, useEffect } from "react";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type ImagesProps = {
|
||||
@ -93,7 +93,7 @@ export const ImageVariant = ({
|
||||
questionLargeCheck,
|
||||
ownPlaceholder,
|
||||
}: ImagesProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const { deleteAnswer, updateAnswer } = useQuizViewStore((state) => state);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import type { QuizQuestionNumber } from "@model/questionTypes/number";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
@ -20,7 +20,7 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
||||
const [reversedInputValue, setReversedInputValue] = useState<string>("0");
|
||||
const [reversedMinRange, setReversedMinRange] = useState<string>("0");
|
||||
const [reversedMaxRange, setReversedMaxRange] = useState<string>("100000000000");
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const theme = useTheme();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Select as SelectComponent } from "@/components/ViewPublicationPage/tools/Select";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import type { QuizQuestionSelect } from "@model/questionTypes/select";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
@ -10,7 +10,7 @@ type SelectProps = {
|
||||
};
|
||||
|
||||
export const Select = ({ currentQuestion }: SelectProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state);
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const theme = useTheme();
|
||||
|
@ -3,13 +3,13 @@ import { Box, Typography, useTheme } from "@mui/material";
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
|
||||
import { Answer, useQuizViewStore } from "@stores/quizView";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import { useMemo, type ChangeEvent } from "react";
|
||||
import type { QuizQuestionText } from "@model/questionTypes/text";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
interface TextNormalProps {
|
||||
currentQuestion: QuizQuestionText;
|
||||
@ -18,7 +18,7 @@ interface TextNormalProps {
|
||||
}
|
||||
|
||||
export const TextNormal = ({ currentQuestion, answer }: TextNormalProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const isTablet = useRootContainerSize() < 850;
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { Box, TextField as MuiTextField, TextFieldProps, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { Answer, useQuizViewStore } from "@stores/quizView";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import type { ChangeEvent, FC } from "react";
|
||||
import type { QuizQuestionText } from "@model/questionTypes/text";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>; // temporary fix ts(2590)
|
||||
|
||||
@ -45,7 +45,7 @@ interface TextSpecialProps {
|
||||
}
|
||||
|
||||
export const TextSpecial = ({ currentQuestion, answer, stepNumber }: TextSpecialProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const isHorizontal = ORIENTATION[Number(stepNumber) - 1].horizontal;
|
||||
const theme = useTheme();
|
||||
@ -129,7 +129,7 @@ export const TextSpecial = ({ currentQuestion, answer, stepNumber }: TextSpecial
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
style={{ width: "100%", height: "100%", objectFit: "contain" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { Box, TextField as MuiTextField, TextFieldProps, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { Answer, useQuizViewStore } from "@stores/quizView";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import type { ChangeEvent, FC } from "react";
|
||||
import type { QuizQuestionText } from "@model/questionTypes/text";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>; // temporary fix ts(2590)
|
||||
|
||||
@ -18,7 +18,7 @@ interface TextSpecialProps {
|
||||
}
|
||||
|
||||
export const TextSpecialHorisontal = ({ currentQuestion, answer, stepNumber }: TextSpecialProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const isHorizontal = true;
|
||||
const theme = useTheme();
|
||||
@ -61,7 +61,7 @@ export const TextSpecialHorisontal = ({ currentQuestion, answer, stepNumber }: T
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
style={{ width: "100%", height: "100%", objectFit: "contain" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
@ -102,7 +102,7 @@ export const TextSpecialHorisontal = ({ currentQuestion, answer, stepNumber }: T
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
style={{ width: "100%", height: "100%", objectFit: "contain" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { TextNormal } from "./TextNormal";
|
||||
import { TextSpecial } from "./TextSpecial";
|
||||
import { TextSpecialHorisontal } from "./TextSpecialHorisontal";
|
||||
|
||||
import type { QuizQuestionText } from "@model/questionTypes/text";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
type TextProps = {
|
||||
currentQuestion: QuizQuestionText;
|
||||
@ -14,11 +14,11 @@ type TextProps = {
|
||||
const pathOnly = window.location.pathname;
|
||||
|
||||
export const Text = ({ currentQuestion, stepNumber }: TextProps) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
|
||||
if (pathOnly === "/92ed5e3e-8e6a-491e-87d0-d3197682d0e3")
|
||||
if (pathOnly === "/92ed5e3e-8e6a-491e-87d0-d3197682d0e3" || pathOnly === "/cc006b40-ccbd-4600-a1d3-f902f85aa0a0")
|
||||
return (
|
||||
<TextSpecialHorisontal
|
||||
currentQuestion={currentQuestion}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { CheckboxIcon } from "@icons/Checkbox";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import type { QuestionVariant } from "@model/questionTypes/shared";
|
||||
import {
|
||||
Checkbox,
|
||||
@ -101,7 +100,7 @@ export const VariantItem = ({
|
||||
questionLargeCheck: boolean;
|
||||
ownPlaceholder: string;
|
||||
}) => {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const theme = useTheme();
|
||||
const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state);
|
||||
const { t } = useTranslation();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { QuestionVariant, QuestionVariantWithEditedImages } from "@/model/questionTypes/shared";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import {
|
||||
FormControlLabel,
|
||||
TextareaAutosize,
|
||||
@ -104,8 +104,9 @@ export const VarimgVariant = ({
|
||||
answer,
|
||||
}: VarimgVariantProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const { settings } = useQuizStore();
|
||||
const { t } = useTranslation();
|
||||
const { settings } = useQuizSettings();
|
||||
const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state);
|
||||
|
||||
const sendVariant = async (event: MouseEvent<HTMLLabelElement>) => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { Button } from "@mui/material";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@ -9,7 +9,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function NextButton({ isNextButtonEnabled, moveToNextQuestion }: Props) {
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@ -25,7 +25,7 @@ export default function NextButton({ isNextButtonEnabled, moveToNextQuestion }:
|
||||
}}
|
||||
onClick={moveToNextQuestion}
|
||||
>
|
||||
{t("Next")} →
|
||||
далее →{/* {t("Next")} → (*.*) */}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Button, useTheme } from "@mui/material";
|
||||
import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface Props {
|
||||
@ -11,7 +11,7 @@ interface Props {
|
||||
|
||||
export default function PrevButton({ isPreviousButtonEnabled, moveToPrevQuestion }: Props) {
|
||||
const theme = useTheme();
|
||||
const { settings } = useQuizSettings();
|
||||
const { settings } = useQuizStore();
|
||||
const isMobileMini = useRootContainerSize() < 382;
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
@ -37,7 +37,8 @@ export default function PrevButton({ isPreviousButtonEnabled, moveToPrevQuestion
|
||||
}}
|
||||
onClick={moveToPrevQuestion}
|
||||
>
|
||||
{isMobileMini ? "←" : `← ${t("Prev")}`}
|
||||
{isMobileMini ? "←" : `← назад`}
|
||||
{/* {isMobileMini ? "←" : `← ${t("Prev")}`} (*.*) */}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
import { QuizSettings } from "@model/settingsData";
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
export const QuizSettingsContext = createContext<
|
||||
| (QuizSettings & {
|
||||
quizId: string;
|
||||
preview: boolean;
|
||||
changeFaviconAndTitle: boolean;
|
||||
})
|
||||
| null
|
||||
>(null);
|
||||
|
||||
export const useQuizSettings = () => {
|
||||
const quizSettings = useContext(QuizSettingsContext);
|
||||
if (quizSettings === null) throw new Error("QuizSettings context is null");
|
||||
|
||||
return quizSettings;
|
||||
};
|
@ -12,6 +12,7 @@ export interface GetQuizDataResponse {
|
||||
due: number;
|
||||
delay: number;
|
||||
pausable: boolean;
|
||||
status: "start" | "stop" | "ai";
|
||||
};
|
||||
items: {
|
||||
id: number;
|
||||
@ -49,6 +50,7 @@ export function parseQuizData(quizDataResponse: GetQuizDataResponse): Omit<QuizS
|
||||
due: quizDataResponse.settings.due,
|
||||
delay: quizDataResponse.settings.delay,
|
||||
pausable: quizDataResponse.settings.pausable,
|
||||
status: quizDataResponse.settings.status,
|
||||
};
|
||||
|
||||
return { cnt: quizDataResponse.cnt, settings, questions: items, show_badge: quizDataResponse.show_badge };
|
||||
|
@ -51,6 +51,7 @@ export type QuizSettingsConfig = {
|
||||
delay: number;
|
||||
pausable: boolean;
|
||||
cfg: QuizConfig;
|
||||
status: "start" | "stop" | "ai";
|
||||
};
|
||||
|
||||
export type QuizSettings = {
|
||||
|
39
lib/stores/useQuizStore.ts
Normal file
39
lib/stores/useQuizStore.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { create } from "zustand";
|
||||
import { produce } from "immer";
|
||||
import { QuizSettings, QuizSettingsConfig } from "@model/settingsData";
|
||||
import { AnyTypedQuizQuestion } from "..";
|
||||
|
||||
export type QuizStore = QuizSettings & {
|
||||
quizId: string;
|
||||
preview: boolean;
|
||||
changeFaviconAndTitle: boolean;
|
||||
};
|
||||
|
||||
export const useQuizStore = create<QuizStore>(() => ({
|
||||
settings: {} as QuizSettingsConfig,
|
||||
questions: [],
|
||||
quizId: "",
|
||||
preview: false,
|
||||
changeFaviconAndTitle: false,
|
||||
cnt: 0,
|
||||
recentlyCompleted: false,
|
||||
show_badge: false,
|
||||
}));
|
||||
|
||||
export const setQuizData = (data: QuizSettings) => {
|
||||
console.log("zusstand");
|
||||
console.log(data);
|
||||
useQuizStore.setState((state: QuizStore) => ({ ...state, ...data }));
|
||||
};
|
||||
export const addQuestion = (newQuestion: AnyTypedQuizQuestion) =>
|
||||
useQuizStore.setState(
|
||||
produce((state: QuizStore) => {
|
||||
state.questions.push(newQuestion);
|
||||
})
|
||||
);
|
||||
export const addquizid = (id: string) =>
|
||||
useQuizStore.setState(
|
||||
produce((state: QuizStore) => {
|
||||
state.quizId = id;
|
||||
})
|
||||
);
|
@ -1,18 +1,31 @@
|
||||
import { useCallback, useDebugValue, useMemo, useState } from "react";
|
||||
import { useCallback, useDebugValue, useEffect, useMemo, useState } from "react";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import moment from "moment";
|
||||
|
||||
import { isResultQuestionEmpty } from "@/components/ViewPublicationPage/tools/checkEmptyData";
|
||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import { useVkMetricsGoals } from "@/utils/hooks/metrics/useVkMetricsGoals";
|
||||
import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoals";
|
||||
import { AnyTypedQuizQuestion } from "@/index";
|
||||
import { getQuizData } from "@/api/quizRelase";
|
||||
import { useQuizGetNext } from "@/api/useQuizGetNext";
|
||||
|
||||
let isgetting = false;
|
||||
|
||||
export function useQuestionFlowControl() {
|
||||
//Получаем инфо о квизе и список вопросов.
|
||||
const { settings, questions } = useQuizSettings();
|
||||
const { loadMoreQuestions } = useQuizGetNext();
|
||||
const { settings, questions, quizId, cnt } = useQuizStore();
|
||||
|
||||
useEffect(() => {
|
||||
console.log("useQuestionFlowControl useEffect");
|
||||
console.log(questions);
|
||||
}, [questions]);
|
||||
console.log(questions);
|
||||
|
||||
//Когда квиз линейный, не ветвящийся, мы идём по вопросам по их порядковому номеру. Это их page.
|
||||
//За корректность page отвечает конструктор квизов. Интересный факт, если в конструкторе удалить из середины вопрос, то случится куча запросов изменения вопросов с изменением этого page
|
||||
const sortedQuestions = useMemo(() => {
|
||||
@ -20,6 +33,7 @@ export function useQuestionFlowControl() {
|
||||
}, [questions]);
|
||||
//React сам будет менять визуал - главное говорить из какого вопроса ему брать инфо. Изменение этой переменной меняет визуал.
|
||||
const [currentQuestionId, setCurrentQuestionId] = useState<string | null>(getFirstQuestionId);
|
||||
const [headAI, setHeadAI] = useState(0);
|
||||
//Список ответов на вопрос. Мы записываем ответы локально, параллельно отправляя на бек информацию о ответах
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
//Список засчитанных баллов для балловых квизов
|
||||
@ -33,7 +47,10 @@ export function useQuestionFlowControl() {
|
||||
//Изменение стейта (переменной currentQuestionId) ведёт к пересчёту что же за объект сейчас используется. Мы каждый раз просто ищем в списке
|
||||
const currentQuestion = sortedQuestions.find((question) => question.id === currentQuestionId) ?? sortedQuestions[0];
|
||||
|
||||
// console.log(currentQuestion)
|
||||
console.log("currentQuestion");
|
||||
console.log(currentQuestion);
|
||||
console.log("filted");
|
||||
console.log(sortedQuestions.find((question) => question.id === currentQuestionId));
|
||||
|
||||
//Индекс текущего вопроса только если квиз линейный
|
||||
const linearQuestionIndex = //: number | null
|
||||
@ -113,7 +130,7 @@ export function useQuestionFlowControl() {
|
||||
return nextQuestionIdPointsLogic();
|
||||
}
|
||||
return nextQuestionIdMainLogic();
|
||||
}, [nextQuestionIdMainLogic, nextQuestionIdPointsLogic, settings.cfg.score]);
|
||||
}, [nextQuestionIdMainLogic, nextQuestionIdPointsLogic, settings.cfg.score, questions]);
|
||||
|
||||
//Поиск предыдущго вопроса либо по индексу либо по id родителя
|
||||
const prevQuestion =
|
||||
@ -196,21 +213,51 @@ export function useQuestionFlowControl() {
|
||||
const moveToPrevQuestion = useCallback(() => {
|
||||
if (!prevQuestion) throw new Error("Previous question not found");
|
||||
|
||||
if (settings.status === "ai" && headAI > 0) setHeadAI((old) => old--);
|
||||
setCurrentQuestionId(prevQuestion.id);
|
||||
}, [prevQuestion]);
|
||||
|
||||
//рычаг управления из визуала в эту функцию
|
||||
const moveToNextQuestion = useCallback(() => {
|
||||
if (!nextQuestion) throw new Error("Next question not found");
|
||||
const moveToNextQuestion = useCallback(async () => {
|
||||
// Если есть следующий вопрос в уже загруженных - используем его
|
||||
|
||||
// Засчитываем переход с вопроса дальше
|
||||
vkMetrics.questionPassed(currentQuestion.id);
|
||||
yandexMetrics.questionPassed(currentQuestion.id);
|
||||
if (nextQuestion) {
|
||||
vkMetrics.questionPassed(currentQuestion.id);
|
||||
yandexMetrics.questionPassed(currentQuestion.id);
|
||||
|
||||
if (nextQuestion.type === "result") return showResult();
|
||||
if (nextQuestion.type === "result") return showResult();
|
||||
setCurrentQuestionId(nextQuestion.id);
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentQuestionId(nextQuestion.id);
|
||||
}, [currentQuestion.id, nextQuestion, showResult, vkMetrics, yandexMetrics]);
|
||||
// Если следующего нет - загружаем новый
|
||||
try {
|
||||
const newQuestion = await loadMoreQuestions();
|
||||
console.log("Ффункция некст вопрос получила его с бека: ");
|
||||
console.log(newQuestion);
|
||||
if (newQuestion) {
|
||||
vkMetrics.questionPassed(currentQuestion.id);
|
||||
yandexMetrics.questionPassed(currentQuestion.id);
|
||||
console.log("МЫ ПАЛУЧИЛИ НОВЫЙ ВОПРОС");
|
||||
console.log(newQuestion);
|
||||
console.log("typeof newQuestion.id");
|
||||
console.log(typeof newQuestion.id);
|
||||
setCurrentQuestionId(newQuestion.id);
|
||||
setHeadAI((old) => old++);
|
||||
}
|
||||
} catch (error) {
|
||||
enqueueSnackbar("Ошибка загрузки следующего вопроса");
|
||||
}
|
||||
}, [
|
||||
currentQuestion.id,
|
||||
nextQuestion,
|
||||
showResult,
|
||||
vkMetrics,
|
||||
yandexMetrics,
|
||||
linearQuestionIndex,
|
||||
loadMoreQuestions,
|
||||
questions,
|
||||
]);
|
||||
|
||||
//рычаг управления из визуала в эту функцию
|
||||
const setQuestion = useCallback(
|
||||
@ -234,6 +281,10 @@ export function useQuestionFlowControl() {
|
||||
return hasAnswer;
|
||||
}
|
||||
|
||||
console.log(linearQuestionIndex);
|
||||
console.log(questions.length);
|
||||
console.log(cnt);
|
||||
if (linearQuestionIndex !== null && questions.length < cnt) return true;
|
||||
return Boolean(nextQuestion);
|
||||
}, [answers, currentQuestion, nextQuestion]);
|
||||
|
||||
@ -246,7 +297,8 @@ export function useQuestionFlowControl() {
|
||||
|
||||
return {
|
||||
currentQuestion,
|
||||
currentQuestionStepNumber: linearQuestionIndex === null ? null : linearQuestionIndex + 1,
|
||||
currentQuestionStepNumber:
|
||||
settings.status === "ai" ? null : linearQuestionIndex === null ? null : linearQuestionIndex + 1,
|
||||
nextQuestion,
|
||||
isNextButtonEnabled,
|
||||
isPreviousButtonEnabled,
|
||||
|
@ -4,7 +4,7 @@ import { OwnVariant, QuestionAnswer, createQuizViewStore } from "@/stores/quizVi
|
||||
import moment from "moment";
|
||||
import { notReachable } from "./notReachable";
|
||||
|
||||
export function sendQuestionAnswer(
|
||||
export async function sendQuestionAnswer(
|
||||
quizId: string,
|
||||
question: RealTypedQuizQuestion,
|
||||
questionAnswer: QuestionAnswer | undefined,
|
||||
|
@ -52,5 +52,6 @@
|
||||
"familiarized": "acknowledged",
|
||||
"and": "and",
|
||||
"Get results": "Get results",
|
||||
"Data sent successfully": "Data sent successfully"
|
||||
"Data sent successfully": "Data sent successfully",
|
||||
"Step": "Step"
|
||||
}
|
||||
|
@ -52,5 +52,6 @@
|
||||
"familiarized": "ознакомлен",
|
||||
"and": "и",
|
||||
"Get results": "Получить результаты",
|
||||
"Data sent successfully": "Данные успешно отправлены"
|
||||
"Data sent successfully": "Данные успешно отправлены",
|
||||
"Step": "Шаг"
|
||||
}
|
||||
|
@ -52,5 +52,6 @@
|
||||
"familiarized": "tanishdim",
|
||||
"and": "va",
|
||||
"Get results": "Natijalarni olish",
|
||||
"Data sent successfully": "Ma'lumotlar muvaffaqiyatli yuborildi"
|
||||
"Data sent successfully": "Ma'lumotlar muvaffaqiyatli yuborildi",
|
||||
"Step": "Qadam"
|
||||
}
|
||||
|
@ -42,6 +42,18 @@ i18n
|
||||
lookupFromPathIndex: 0,
|
||||
caches: [], // Не использовать localStorage
|
||||
},
|
||||
parseMissingKeyHandler: (key) => {
|
||||
console.warn("Missing translation:", key);
|
||||
return key; // Вернёт ключ вместо ошибки
|
||||
},
|
||||
missingKeyHandler: (lngs, ns, key) => {
|
||||
console.error("🚨 Missing i18n key:", {
|
||||
key,
|
||||
languages: lngs,
|
||||
namespace: ns,
|
||||
stack: new Error().stack, // Выведет стек вызовов
|
||||
});
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
//console.log("i18n инициализирован! Текущий язык:", i18n.language);
|
||||
|
Loading…
Reference in New Issue
Block a user