diff --git a/package.json b/package.json
index 8d65845..5c14905 100755
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"notistack": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-error-boundary": "^4.0.12",
"react-router-dom": "^6.21.3",
"swr": "^2.2.4",
"typescript": "^5.2.2",
diff --git a/src/App.tsx b/src/App.tsx
index 398a58d..5e2e7ad 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,8 +1,7 @@
import { Box } from "@mui/material";
-import ContextProviders from "./ContextProviders";
-import { QuizIdContext } from "./contexts/QuizIdContext";
import { useParams } from "react-router-dom";
-import { ViewPage } from "./pages/ViewPublicationPage/ViewPublicationPage";
+import QuizAnswerer from "./QuizAnswerer";
+import { QuizIdContext } from "./contexts/QuizIdContext";
const defaultQuizId = "ef836ff8-35b1-4031-9acf-af5766bac2b2";
@@ -12,13 +11,11 @@ export default function App() {
return (
-
-
-
-
-
+
+
+
);
}
diff --git a/src/ContextProviders.tsx b/src/QuizAnswerer.tsx
similarity index 58%
rename from src/ContextProviders.tsx
rename to src/QuizAnswerer.tsx
index ea17a51..3d3e935 100644
--- a/src/ContextProviders.tsx
+++ b/src/QuizAnswerer.tsx
@@ -2,20 +2,22 @@ import { CssBaseline, ThemeProvider } from "@mui/material";
import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
import { ruRU } from '@mui/x-date-pickers/locales';
+import LoadingSkeleton from "@ui_kit/LoadingSkeleton";
+import { handleComponentError } from "@utils/handleComponentError";
import moment from "moment";
import { SnackbarProvider } from 'notistack';
+import { Suspense } from "react";
+import { ErrorBoundary } from "react-error-boundary";
import { SWRConfig } from "swr";
+import { ApologyPage } from "./pages/ViewPublicationPage/ApologyPage";
+import { ViewPage } from "./pages/ViewPublicationPage/ViewPublicationPage";
import lightTheme from "./utils/themes/light";
moment.locale("ru");
const localeText = ruRU.components.MuiLocalizationProvider.defaultProps.localeText;
-interface Props {
- children: JSX.Element;
-}
-
-export default function ContextProviders({ children }: Props) {
+export default function QuizAnswerer() {
return (
- {children}
+ }
+ onError={handleComponentError}
+ >
+ }>
+
+
+
);
-}
+}
diff --git a/src/WidgetApp.tsx b/src/WidgetApp.tsx
index f13c684..a6c7215 100644
--- a/src/WidgetApp.tsx
+++ b/src/WidgetApp.tsx
@@ -1,7 +1,6 @@
import { Box } from "@mui/material";
-import ContextProviders from "./ContextProviders";
+import QuizAnswerer from "./QuizAnswerer";
import { QuizIdContext } from "./contexts/QuizIdContext";
-import { ViewPage } from "./pages/ViewPublicationPage/ViewPublicationPage";
interface Props {
@@ -12,14 +11,12 @@ export default function WidgetApp({ quizId }: Props) {
return (
-
-
-
-
-
+
+
+
);
}
diff --git a/src/model/api/getQuizData.ts b/src/model/api/getQuizData.ts
index 9fab68d..96c44fd 100644
--- a/src/model/api/getQuizData.ts
+++ b/src/model/api/getQuizData.ts
@@ -24,8 +24,8 @@ export interface GetQuizDataResponse {
}[];
}
-export function parseQuizData(quizDataResponse: GetQuizDataResponse, quizId: string): QuizSettings {
- const items: QuizSettings["items"] = quizDataResponse.items.map((item) => {
+export function parseQuizData(quizDataResponse: GetQuizDataResponse, quizId: string): Omit {
+ const items: QuizSettings["questions"] = quizDataResponse.items.map((item) => {
const content = JSON.parse(item.c);
return {
@@ -51,5 +51,5 @@ export function parseQuizData(quizDataResponse: GetQuizDataResponse, quizId: str
pausable: quizDataResponse.settings.pausable
};
- return { cnt: quizDataResponse.cnt, settings, items };
+ return { cnt: quizDataResponse.cnt, settings, questions: items };
}
diff --git a/src/model/settingsData.ts b/src/model/settingsData.ts
index 29d7fb1..bfbbd1d 100644
--- a/src/model/settingsData.ts
+++ b/src/model/settingsData.ts
@@ -30,7 +30,7 @@ export type FCField = {
};
export type QuizSettings = {
- items: AnyTypedQuizQuestion[];
+ questions: AnyTypedQuizQuestion[];
settings: {
qid: string;
fp: boolean;
@@ -43,6 +43,7 @@ export type QuizSettings = {
cfg: QuizConfig;
};
cnt: number;
+ recentlyCompleted: boolean;
};
export interface QuizConfig {
diff --git a/src/pages/ViewPublicationPage/ContactForm.tsx b/src/pages/ViewPublicationPage/ContactForm.tsx
index 572b991..321423a 100644
--- a/src/pages/ViewPublicationPage/ContactForm.tsx
+++ b/src/pages/ViewPublicationPage/ContactForm.tsx
@@ -1,22 +1,21 @@
import AddressIcon from "@icons/ContactFormIcon/AddressIcon";
-import NameIcon from "@icons/ContactFormIcon/NameIcon";
import EmailIcon from "@icons/ContactFormIcon/EmailIcon";
+import NameIcon from "@icons/ContactFormIcon/NameIcon";
import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon";
import TextIcon from "@icons/ContactFormIcon/TextIcon";
import { Box, Button, InputAdornment, Link, TextField as MuiTextField, TextFieldProps, Typography, useMediaQuery, useTheme } from "@mui/material";
-
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import { FC, useRef, useState } from "react";
import { sendFC } from "@api/quizRelase";
import { NameplateLogo } from "@icons/NameplateLogo";
import { QuizQuestionResult } from "@model/questionTypes/result";
-import { quizThemes } from "@utils/themes/Publication/themePublication";
+import { useQuizData } from "@utils/hooks/useQuizData";
import { enqueueSnackbar } from "notistack";
import { ApologyPage } from "./ApologyPage";
import { checkEmptyData } from "./tools/checkEmptyData";
-import { useQuestionsStore } from "@stores/quizData/store";
+import { quizThemes } from "@utils/themes/Publication/themePublication";
const TextField = MuiTextField as unknown as FC; // temporary fix ts(2590)
@@ -74,7 +73,7 @@ export const ContactForm = ({
setShowResultForm,
}: ContactFormProps) => {
const theme = useTheme();
- const { settings, items } = useQuestionsStore();
+ const { settings, questions } = useQuizData();
const [ready, setReady] = useState(false);
const [name, setName] = useState("");
@@ -93,7 +92,7 @@ export const ContactForm = ({
};
//@ts-ignore
- const resultQuestion: QuizQuestionResult = items.find((question) => {
+ const resultQuestion: QuizQuestionResult = questions.find((question) => {
if (settings?.cfg.haveRoot) {
//ветвимся
return (
@@ -110,8 +109,6 @@ export const ContactForm = ({
});
const inputHC = async () => {
- if (!settings) return;
-
//@ts-ignore
const FC = settings?.cfg.formContact.fields || settings?.cfg.formContact;
const body = {};
@@ -157,7 +154,6 @@ export const ContactForm = ({
}
let isWide = Object.keys(filteredFC).length > 2;
- if (!settings) throw new Error("settings is null");
if (!resultQuestion)
return (
@@ -360,15 +356,13 @@ export const ContactForm = ({
@@ -393,7 +387,7 @@ const Inputs = ({
adress,
setAdress,
}: any) => {
- const { settings } = useQuestionsStore();
+ const { settings } = useQuizData();
// @ts-ignore
const FC = settings?.cfg.formContact.fields || settings?.cfg.formContact;
diff --git a/src/pages/ViewPublicationPage/Footer.tsx b/src/pages/ViewPublicationPage/Footer.tsx
index 9dd9f3c..34866ca 100644
--- a/src/pages/ViewPublicationPage/Footer.tsx
+++ b/src/pages/ViewPublicationPage/Footer.tsx
@@ -1,16 +1,14 @@
import { Box, Button, Typography, useMediaQuery, useTheme } from "@mui/material";
import { useCallback, useMemo, useState } from "react";
-import { getQuestionById } from "@stores/quizData/actions";
-
import { enqueueSnackbar } from "notistack";
import type { AnyTypedQuizQuestion, QuizQuestionBase } from "../../model/questionTypes/shared";
import { checkEmptyData } from "./tools/checkEmptyData";
import type { QuizQuestionResult } from "@model/questionTypes/result";
-import { useQuestionsStore } from "@stores/quizData/store";
import { useQuizViewStore } from "@stores/quizView/store";
+import { useQuizData } from "@utils/hooks/useQuizData";
type FooterProps = {
setCurrentQuestion: (step: AnyTypedQuizQuestion) => void;
@@ -22,13 +20,13 @@ type FooterProps = {
export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setShowResultForm }: FooterProps) => {
const theme = useTheme();
- const { settings, items } = useQuestionsStore();
+ const { settings, questions } = useQuizData();
const answers = useQuizViewStore(state => state.answers);
const [stepNumber, setStepNumber] = useState(1);
const isMobileMini = useMediaQuery(theme.breakpoints.down(382));
- const isLinear = !items.some(({ content }) => content.rule.parentId === "root");
+ const isLinear = !questions.some(({ content }) => content.rule.parentId === "root");
const getNextQuestionId = useCallback(() => {
console.log("Смотрим какой вопрос будет дальше. Что у нас сегодня вкусненького? Щя покажу от какого вопроса мы ищем следующий шаг");
@@ -86,27 +84,27 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
}
//ничё не нашли, ищем резулт
console.log("ничё не нашли, ищем резулт ");
- return items.find(q => {
+ return questions.find(q => {
console.log('q.type === "result"', q.type === "result");
console.log('q.content.rule.parentId', q.content.rule.parentId);
console.log('question.content.id', question.content.id);
return q.type === "result" && q.content.rule.parentId === question.content.id;
})?.id;
- }, [answers, items, question]);
+ }, [answers, questions, question]);
const isPreviousButtonDisabled = useMemo(() => {
// Логика для аргумента disabled у кнопки "Назад"
if (isLinear) {
- const questionIndex = items.findIndex(({ id }) => id === question.id);
+ const questionIndex = questions.findIndex(({ id }) => id === question.id);
- const previousQuestion = items[questionIndex - 1];
+ const previousQuestion = questions[questionIndex - 1];
return previousQuestion ? false : true;
} else {
return question?.content.rule.parentId === "root" ? true : false;
}
- }, [items, isLinear, question?.content.rule.parentId, question.id]);
+ }, [questions, isLinear, question?.content.rule.parentId, question.id]);
const isNextButtonDisabled = useMemo(() => {
// Логика для аргумента disabled у кнопки "Далее"
@@ -128,7 +126,8 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
if (nextQuestionId) {
return false;
} else {
- const nextQuestion = getQuestionById(question.content.rule.default);
+ const questionId = question.content.rule.default;
+ const nextQuestion = questions.find(q => q.id === questionId || q.content.id === questionId) || null;
if (nextQuestion?.type) {
return false;
@@ -137,7 +136,6 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
}, [answers, getNextQuestionId, isLinear, question.content, question.id]);
const showResult = (nextQuestion: QuizQuestionResult) => {
- if (!settings) return;
if (!nextQuestion) return;
const isEmpty = checkEmptyData({ resultData: nextQuestion });
@@ -168,9 +166,9 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
if (isLinear) {
setStepNumber(q => q - 1);
- const questionIndex = items.findIndex(({ id }) => id === question.id);
+ const questionIndex = questions.findIndex(({ id }) => id === question.id);
- const previousQuestion = items[questionIndex - 1];
+ const previousQuestion = questions[questionIndex - 1];
if (previousQuestion) {
setCurrentQuestion(previousQuestion);
@@ -180,7 +178,8 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
}
if (question?.content.rule.parentId !== "root") {
- const parent = getQuestionById(question?.content.rule.parentId);
+ const questionId = question?.content.rule.parentId;
+ const parent = questions.find(q => q.id === questionId || q.content.id === questionId) || null;
if (parent?.type) {
setCurrentQuestion(parent);
} else {
@@ -195,14 +194,14 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
if (isLinear) {
setStepNumber(q => q + 1);
- const questionIndex = items.findIndex(({ id }) => id === question.id);
- const nextQuestion = items[questionIndex + 1];
+ const questionIndex = questions.findIndex(({ id }) => id === question.id);
+ const nextQuestion = questions[questionIndex + 1];
if (nextQuestion && nextQuestion.type !== "result") {
setCurrentQuestion(nextQuestion);
} else {
//@ts-ignore
- showResult(items.find(q => q.content.rule.parentId === "line"));
+ showResult(questions.find(q => q.content.rule.parentId === "line"));
}
return;
@@ -211,7 +210,7 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
const nextQuestionId = getNextQuestionId();
if (nextQuestionId) {
- const nextQuestion = getQuestionById(nextQuestionId);
+ const nextQuestion = questions.find(q => q.id === nextQuestionId || q.content.id === nextQuestionId) || null;
if (nextQuestion?.type && nextQuestion.type === "result") {
showResult(nextQuestion);
@@ -278,7 +277,7 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
Из
- {items.filter(q => q.type !== "result").length}
+ {questions.filter(q => q.type !== "result").length}
}
diff --git a/src/pages/ViewPublicationPage/Question.tsx b/src/pages/ViewPublicationPage/Question.tsx
index 03f43ac..d3fe759 100644
--- a/src/pages/ViewPublicationPage/Question.tsx
+++ b/src/pages/ViewPublicationPage/Question.tsx
@@ -1,8 +1,6 @@
import { Box, useMediaQuery, useTheme } from "@mui/material";
import { useEffect, useState } from "react";
-import { getQuestionById } from "@stores/quizData/actions";
-
import { ContactForm } from "./ContactForm";
import { Footer } from "./Footer";
import { ResultForm } from "./ResultForm";
@@ -23,23 +21,22 @@ import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
import { NameplateLogoFQ } from "@icons/NameplateLogoFQ";
import { NameplateLogoFQDark } from "@icons/NameplateLogoFQDark";
import { QuizQuestionResult } from "@model/questionTypes/result";
-import { useQuestionsStore } from "@stores/quizData/store";
import { notReachable } from "@utils/notReachable";
import { quizThemes } from "@utils/themes/Publication/themePublication";
+import { useQuizData } from "@utils/hooks/useQuizData";
export const Question = () => {
const theme = useTheme();
- const settings = useQuestionsStore(state => state.settings);
- const questions = useQuestionsStore(state => state.items);
+ const { settings, questions } = useQuizData();
const [currentQuestion, setCurrentQuestion] = useState();
const [showContactForm, setShowContactForm] = useState(false);
const [showResultForm, setShowResultForm] = useState(false);
const isMobile = useMediaQuery(theme.breakpoints.down(650));
useEffect(() => {
-
if (settings?.cfg.haveRoot) {//ветвимся
- const nextQuestion = getQuestionById(settings?.cfg.haveRoot || "");
+ const questionId = settings?.cfg.haveRoot || "";
+ const nextQuestion = questions.find(q => q.id === questionId || q.content.id === questionId) || null;
if (nextQuestion?.type) {
setCurrentQuestion(nextQuestion);
@@ -49,10 +46,8 @@ export const Question = () => {
} else {//идём прямо
setCurrentQuestion(questions[0]);
}
-
}, []);
- if (!settings) throw new Error("settings is null");
if (!currentQuestion || currentQuestion.type === "result") return "не смог отобразить вопрос";
return (
diff --git a/src/pages/ViewPublicationPage/ResultForm.tsx b/src/pages/ViewPublicationPage/ResultForm.tsx
index 6666bd4..335d9f3 100644
--- a/src/pages/ViewPublicationPage/ResultForm.tsx
+++ b/src/pages/ViewPublicationPage/ResultForm.tsx
@@ -9,10 +9,10 @@ import {
import { NameplateLogo } from "@icons/NameplateLogo";
import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe";
-import { useQuestionsStore } from "@stores/quizData/store";
import { quizThemes } from "@utils/themes/Publication/themePublication";
import { useCallback, useEffect, useMemo } from "react";
import type { QuizQuestionResult } from "../../model/questionTypes/result";
+import { useQuizData } from "@utils/hooks/useQuizData";
type ResultFormProps = {
@@ -29,34 +29,33 @@ export const ResultForm = ({
}: ResultFormProps) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(650));
- const { settings, items } = useQuestionsStore();
- if (!settings) throw new Error("settings is null");
+ const { settings, questions } = useQuizData();
const resultQuestion = useMemo(() => {
if (settings?.cfg.haveRoot) {
//ищём для ветвления
- return (items.find(
+ return (questions.find(
(question): question is QuizQuestionResult =>
question.type === "result" &&
question.content.rule.parentId === currentQuestion.content.id
- ) || items.find(
+ ) || questions.find(
(question): question is QuizQuestionResult =>
question.type === "result" &&
question.content.rule.parentId === "line"
));
} else {
- return items.find(
+ return questions.find(
(question): question is QuizQuestionResult =>
question.type === "result" &&
question.content.rule.parentId === "line"
);
}
- }, [currentQuestion.content.id, items, settings?.cfg.haveRoot]);
+ }, [currentQuestion.content.id, questions, settings?.cfg.haveRoot]);
const followNextForm = useCallback(() => {
setShowResultForm(false);
setShowContactForm(true);
- },[setShowContactForm, setShowResultForm]);
+ }, [setShowContactForm, setShowResultForm]);
useEffect(() => {
if (!resultQuestion) {
diff --git a/src/pages/ViewPublicationPage/StartPageViewPublication.tsx b/src/pages/ViewPublicationPage/StartPageViewPublication.tsx
index 9546093..b80dba8 100644
--- a/src/pages/ViewPublicationPage/StartPageViewPublication.tsx
+++ b/src/pages/ViewPublicationPage/StartPageViewPublication.tsx
@@ -1,12 +1,12 @@
import { Box, Button, ButtonBase, Link, Paper, Typography, useMediaQuery, useTheme } from "@mui/material";
-import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe";
-import { notReachable } from "../../utils/notReachable";
import { useUADevice } from "../../utils/hooks/useUADevice";
+import { notReachable } from "../../utils/notReachable";
+import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe";
import { NameplateLogo } from "@icons/NameplateLogo";
-import { quizThemes } from "@utils/themes/Publication/themePublication";
import { QuizStartpageAlignType, QuizStartpageType } from "@model/settingsData";
-import { useQuestionsStore } from "@stores/quizData/store";
+import { useQuizData } from "@utils/hooks/useQuizData";
+import { quizThemes } from "@utils/themes/Publication/themePublication";
interface Props {
@@ -15,12 +15,10 @@ interface Props {
export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
const theme = useTheme();
- const { settings } = useQuestionsStore();
+ const { settings } = useQuizData();
const { isMobileDevice } = useUADevice();
const isMobile = useMediaQuery(theme.breakpoints.down(650));
- if (!settings) throw new Error("settings is null");
-
const handleCopyNumber = () => {
navigator.clipboard.writeText(settings.cfg.info.phonenumber);
};
diff --git a/src/pages/ViewPublicationPage/ViewPublicationPage.tsx b/src/pages/ViewPublicationPage/ViewPublicationPage.tsx
index 16ba51e..d2a9a0c 100644
--- a/src/pages/ViewPublicationPage/ViewPublicationPage.tsx
+++ b/src/pages/ViewPublicationPage/ViewPublicationPage.tsx
@@ -1,55 +1,33 @@
-import { getData } from "@api/quizRelase";
-import { QuizSettings } from "@model/settingsData";
import { Box, ThemeProvider } from "@mui/material";
-import { setQuizData } from "@stores/quizData/actions";
-import { useQuestionsStore } from "@stores/quizData/store";
-import LoadingSkeleton from "@ui_kit/LoadingSkeleton";
+import { useQuizData } from "@utils/hooks/useQuizData";
import { quizThemes } from "@utils/themes/Publication/themePublication";
-import { enqueueSnackbar } from "notistack";
import { useEffect, useState } from "react";
-import useSWR from "swr";
import { ApologyPage } from "./ApologyPage";
import { Question } from "./Question";
import { StartPageViewPublication } from "./StartPageViewPublication";
-import { parseQuizData } from "@model/api/getQuizData";
-import { replaceSpacesToEmptyLines } from "./tools/replaceSpacesToEmptyLines";
-import { useQuizId } from "../../contexts/QuizIdContext";
-
export const ViewPage = () => {
- const quizId = useQuizId();
- const { isLoading, error } = useSWR(["quizData", quizId], params => getQuizData(params[1]), {
- onSuccess: setQuizData,
- });
- const { settings, items, recentlyСompleted } = useQuestionsStore();
+ const { settings, questions, recentlyCompleted } = useQuizData();
const [visualStartPage, setVisualStartPage] = useState();
- useEffect(() => {//установка фавиконки
- if (!settings) return;
-
+ useEffect(() => {
const link = document.querySelector('link[rel="icon"]');
if (link && settings.cfg.startpage.favIcon) {
link.setAttribute("href", settings?.cfg.startpage.favIcon);
}
- //установка заголовка страницы
+
document.title = settings.name;
setVisualStartPage(!settings.cfg.noStartPage);
}, [settings]);
- const questionsCount = items.filter(({ type }) => type !== null && type !== "result").length;
-
- if (error) {
- console.log(error);
- return ;
- }
- if (isLoading || !settings) return ;
+ const questionsCount = questions.filter(({ type }) => type !== null && type !== "result").length;
if (questionsCount === 0) return ;
return (
- {recentlyСompleted ? (
+ {recentlyCompleted ? (
) : (
@@ -62,21 +40,4 @@ export const ViewPage = () => {
)}
);
-};
-
-async function getQuizData(quizId: string) {
- const response = await getData(quizId);
- const quizDataResponse = response.data;
-
- if (response.error) {
- enqueueSnackbar(response.error);
- throw new Error(response.error);
- }
- if (!quizDataResponse) {
- throw new Error("Quiz not found");
- }
-
- const quizSettings = replaceSpacesToEmptyLines(parseQuizData(quizDataResponse, quizId));
-
- return JSON.parse(JSON.stringify({ data: quizSettings }).replaceAll(/\\" \\"/g, '""').replaceAll(/" "/g, '""')).data as QuizSettings & { recentlyСompleted: boolean; };
-}
+};
diff --git a/src/pages/ViewPublicationPage/questions/Date.tsx b/src/pages/ViewPublicationPage/questions/Date.tsx
index 6f80cd2..a85f769 100644
--- a/src/pages/ViewPublicationPage/questions/Date.tsx
+++ b/src/pages/ViewPublicationPage/questions/Date.tsx
@@ -8,103 +8,101 @@ import type { QuizQuestionDate } from "../../../model/questionTypes/date";
import CalendarIcon from "@icons/CalendarIcon";
import { enqueueSnackbar } from "notistack";
import { sendAnswer } from "@api/quizRelase";
-
+
import { quizThemes } from "@utils/themes/Publication/themePublication";
-import { useQuestionsStore } from "@stores/quizData/store";
+import { useQuizData } from "@utils/hooks/useQuizData";
type DateProps = {
- currentQuestion: QuizQuestionDate;
+ currentQuestion: QuizQuestionDate;
};
export const Date = ({ currentQuestion }: DateProps) => {
- const theme = useTheme();
+ const theme = useTheme();
- const { settings } = useQuestionsStore();
- const { answers } = useQuizViewStore();
- const answer = answers.find(
- ({ questionId }) => questionId === currentQuestion.id
- )?.answer as string;
- const currentAnswer = moment(answer) || moment();
-
- if (!settings) throw new Error("settings is null");
+ const { settings } = useQuizData();
+ const { answers } = useQuizViewStore();
+ const answer = answers.find(
+ ({ questionId }) => questionId === currentQuestion.id
+ )?.answer as string;
+ const currentAnswer = moment(answer) || moment();
- return (
-
-
- {currentQuestion.title}
-
-
- (
-
+
+ {currentQuestion.title}
+
+
- ),
- }}
- value={ currentAnswer }
- onChange={async (date) => {
- console.log(date)
- if (!date) {
- return;
- }
+ >
+ (
+
+ ),
+ }}
+ value={currentAnswer}
+ onChange={async (date) => {
+ console.log(date);
+ if (!date) {
+ return;
+ }
- try {
- await sendAnswer({
- questionId: currentQuestion.id,
- body: moment(date).format("YYYY.MM.DD"),
- qid: settings.qid,
- });
+ try {
+ await sendAnswer({
+ questionId: currentQuestion.id,
+ body: moment(date).format("YYYY.MM.DD"),
+ qid: settings.qid,
+ });
- updateAnswer(
- currentQuestion.id,
- date
- );
- } catch (e) {
- enqueueSnackbar("ответ не был засчитан");
- }
- }}
- slotProps={{
- openPickerButton: {
- sx: {
- p: 0,
- },
- "data-cy": "open-datepicker",
- },
- layout: {
- sx: { backgroundColor: theme.palette.background.default },
- },
- }}
- sx={{
- "& .MuiInputBase-root": {
- backgroundColor: quizThemes[settings.cfg.theme].isLight
- ? "white"
- : theme.palette.background.default,
- borderRadius: "10px",
- maxWidth: "250px",
- pr: "22px",
- "& input": {
- py: "11px",
- pl: "20px",
- lineHeight: "19px",
- },
- "& fieldset": {
- borderColor: "#9A9AAF",
- },
- },
- }}
- />
-
-
- );
+ updateAnswer(
+ currentQuestion.id,
+ date
+ );
+ } catch (e) {
+ enqueueSnackbar("ответ не был засчитан");
+ }
+ }}
+ slotProps={{
+ openPickerButton: {
+ sx: {
+ p: 0,
+ },
+ "data-cy": "open-datepicker",
+ },
+ layout: {
+ sx: { backgroundColor: theme.palette.background.default },
+ },
+ }}
+ sx={{
+ "& .MuiInputBase-root": {
+ backgroundColor: quizThemes[settings.cfg.theme].isLight
+ ? "white"
+ : theme.palette.background.default,
+ borderRadius: "10px",
+ maxWidth: "250px",
+ pr: "22px",
+ "& input": {
+ py: "11px",
+ pl: "20px",
+ lineHeight: "19px",
+ },
+ "& fieldset": {
+ borderColor: "#9A9AAF",
+ },
+ },
+ }}
+ />
+
+
+ );
};
diff --git a/src/pages/ViewPublicationPage/questions/Emoji.tsx b/src/pages/ViewPublicationPage/questions/Emoji.tsx
index dd8f12b..b6ba3eb 100644
--- a/src/pages/ViewPublicationPage/questions/Emoji.tsx
+++ b/src/pages/ViewPublicationPage/questions/Emoji.tsx
@@ -14,148 +14,146 @@ import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
import { sendAnswer } from "@api/quizRelase";
-
+
import { enqueueSnackbar } from "notistack";
import type { QuizQuestionEmoji } from "../../../model/questionTypes/emoji";
-import { useQuestionsStore } from "@stores/quizData/store";
+import { useQuizData } from "@utils/hooks/useQuizData";
type EmojiProps = {
- currentQuestion: QuizQuestionEmoji;
+ currentQuestion: QuizQuestionEmoji;
};
export const Emoji = ({ currentQuestion }: EmojiProps) => {
- const theme = useTheme();
-
- const { settings } = useQuestionsStore()
- const { answers } = useQuizViewStore();
- const { answer } =
- answers.find(
- ({ questionId }) => questionId === currentQuestion.id
- ) ?? {};
+ const theme = useTheme();
- if (!settings) throw new Error("settings is null");
+ const { settings } = useQuizData();
+ const { answers } = useQuizViewStore();
+ const { answer } =
+ answers.find(
+ ({ questionId }) => questionId === currentQuestion.id
+ ) ?? {};
return (
-
- {currentQuestion.title}
- answer === id
- )}
- onChange={({ target }) =>{
- updateAnswer(
- currentQuestion.id,
- currentQuestion.content.variants[Number(target.value)].answer
- )
- }
- }
- sx={{
- display: "flex",
- flexWrap: "wrap",
- flexDirection: "row",
- justifyContent: "space-between",
- marginTop: "20px",
- }}
- >
-
- {currentQuestion.content.variants.map((variant, index) => (
-
-
-
- {variant.extendedText && (
-
- {variant.extendedText}
-
- )}
-
-
- {
- event.preventDefault();
-
- try {
-
- await sendAnswer({
- questionId: currentQuestion.id,
- body: currentQuestion.content.variants[index].extendedText + " " + currentQuestion.content.variants[index].answer,
- qid: settings.qid
- })
-
- updateAnswer(
+
+ {currentQuestion.title}
+ answer === id
+ )}
+ onChange={({ target }) => {
+ updateAnswer(
currentQuestion.id,
- currentQuestion.content.variants[index].id
- );
-
- } catch (e) {
- enqueueSnackbar("ответ не был засчитан")
- }
-
-
-
- if (answer === currentQuestion.content.variants[index].id) {
- deleteAnswer(currentQuestion.id);
- try {
-
- await sendAnswer({
- questionId: currentQuestion.id,
- body: "",
- qid: settings.qid
- })
-
- } catch (e) {
- enqueueSnackbar("ответ не был засчитан")
- }
- }
- }}
-
- control={
- } icon={} />
- }
- label={
-
- {variant.answer}
-
- }
- />
-
- ))}
-
-
-
+ currentQuestion.content.variants[Number(target.value)].answer
+ );
+ }
+ }
+ sx={{
+ display: "flex",
+ flexWrap: "wrap",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ marginTop: "20px",
+ }}
+ >
+
+ {currentQuestion.content.variants.map((variant, index) => (
+
+
+
+ {variant.extendedText && (
+
+ {variant.extendedText}
+
+ )}
+
+
+ {
+ event.preventDefault();
+
+ try {
+
+ await sendAnswer({
+ questionId: currentQuestion.id,
+ body: currentQuestion.content.variants[index].extendedText + " " + currentQuestion.content.variants[index].answer,
+ qid: settings.qid
+ });
+
+ updateAnswer(
+ currentQuestion.id,
+ currentQuestion.content.variants[index].id
+ );
+
+ } catch (e) {
+ enqueueSnackbar("ответ не был засчитан");
+ }
+
+
+
+ if (answer === currentQuestion.content.variants[index].id) {
+ deleteAnswer(currentQuestion.id);
+ try {
+
+ await sendAnswer({
+ questionId: currentQuestion.id,
+ body: "",
+ qid: settings.qid
+ });
+
+ } catch (e) {
+ enqueueSnackbar("ответ не был засчитан");
+ }
+ }
+ }}
+
+ control={
+ } icon={} />
+ }
+ label={
+
+ {variant.answer}
+
+ }
+ />
+
+ ))}
+
+
+
);
-
+
};
diff --git a/src/pages/ViewPublicationPage/questions/File.tsx b/src/pages/ViewPublicationPage/questions/File.tsx
index cc4a53d..5336533 100644
--- a/src/pages/ViewPublicationPage/questions/File.tsx
+++ b/src/pages/ViewPublicationPage/questions/File.tsx
@@ -1,12 +1,11 @@
import {
- Box,
- Typography,
- ButtonBase,
- useTheme,
- IconButton, useMediaQuery, Modal,
+ Box,
+ Typography,
+ ButtonBase,
+ useTheme,
+ IconButton, useMediaQuery, Modal,
} from "@mui/material";
import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
-import { UPLOAD_FILE_TYPES_MAP } from "../tools/File";
import UploadIcon from "@icons/UploadIcon";
import CloseBold from "@icons/CloseBold";
@@ -17,328 +16,326 @@ import type { DragEvent } from "react";
import type { UploadFileType } from "@model/questionTypes/file";
import { enqueueSnackbar } from "notistack";
import { sendAnswer, sendFile } from "@api/quizRelase";
-import { useQuestionsStore } from "@stores/quizData/store"
import Info from "@icons/Info";
+import { useQuizData } from "@utils/hooks/useQuizData";
type FileProps = {
- currentQuestion: QuizQuestionFile;
+ currentQuestion: QuizQuestionFile;
};
-const CurrentModal = ({ status }: { status: "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | "" }) => {
- switch (status) {
- case 'errorType':
- return (<>
- Выбран некорректный тип файла
- >)
- case 'errorSize':
- return (<>
- Файл слишком большой. Максимальный размер 50 МБ
- >)
- default:
- return (<>
- Допустимые расширения файлов:
- {
- //@ts-ignore
- ACCEPT_SEND_FILE_TYPES_MAP[status].join(" ")}
- >)
- }
-}
+const CurrentModal = ({ status }: { status: "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | ""; }) => {
+ switch (status) {
+ case 'errorType':
+ return (<>
+ Выбран некорректный тип файла
+ >);
+ case 'errorSize':
+ return (<>
+ Файл слишком большой. Максимальный размер 50 МБ
+ >);
+ default:
+ return (<>
+ Допустимые расширения файлов:
+ {
+ //@ts-ignore
+ ACCEPT_SEND_FILE_TYPES_MAP[status].join(" ")}
+ >);
+ }
+};
const ACCEPT_SEND_FILE_TYPES_MAP = {
- picture: [
- ".jpeg",
- ".jpg",
- ".png",
- ".ico",
- ".gif",
- ".tiff",
- ".webp",
- ".eps",
- ".svg"
- ],
- video: [
- ".mp4",
- ".mov",
- ".wmv",
- ".avi",
- ".avchd",
- ".flv",
- ".f4v",
- ".swf",
- ".mkv",
- ".webm",
- ".mpeg-2"
- ],
- audio: [
- ".aac",
- ".aiff",
- ".dsd",
- ".flac",
- ".mp3",
- ".mqa",
- ".ogg",
- ".wav",
- ".wma"
- ],
- document: [
- ".doc",
- ".docx",
- ".dotx",
- ".rtf",
- ".odt",
- ".pdf",
- ".txt",
- ".xls",
- ".ppt",
- ".xlsx",
- ".pptx",
- ".pages",
- ],
+ picture: [
+ ".jpeg",
+ ".jpg",
+ ".png",
+ ".ico",
+ ".gif",
+ ".tiff",
+ ".webp",
+ ".eps",
+ ".svg"
+ ],
+ video: [
+ ".mp4",
+ ".mov",
+ ".wmv",
+ ".avi",
+ ".avchd",
+ ".flv",
+ ".f4v",
+ ".swf",
+ ".mkv",
+ ".webm",
+ ".mpeg-2"
+ ],
+ audio: [
+ ".aac",
+ ".aiff",
+ ".dsd",
+ ".flac",
+ ".mp3",
+ ".mqa",
+ ".ogg",
+ ".wav",
+ ".wma"
+ ],
+ document: [
+ ".doc",
+ ".docx",
+ ".dotx",
+ ".rtf",
+ ".odt",
+ ".pdf",
+ ".txt",
+ ".xls",
+ ".ppt",
+ ".xlsx",
+ ".pptx",
+ ".pages",
+ ],
-}
+};
const UPLOAD_FILE_DESCRIPTIONS_MAP: Record<
- UploadFileType,
- { title: string; description: string }
+ UploadFileType,
+ { title: string; description: string; }
> = {
- picture: {
- title: "Добавить изображение",
- description: "Принимает изображения",
- },
- video: {
- title: "Добавить видео",
- description: "Принимает .mp4 и .mov формат — максимум 100мб",
- },
- audio: { title: "Добавить аудиофайл", description: "Принимает аудиофайлы" },
- document: { title: "Добавить документ", description: "Принимает документы" },
+ picture: {
+ title: "Добавить изображение",
+ description: "Принимает изображения",
+ },
+ video: {
+ title: "Добавить видео",
+ description: "Принимает .mp4 и .mov формат — максимум 100мб",
+ },
+ audio: { title: "Добавить аудиофайл", description: "Принимает аудиофайлы" },
+ document: { title: "Добавить документ", description: "Принимает документы" },
} as const;
export const File = ({ currentQuestion }: FileProps) => {
- const theme = useTheme();
- const { settings } = useQuestionsStore()
- const { answers } = useQuizViewStore();
+ const theme = useTheme();
+ const { settings } = useQuizData();
+ const { answers } = useQuizViewStore();
- const [statusModal, setStatusModal] = useState<"errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | "">("")
+ const [statusModal, setStatusModal] = useState<"errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | "">("");
- const answer = answers.find(
- ({ questionId }) => questionId === currentQuestion.id
- )?.answer as string;
- const isMobile = useMediaQuery(theme.breakpoints.down(500));
- const uploadFile = async ({ target }: ChangeEvent) => {
- if (!settings) return;
-
- const file = target.files?.[0];
- if (file) {
- if (file.size <= 52428800) {
- //проверяем на соответствие
- console.log(file.name.toLowerCase())
- if (ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].find((ednding => {
- console.log(ednding)
- console.log(file.name.toLowerCase().endsWith(ednding))
- return file.name.toLowerCase().endsWith(ednding)
- }))) {
+ const answer = answers.find(
+ ({ questionId }) => questionId === currentQuestion.id
+ )?.answer as string;
+ const isMobile = useMediaQuery(theme.breakpoints.down(500));
+ const uploadFile = async ({ target }: ChangeEvent) => {
+ const file = target.files?.[0];
+ if (file) {
+ if (file.size <= 52428800) {
+ //проверяем на соответствие
+ console.log(file.name.toLowerCase());
+ if (ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].find((ednding => {
+ console.log(ednding);
+ console.log(file.name.toLowerCase().endsWith(ednding));
+ return file.name.toLowerCase().endsWith(ednding);
+ }))) {
- //Нужный формат
- console.log(file)
- try {
+ //Нужный формат
+ console.log(file);
+ try {
- const data = await sendFile({
- questionId: currentQuestion.id,
- body: {
- file: file,
- name: file.name
- },
- qid: settings.qid
- })
- console.log(data)
+ const data = await sendFile({
+ questionId: currentQuestion.id,
+ body: {
+ file: file,
+ name: file.name
+ },
+ qid: settings.qid
+ });
+ console.log(data);
- await sendAnswer({
- questionId: currentQuestion.id,
- //@ts-ignore
- body: `https://storage.yandexcloud.net/squizanswer/${settings.qid}/${currentQuestion.id}/${data.data.fileIDMap[currentQuestion.id]}`,
- //@ts-ignore
- qid: settings.qid
- })
+ await sendAnswer({
+ questionId: currentQuestion.id,
+ //@ts-ignore
+ body: `https://storage.yandexcloud.net/squizanswer/${settings.qid}/${currentQuestion.id}/${data.data.fileIDMap[currentQuestion.id]}`,
+ //@ts-ignore
+ qid: settings.qid
+ });
- updateAnswer(
- currentQuestion.id,
- `${file.name}|${URL.createObjectURL(file)}`
- );
+ updateAnswer(
+ currentQuestion.id,
+ `${file.name}|${URL.createObjectURL(file)}`
+ );
- } catch (e) {
- console.log(e)
- enqueueSnackbar("ответ не был засчитан")
- }
+ } catch (e) {
+ console.log(e);
+ enqueueSnackbar("ответ не был засчитан");
+ }
- } else {
+ } else {
+
+ //неподходящий формат
+ setStatusModal("errorType");
+ }
+ } else {
+
+ setStatusModal("errorSize");
+ }
- //неподходящий формат
- setStatusModal("errorType")
}
- } else {
+ };
- setStatusModal("errorSize")
- }
-
- }
- };
-
- return (
- <>
-
- {currentQuestion.title}
-
- {answer?.split("|")[0] && (
-
- Вы загрузили:
-
-
- {answer?.split("|")[0]}
- {
- updateAnswer(currentQuestion.id, "");
- }}
- >
-
-
-
-
- )}
-
- {!answer?.split("|")[0] && (
-
-
-
+ return (
+ <>
+
+ {currentQuestion.title}
) =>
- event.preventDefault()
- }
- sx={{
- width: "100%",
- height: isMobile ? undefined : "120px",
- display: "flex",
- gap: "50px",
- justifyContent: "flex-start",
- alignItems: "center",
- padding: "33px 44px 33px 55px",
- backgroundColor: theme.palette.background.default,
- border: `1px solid #9A9AAF`,
- // border: `1px solid ${theme.palette.grey2.main}`,
- borderRadius: "8px",
- }}
+ sx={{
+ display: "flex",
+ flexDirection: "column",
+ width: "100%",
+ marginTop: "20px",
+ maxWidth: answer?.split("|")[0] ? "640px" : "600px",
+ }}
>
-
-
-
- {
- UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type]
- .title
- }
-
-
- {
- UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type]
- .description
- }
-
-
+ {answer?.split("|")[0] && (
+
+ Вы загрузили:
+
+
+ {answer?.split("|")[0]}
+ {
+ updateAnswer(currentQuestion.id, "");
+ }}
+ >
+
+
+
+
+ )}
+
+ {!answer?.split("|")[0] && (
+
+
+
+ ) =>
+ event.preventDefault()
+ }
+ sx={{
+ width: "100%",
+ height: isMobile ? undefined : "120px",
+ display: "flex",
+ gap: "50px",
+ justifyContent: "flex-start",
+ alignItems: "center",
+ padding: "33px 44px 33px 55px",
+ backgroundColor: theme.palette.background.default,
+ border: `1px solid #9A9AAF`,
+ // border: `1px solid ${theme.palette.grey2.main}`,
+ borderRadius: "8px",
+ }}
+ >
+
+
+
+ {
+ UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type]
+ .title
+ }
+
+
+ {
+ UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type]
+ .description
+ }
+
+
+
+
+ setStatusModal(currentQuestion.content.type)} />
+
+ )}
+ {answer && currentQuestion.content.type === "picture" && (
+
+ )}
+ {answer && currentQuestion.content.type === "video" && (
+
+ )}
-
- setStatusModal(currentQuestion.content.type)} />
- )}
- {answer && currentQuestion.content.type === "picture" && (
-
- )}
- {answer && currentQuestion.content.type === "video" && (
-
- )}
-
-
- setStatusModal("")}
- >
-
-
-
-
- >
- );
+ setStatusModal("")}
+ >
+
+
+
+
+ >
+ );
};
diff --git a/src/pages/ViewPublicationPage/questions/Images.tsx b/src/pages/ViewPublicationPage/questions/Images.tsx
index cb7530a..6d0483c 100644
--- a/src/pages/ViewPublicationPage/questions/Images.tsx
+++ b/src/pages/ViewPublicationPage/questions/Images.tsx
@@ -1,11 +1,11 @@
import {
- Box,
- Typography,
- RadioGroup,
- FormControlLabel,
- Radio,
- useTheme,
- useMediaQuery,
+ Box,
+ Typography,
+ RadioGroup,
+ FormControlLabel,
+ Radio,
+ useTheme,
+ useMediaQuery,
} from "@mui/material";
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@stores/quizView/store";
@@ -15,138 +15,136 @@ import RadioIcon from "@ui_kit/RadioIcon";
import type { QuizQuestionImages } from "../../../model/questionTypes/images";
import { enqueueSnackbar } from "notistack";
import { sendAnswer } from "@api/quizRelase";
-import { useQuestionsStore } from "@stores/quizData/store"
+import { useQuizData } from "@utils/hooks/useQuizData";
type ImagesProps = {
- currentQuestion: QuizQuestionImages;
+ currentQuestion: QuizQuestionImages;
};
export const Images = ({ currentQuestion }: ImagesProps) => {
- const { settings } = useQuestionsStore()
- const { answers } = useQuizViewStore();
- const theme = useTheme();
- const { answer } =
- answers.find(
- ({ questionId }) => questionId === currentQuestion.id
- ) ?? {};
- const isTablet = useMediaQuery(theme.breakpoints.down(1000));
- const isMobile = useMediaQuery(theme.breakpoints.down(500));
+ const { settings } = useQuizData();
+ const { answers } = useQuizViewStore();
+ const theme = useTheme();
+ const { answer } =
+ answers.find(
+ ({ questionId }) => questionId === currentQuestion.id
+ ) ?? {};
+ const isTablet = useMediaQuery(theme.breakpoints.down(1000));
+ const isMobile = useMediaQuery(theme.breakpoints.down(500));
- if (!settings) throw new Error("settings is null");
-
- return (
-
- {currentQuestion.title}
- answer === id
- )}
- sx={{
- display: "flex",
- flexWrap: "wrap",
- flexDirection: "row",
- justifyContent: "space-between",
- marginTop: "20px",
- }}
- >
-
- {currentQuestion.content.variants.map((variant, index) => (
- {
- event.preventDefault();
-
- try {
-
- await sendAnswer({
- questionId: currentQuestion.id,
- body: `${currentQuestion.content.variants[index].answer}
`,
- qid: settings.qid
- })
-
- updateAnswer(
- currentQuestion.id,
- currentQuestion.content.variants[index].id
- );
-
- } catch (e) {
- enqueueSnackbar("ответ не был засчитан")
- }
-
-
- if (answer === currentQuestion.content.variants[index].id) {
- deleteAnswer(currentQuestion.id);
- try {
-
- await sendAnswer({
- questionId: currentQuestion.id,
- body: "",
- qid: settings.qid
- })
-
- } catch (e) {
- enqueueSnackbar("ответ не был засчитан")
- }
- }
- }}
- >
-
-
- {variant.extendedText && (
-
- )}
-
-
-
+ {currentQuestion.title}
+ answer === id
+ )}
sx={{
- textAlign: "center",
- color: theme.palette.text.primary,
- marginTop: "10px",
- marginLeft: 0,
- padding: "10px",
- "& .MuiFormControlLabel-label": {
- wordBreak: "break-word",
- },
+ display: "flex",
+ flexWrap: "wrap",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ marginTop: "20px",
}}
- value={index}
- control={
- } icon={} />
- }
- label={variant.answer}
- />
-
- ))}
+ >
+
+ {currentQuestion.content.variants.map((variant, index) => (
+ {
+ event.preventDefault();
+
+ try {
+
+ await sendAnswer({
+ questionId: currentQuestion.id,
+ body: `${currentQuestion.content.variants[index].answer}
`,
+ qid: settings.qid
+ });
+
+ updateAnswer(
+ currentQuestion.id,
+ currentQuestion.content.variants[index].id
+ );
+
+ } catch (e) {
+ enqueueSnackbar("ответ не был засчитан");
+ }
+
+
+ if (answer === currentQuestion.content.variants[index].id) {
+ deleteAnswer(currentQuestion.id);
+ try {
+
+ await sendAnswer({
+ questionId: currentQuestion.id,
+ body: "",
+ qid: settings.qid
+ });
+
+ } catch (e) {
+ enqueueSnackbar("ответ не был засчитан");
+ }
+ }
+ }}
+ >
+
+
+ {variant.extendedText && (
+
+ )}
+
+
+ } icon={} />
+ }
+ label={variant.answer}
+ />
+
+ ))}
+
+
-
-
- );
+ );
};
diff --git a/src/pages/ViewPublicationPage/questions/Number.tsx b/src/pages/ViewPublicationPage/questions/Number.tsx
index e8d1025..1c1c920 100644
--- a/src/pages/ViewPublicationPage/questions/Number.tsx
+++ b/src/pages/ViewPublicationPage/questions/Number.tsx
@@ -12,14 +12,14 @@ import { enqueueSnackbar } from "notistack";
import { sendAnswer } from "@api/quizRelase";
import { quizThemes } from "@utils/themes/Publication/themePublication";
-import { useQuestionsStore } from "@stores/quizData/store";
+import { useQuizData } from "@utils/hooks/useQuizData";
type NumberProps = {
currentQuestion: QuizQuestionNumber;
};
export const Number = ({ currentQuestion }: NumberProps) => {
- const { settings } = useQuestionsStore();
+ const { settings } = useQuizData();
const [inputValue, setInputValue] = useState("0");
const [minRange, setMinRange] = useState("0");
const [maxRange, setMaxRange] = useState("100000000000");
@@ -105,8 +105,6 @@ export const Number = ({ currentQuestion }: NumberProps) => {
}
}, []);
- if (!settings) throw new Error("settings is null");
-
return (
diff --git a/src/pages/ViewPublicationPage/questions/Rating.tsx b/src/pages/ViewPublicationPage/questions/Rating.tsx
index b22562b..bf89ab6 100644
--- a/src/pages/ViewPublicationPage/questions/Rating.tsx
+++ b/src/pages/ViewPublicationPage/questions/Rating.tsx
@@ -1,9 +1,9 @@
import {
- Box,
- Typography,
- Rating as RatingComponent,
- useTheme,
- useMediaQuery
+ Box,
+ Typography,
+ Rating as RatingComponent,
+ useTheme,
+ useMediaQuery
} from "@mui/material";
import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
@@ -19,127 +19,125 @@ import StarIconMini from "@icons/questionsPage/StarIconMini";
import type { QuizQuestionRating } from "../../../model/questionTypes/rating";
import { enqueueSnackbar } from "notistack";
import { sendAnswer } from "@api/quizRelase";
-import { useQuestionsStore } from "@stores/quizData/store";
+import { useQuizData } from "@utils/hooks/useQuizData";
type RatingProps = {
- currentQuestion: QuizQuestionRating;
+ currentQuestion: QuizQuestionRating;
};
const buttonRatingForm = [
- {
- name: "star",
- icon: (color: string) => ,
- },
- {
- name: "trophie",
- icon: (color: string) => ,
- },
- {
- name: "flag",
- icon: (color: string) => ,
- },
- {
- name: "heart",
- icon: (color: string) => ,
- },
- {
- name: "like",
- icon: (color: string) => ,
- },
- {
- name: "bubble",
- icon: (color: string) => ,
- },
- {
- name: "hashtag",
- icon: (color: string) => ,
- },
+ {
+ name: "star",
+ icon: (color: string) => ,
+ },
+ {
+ name: "trophie",
+ icon: (color: string) => ,
+ },
+ {
+ name: "flag",
+ icon: (color: string) => ,
+ },
+ {
+ name: "heart",
+ icon: (color: string) => ,
+ },
+ {
+ name: "like",
+ icon: (color: string) => ,
+ },
+ {
+ name: "bubble",
+ icon: (color: string) => ,
+ },
+ {
+ name: "hashtag",
+ icon: (color: string) => ,
+ },
];
export const Rating = ({ currentQuestion }: RatingProps) => {
- const { settings } = useQuestionsStore()
- const { answers } = useQuizViewStore();
- const theme = useTheme();
- const isMobile = useMediaQuery(theme.breakpoints.down(650));
- const { answer } =
- answers.find(
- ({ questionId }) => questionId === currentQuestion.id
- ) ?? {};
- const form = buttonRatingForm.find(
- ({ name }) => name === currentQuestion.content.form
- );
+ const { settings } = useQuizData();
+ const { answers } = useQuizViewStore();
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down(650));
+ const { answer } =
+ answers.find(
+ ({ questionId }) => questionId === currentQuestion.id
+ ) ?? {};
+ const form = buttonRatingForm.find(
+ ({ name }) => name === currentQuestion.content.form
+ );
- if (!settings) throw new Error("settings is null");
-
- return (
-
- {currentQuestion.title}
-
-
- {
+ return (
+
+ {currentQuestion.title}
+
+
+ {
- try {
+ try {
- await sendAnswer({
- questionId: currentQuestion.id,
- body: String(value) + " из " + currentQuestion.content.steps,
- qid: settings.qid
- })
+ await sendAnswer({
+ questionId: currentQuestion.id,
+ body: String(value) + " из " + currentQuestion.content.steps,
+ qid: settings.qid
+ });
- updateAnswer(currentQuestion.id, String(value))
+ updateAnswer(currentQuestion.id, String(value));
- } catch (e) {
- enqueueSnackbar("ответ не был засчитан")
- }
- }}
- sx={{
- height: "50px",
- gap: isMobile ? undefined : "15px",
- justifyContent: isMobile ? "space-between" : undefined,
- width: isMobile ? "100%" : undefined
- }}
- max={currentQuestion.content.steps}
- icon={form?.icon(theme.palette.primary.main)}
- emptyIcon={form?.icon("#9A9AAF")}
- />
+ } catch (e) {
+ enqueueSnackbar("ответ не был засчитан");
+ }
+ }}
+ sx={{
+ height: "50px",
+ gap: isMobile ? undefined : "15px",
+ justifyContent: isMobile ? "space-between" : undefined,
+ width: isMobile ? "100%" : undefined
+ }}
+ max={currentQuestion.content.steps}
+ icon={form?.icon(theme.palette.primary.main)}
+ emptyIcon={form?.icon("#9A9AAF")}
+ />
+
+
+
+ {currentQuestion.content.ratingNegativeDescription}
+
+
+ {currentQuestion.content.ratingPositiveDescription}
+
+
+
-
-
- {currentQuestion.content.ratingNegativeDescription}
-
-
- {currentQuestion.content.ratingPositiveDescription}
-
-
-
-
- );
+ );
};
diff --git a/src/pages/ViewPublicationPage/questions/Select.tsx b/src/pages/ViewPublicationPage/questions/Select.tsx
index f990c5b..14abd5b 100644
--- a/src/pages/ViewPublicationPage/questions/Select.tsx
+++ b/src/pages/ViewPublicationPage/questions/Select.tsx
@@ -7,75 +7,73 @@ import { useQuizViewStore, updateAnswer, deleteAnswer } from "@stores/quizView/s
import type { QuizQuestionSelect } from "../../../model/questionTypes/select";
import { enqueueSnackbar } from "notistack";
import { sendAnswer } from "@api/quizRelase";
+import { useQuizData } from "@utils/hooks/useQuizData";
-import { useQuestionsStore } from "@stores/quizData/store"
type SelectProps = {
- currentQuestion: QuizQuestionSelect;
+ currentQuestion: QuizQuestionSelect;
};
export const Select = ({ currentQuestion }: SelectProps) => {
- const theme = useTheme();
+ const theme = useTheme();
- const { settings } = useQuestionsStore()
- const { answers } = useQuizViewStore();
- const { answer } =
- answers.find(
- ({ questionId }) => questionId === currentQuestion.id
- ) ?? {};
+ const { settings } = useQuizData();
+ const { answers } = useQuizViewStore();
+ const { answer } =
+ answers.find(
+ ({ questionId }) => questionId === currentQuestion.id
+ ) ?? {};
- if (!settings) throw new Error("settings is null");
+ return (
+
+ {currentQuestion.title}
+
+ answer)}
+ colorMain={theme.palette.primary.main}
+ onChange={async (_, value) => {
+ if (value < 0) {
+ deleteAnswer(currentQuestion.id);
+ try {
- return (
-
- {currentQuestion.title}
-
- answer)}
- colorMain={theme.palette.primary.main}
- onChange={async(_, value) => {
- if (value < 0) {
- deleteAnswer(currentQuestion.id);
- try {
-
- await sendAnswer({
- questionId: currentQuestion.id,
- body: "",
- qid: settings.qid
- })
-
- } catch (e) {
- enqueueSnackbar("ответ не был засчитан")
- }
- return;
- }
+ await sendAnswer({
+ questionId: currentQuestion.id,
+ body: "",
+ qid: settings.qid
+ });
- try {
-
- await sendAnswer({
- questionId: currentQuestion.id,
- body: String(currentQuestion.content.variants[Number(value)].answer),
- qid: settings.qid
- })
+ } catch (e) {
+ enqueueSnackbar("ответ не был засчитан");
+ }
+ return;
+ }
- updateAnswer(currentQuestion.id, String(value));
+ try {
- } catch (e) {
- enqueueSnackbar("ответ не был засчитан")
- }
+ await sendAnswer({
+ questionId: currentQuestion.id,
+ body: String(currentQuestion.content.variants[Number(value)].answer),
+ qid: settings.qid
+ });
+
+ updateAnswer(currentQuestion.id, String(value));
+
+ } catch (e) {
+ enqueueSnackbar("ответ не был засчитан");
+ }
- }}
- />
-
-
- );
+ }}
+ />
+
+
+ );
};
diff --git a/src/pages/ViewPublicationPage/questions/Text.tsx b/src/pages/ViewPublicationPage/questions/Text.tsx
index 5609f07..e934418 100644
--- a/src/pages/ViewPublicationPage/questions/Text.tsx
+++ b/src/pages/ViewPublicationPage/questions/Text.tsx
@@ -6,63 +6,61 @@ import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
import type { QuizQuestionText } from "../../../model/questionTypes/text";
import { enqueueSnackbar } from "notistack";
-import { useQuestionsStore } from "@stores/quizData/store"
import { sendAnswer } from "@api/quizRelase";
import { useDebouncedCallback } from "use-debounce";
+import { useQuizData } from "@utils/hooks/useQuizData";
type TextProps = {
- currentQuestion: QuizQuestionText;
+ currentQuestion: QuizQuestionText;
};
export const Text = ({ currentQuestion }: TextProps) => {
- const theme = useTheme();
- const { settings } = useQuestionsStore()
- const { answers } = useQuizViewStore();
- const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
+ const theme = useTheme();
+ const { settings } = useQuizData();
+ const { answers } = useQuizViewStore();
+ const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
- const inputHC = useDebouncedCallback(async (text) => {
- if (!settings) return;
-
- try {
+ const inputHC = useDebouncedCallback(async (text) => {
+ try {
- await sendAnswer({
- questionId: currentQuestion.id,
- body: text,
- qid: settings.qid
- })
+ await sendAnswer({
+ questionId: currentQuestion.id,
+ body: text,
+ qid: settings.qid
+ });
- } catch (e) {
- enqueueSnackbar("ответ не был засчитан")
- }
- }, 400);
- return (
-
- {currentQuestion.title}
-
- {
- updateAnswer(currentQuestion.id, target.value)
- inputHC(target.value)
- }
- }
- sx={{
- "&:focus-visible": {
- borderColor: theme.palette.primary.main
- }
- }}
- />
-
-
- );
+ } catch (e) {
+ enqueueSnackbar("ответ не был засчитан");
+ }
+ }, 400);
+ return (
+
+ {currentQuestion.title}
+
+ {
+ updateAnswer(currentQuestion.id, target.value);
+ inputHC(target.value);
+ }
+ }
+ sx={{
+ "&:focus-visible": {
+ borderColor: theme.palette.primary.main
+ }
+ }}
+ />
+
+
+ );
};
diff --git a/src/pages/ViewPublicationPage/questions/Variant.tsx b/src/pages/ViewPublicationPage/questions/Variant.tsx
index a552c01..fee885a 100644
--- a/src/pages/ViewPublicationPage/questions/Variant.tsx
+++ b/src/pages/ViewPublicationPage/questions/Variant.tsx
@@ -29,7 +29,7 @@ import { quizThemes } from "@utils/themes/Publication/themePublication";
import { enqueueSnackbar } from "notistack";
import type { QuestionVariant } from "../../../model/questionTypes/shared";
import type { QuizQuestionVariant } from "../../../model/questionTypes/variant";
-import { useQuestionsStore } from "@stores/quizData/store";
+import { useQuizData } from "@utils/hooks/useQuizData";
const TextField = MuiTextField as unknown as FC;
@@ -135,11 +135,9 @@ const VariantItem = ({
index,
own = false,
}: VariantItemProps) => {
- const { settings } = useQuestionsStore()
+ const { settings } = useQuizData();
const theme = useTheme();
- if (!settings) throw new Error("settings is null");
-
return (
item !== variantId)
: [...currentAnswer, variantId],
qid: settings.qid
- })
+ });
updateAnswer(
currentQuestion.id,
@@ -205,7 +203,7 @@ const VariantItem = ({
);
} catch (e) {
- enqueueSnackbar("ответ не был засчитан")
+ enqueueSnackbar("ответ не был засчитан");
}
@@ -218,12 +216,12 @@ const VariantItem = ({
questionId: currentQuestion.id,
body: currentQuestion.content.variants[index].answer,
qid: settings.qid
- })
+ });
updateAnswer(currentQuestion.id, variantId);
} catch (e) {
- enqueueSnackbar("ответ не был засчитан")
+ enqueueSnackbar("ответ не был засчитан");
}
if (answer === variantId) {
@@ -233,10 +231,10 @@ const VariantItem = ({
questionId: currentQuestion.id,
body: "",
qid: settings.qid
- })
+ });
} catch (e) {
- enqueueSnackbar("ответ не был засчитан")
+ enqueueSnackbar("ответ не был засчитан");
}
deleteAnswer(currentQuestion.id);
}
diff --git a/src/pages/ViewPublicationPage/questions/Varimg.tsx b/src/pages/ViewPublicationPage/questions/Varimg.tsx
index 0a16837..ec8c79b 100644
--- a/src/pages/ViewPublicationPage/questions/Varimg.tsx
+++ b/src/pages/ViewPublicationPage/questions/Varimg.tsx
@@ -7,8 +7,6 @@ import {
useTheme,
useMediaQuery
} from "@mui/material";
-
-import { useQuestionsStore } from "@stores/quizData/store";
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@stores/quizView/store";
import RadioCheck from "@ui_kit/RadioCheck";
@@ -19,13 +17,14 @@ import { enqueueSnackbar } from "notistack";
import { sendAnswer } from "@api/quizRelase";
import { quizThemes } from "@utils/themes/Publication/themePublication";
import BlankImage from "@icons/BlankImage";
+import { useQuizData } from "@utils/hooks/useQuizData";
type VarimgProps = {
currentQuestion: QuizQuestionVarImg;
};
export const Varimg = ({ currentQuestion }: VarimgProps) => {
- const { settings } = useQuestionsStore();
+ const { settings } = useQuizData();
const { answers } = useQuizViewStore();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(650));
@@ -38,8 +37,6 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
({ id }) => answer === id
);
- if (!settings) throw new Error("settings is null");
-
return (
{currentQuestion.title}
diff --git a/src/pages/ViewPublicationPage/questions/gag.png b/src/pages/ViewPublicationPage/questions/gag.png
deleted file mode 100644
index 5bb379f..0000000
Binary files a/src/pages/ViewPublicationPage/questions/gag.png and /dev/null differ
diff --git a/src/pages/ViewPublicationPage/tools/File.tsx b/src/pages/ViewPublicationPage/tools/File.tsx
index cc57a0e..6598de6 100644
--- a/src/pages/ViewPublicationPage/tools/File.tsx
+++ b/src/pages/ViewPublicationPage/tools/File.tsx
@@ -2,62 +2,62 @@ import { ChangeEvent, useEffect, useRef, useState } from "react";
import { Box, Button, Typography } from "@mui/material";
import type {
- QuizQuestionFile,
- UploadFileType,
-} from "model/questionTypes/file";
+ QuizQuestionFile,
+ UploadFileType,
+} from "@model/questionTypes/file";
-export const UPLOAD_FILE_TYPES_MAP: Record = {
- picture: "image/*",
- video: "video/*",
- audio: "audio/*",
- document:
- ".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,.pdf",
+const UPLOAD_FILE_TYPES_MAP: Record = {
+ picture: "image/*",
+ video: "video/*",
+ audio: "audio/*",
+ document:
+ ".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,.pdf",
} as const;
interface Props {
- question: QuizQuestionFile;
+ question: QuizQuestionFile;
}
export default function File({ question }: Props) {
- const fileInputRef = useRef(null);
- const [file, setFile] = useState(null);
- const [acceptedType, setAcceptedType] = useState(
- UPLOAD_FILE_TYPES_MAP.picture
- );
+ const fileInputRef = useRef(null);
+ const [file, setFile] = useState(null);
+ const [acceptedType, setAcceptedType] = useState(
+ UPLOAD_FILE_TYPES_MAP.picture
+ );
- useEffect(() => {
- setAcceptedType(UPLOAD_FILE_TYPES_MAP[question.content.type]);
- }, [question.content.type]);
+ useEffect(() => {
+ setAcceptedType(UPLOAD_FILE_TYPES_MAP[question.content.type]);
+ }, [question.content.type]);
- function handleFileChange(event: ChangeEvent) {
- if (!event.target.files?.[0]) return setFile(null);
- setFile(event.target.files[0]);
- }
+ function handleFileChange(event: ChangeEvent) {
+ if (!event.target.files?.[0]) return setFile(null);
+ setFile(event.target.files[0]);
+ }
- return (
-
- {question.title}
-
- {file && Выбран файл: {file.name}}
-
- );
+ return (
+
+ {question.title}
+
+ {file && Выбран файл: {file.name}}
+
+ );
}
diff --git a/src/stores/quizData/actions.ts b/src/stores/quizData/actions.ts
deleted file mode 100644
index 17f826a..0000000
--- a/src/stores/quizData/actions.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
-import { useQuestionsStore } from "./store";
-import { QuizSettings } from "@model/settingsData";
-
-
-export const getQuestionById = (questionId: string | null): AnyTypedQuizQuestion | null => {
- if (questionId === null) return null;
-
- return useQuestionsStore.getState().items.find(q => q.id === questionId || q.content.id === questionId) || null;
-};
-
-export const setQuizData = (quizData: QuizSettings) => useQuestionsStore.setState(quizData);
diff --git a/src/stores/quizData/hooks.ts b/src/stores/quizData/hooks.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/src/stores/quizData/store.ts b/src/stores/quizData/store.ts
deleted file mode 100644
index 88f530b..0000000
--- a/src/stores/quizData/store.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { QuizSettings } from "@model/settingsData";
-import { create } from "zustand";
-import { devtools } from "zustand/middleware";
-
-
-type QuizDataStore = {
- settings: QuizSettings["settings"] | null;
- items: QuizSettings["items"];
- cnt: QuizSettings["cnt"];
- recentlyСompleted: boolean;
-};
-
-const initialState: QuizDataStore = {
- settings: null,
- items: [],
- cnt: 0,
- recentlyСompleted: false,
-};
-
-export const useQuestionsStore = create()(
- devtools(
- () => initialState,
- {
- name: "QuizDataStore",
- enabled: import.meta.env.DEV,
- trace: import.meta.env.DEV,
- }
- )
-);
diff --git a/src/utils/handleComponentError.ts b/src/utils/handleComponentError.ts
new file mode 100644
index 0000000..8d21fd7
--- /dev/null
+++ b/src/utils/handleComponentError.ts
@@ -0,0 +1,43 @@
+import { ErrorInfo } from "react";
+
+
+interface ComponentError {
+ timestamp: number;
+ message: string;
+ callStack: string | undefined;
+ componentStack: string | null | undefined;
+}
+
+export function handleComponentError(error: Error, info: ErrorInfo) {
+ const componentError: ComponentError = {
+ timestamp: Math.floor(Date.now() / 1000),
+ message: error.message,
+ callStack: error.stack,
+ componentStack: info.componentStack,
+ };
+
+ queueErrorRequest(componentError);
+}
+
+let errorsQueue: ComponentError[] = [];
+let timeoutId: ReturnType;
+
+function queueErrorRequest(error: ComponentError) {
+ errorsQueue.push(error);
+
+ clearTimeout(timeoutId);
+ timeoutId = setTimeout(() => {
+ sendErrorsToServer();
+ }, 1000);
+}
+
+async function sendErrorsToServer() {
+ // makeRequest({
+ // url: "",
+ // method: "POST",
+ // body: errorsQueue,
+ // useToken: true,
+ // });
+ console.log(`Fake-sending ${errorsQueue.length} errors to server`, errorsQueue);
+ errorsQueue = [];
+}
diff --git a/src/utils/hooks/useGetSettings.ts b/src/utils/hooks/useGetSettings.ts
deleted file mode 100644
index 822cd40..0000000
--- a/src/utils/hooks/useGetSettings.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { useEffect, useLayoutEffect, useRef, useState } from "react"
-import { useQuestionsStore } from "@stores/quizData/store";
-
-import { getData } from "@api/quizRelase"
-
-interface SettingsGetter {
- quizId: string
-}
-
-export function useGetSettings(quizId: string) {
-
- const [fetchState, setFetchState] = useState<"fetching" | "idle" | "all fetched">("idle")
-
- useEffect(() => {
- async function get() {
- const data = await getData(quizId)
- //@ts-ignore
- const settings = data.settings
- const parseData = {
- settings: {
- fp: settings.fp,
- rep: settings.rep,
- name: settings.name,
- cfg: JSON.parse(settings?.cfg),
- lim: settings.lim,
- due: settings.due,
- delay: settings.delay,
- pausable: settings.pausable
- },
- //@ts-ignore
- items: data.items.map((item) => {
- const content = JSON.parse(item.c)
- return {
- description: item.desc,
- id: item.id,
- page: item.p,
- required: item.req,
- title: item.title,
- type: item.typ,
- content
- }
- }),
- //@ts-ignore
- cnt: data.cnt
- }
- //@ts-ignore
- useQuestionsStore.setState(parseData)
- }
- get()
- // const controller = new AbortController()
-
-
- }, [])
- return
- // return
-}
diff --git a/src/utils/hooks/useQuizData.ts b/src/utils/hooks/useQuizData.ts
new file mode 100644
index 0000000..1e180c7
--- /dev/null
+++ b/src/utils/hooks/useQuizData.ts
@@ -0,0 +1,34 @@
+import { getData } from "@api/quizRelase";
+import { parseQuizData } from "@model/api/getQuizData";
+import { QuizSettings } from "@model/settingsData";
+import { enqueueSnackbar } from "notistack";
+import useSWR from "swr";
+import { useQuizId } from "../../contexts/QuizIdContext";
+import { replaceSpacesToEmptyLines } from "../../pages/ViewPublicationPage/tools/replaceSpacesToEmptyLines";
+
+
+export function useQuizData() {
+ const quizId = useQuizId();
+ const { data } = useSWR(["quizData", quizId], params => getQuizData(params[1]), {
+ suspense: true,
+ });
+
+ return data;
+}
+
+async function getQuizData(quizId: string) {
+ const response = await getData(quizId);
+ const quizDataResponse = response.data;
+
+ if (response.error) {
+ enqueueSnackbar(response.error);
+ throw new Error(response.error);
+ }
+ if (!quizDataResponse) {
+ throw new Error("Quiz not found");
+ }
+
+ const quizSettings = replaceSpacesToEmptyLines(parseQuizData(quizDataResponse, quizId));
+
+ return JSON.parse(JSON.stringify({ data: quizSettings }).replaceAll(/\\" \\"/g, '""').replaceAll(/" "/g, '""')).data as QuizSettings;
+}
diff --git a/yarn.lock b/yarn.lock
index e1e30ce..ef80ce4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2714,6 +2714,13 @@ react-dom@^18.2.0:
loose-envify "^1.1.0"
scheduler "^0.23.0"
+react-error-boundary@^4.0.12:
+ version "4.0.12"
+ resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-4.0.12.tgz#59f8f1dbc53bbbb34fc384c8db7cf4082cb63e2c"
+ integrity sha512-kJdxdEYlb7CPC1A0SeUY38cHpjuu6UkvzKiAmqmOFL21VRfMhOcWxTCBgLVCO0VEMh9JhFNcVaXlV4/BTpiwOA==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"