Merge branch 'dev' into 'staging'
split widget and default App components See merge request frontend/squzanswerer!50
This commit is contained in:
commit
00323a1c6c
@ -27,6 +27,8 @@
|
|||||||
"notistack": "^3.0.1",
|
"notistack": "^3.0.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-error-boundary": "^4.0.12",
|
||||||
|
"react-router-dom": "^6.21.3",
|
||||||
"swr": "^2.2.4",
|
"swr": "^2.2.4",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"use-debounce": "^9.0.4",
|
"use-debounce": "^9.0.4",
|
||||||
|
61
src/App.tsx
61
src/App.tsx
@ -1,56 +1,21 @@
|
|||||||
import { Box, CssBaseline, ThemeProvider } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
import { LocalizationProvider } from "@mui/x-date-pickers";
|
import { useParams } from "react-router-dom";
|
||||||
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
|
import QuizAnswerer from "./QuizAnswerer";
|
||||||
import { ruRU } from '@mui/x-date-pickers/locales';
|
import { QuizIdContext } from "./contexts/QuizIdContext";
|
||||||
import moment from "moment";
|
|
||||||
import { SnackbarProvider } from 'notistack';
|
|
||||||
import { SWRConfig } from "swr";
|
|
||||||
import { ViewPage } from "./pages/ViewPublicationPage/ViewPublicationPage";
|
|
||||||
import lightTheme from "./utils/themes/light";
|
|
||||||
|
|
||||||
|
|
||||||
const defaultQuizId = "ef836ff8-35b1-4031-9acf-af5766bac2b2";
|
const defaultQuizId = "ef836ff8-35b1-4031-9acf-af5766bac2b2";
|
||||||
|
|
||||||
moment.locale("ru");
|
export default function App() {
|
||||||
const localeText = ruRU.components.MuiLocalizationProvider.defaultProps.localeText;
|
const quizId = useParams().quizId ?? defaultQuizId;
|
||||||
|
|
||||||
interface Props {
|
|
||||||
widget?: boolean;
|
|
||||||
quizId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function App({ widget = false, quizId }: Props) {
|
|
||||||
quizId ??= defaultQuizId;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SWRConfig value={{
|
<QuizIdContext.Provider value={quizId}>
|
||||||
revalidateOnFocus: false,
|
<Box sx={{
|
||||||
shouldRetryOnError: false,
|
height: "100dvh",
|
||||||
}}>
|
}}>
|
||||||
<LocalizationProvider dateAdapter={AdapterMoment} adapterLocale="ru" localeText={localeText}>
|
<QuizAnswerer />
|
||||||
<ThemeProvider theme={lightTheme}>
|
</Box>
|
||||||
<SnackbarProvider
|
</QuizIdContext.Provider>
|
||||||
preventDuplicate={true}
|
|
||||||
style={{ backgroundColor: lightTheme.palette.brightPurple.main }}
|
|
||||||
>
|
|
||||||
<CssBaseline />
|
|
||||||
{widget ? (
|
|
||||||
<Box sx={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
}}>
|
|
||||||
<ViewPage quizId={quizId} />
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
<Box sx={{
|
|
||||||
height: "100dvh",
|
|
||||||
}}>
|
|
||||||
<ViewPage quizId={quizId} />
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</SnackbarProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
</LocalizationProvider>
|
|
||||||
</SWRConfig>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
47
src/QuizAnswerer.tsx
Normal file
47
src/QuizAnswerer.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
export default function QuizAnswerer() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SWRConfig value={{
|
||||||
|
revalidateOnFocus: false,
|
||||||
|
shouldRetryOnError: false,
|
||||||
|
}}>
|
||||||
|
<LocalizationProvider dateAdapter={AdapterMoment} adapterLocale="ru" localeText={localeText}>
|
||||||
|
<ThemeProvider theme={lightTheme}>
|
||||||
|
<SnackbarProvider
|
||||||
|
preventDuplicate={true}
|
||||||
|
style={{ backgroundColor: lightTheme.palette.brightPurple.main }}
|
||||||
|
>
|
||||||
|
<CssBaseline />
|
||||||
|
<ErrorBoundary
|
||||||
|
fallback={<ApologyPage message="Что-то пошло не так" />}
|
||||||
|
onError={handleComponentError}
|
||||||
|
>
|
||||||
|
<Suspense fallback={<LoadingSkeleton />}>
|
||||||
|
<ViewPage />
|
||||||
|
</Suspense>
|
||||||
|
</ErrorBoundary>
|
||||||
|
</SnackbarProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
</LocalizationProvider>
|
||||||
|
</SWRConfig>
|
||||||
|
);
|
||||||
|
}
|
22
src/WidgetApp.tsx
Normal file
22
src/WidgetApp.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Box } from "@mui/material";
|
||||||
|
import QuizAnswerer from "./QuizAnswerer";
|
||||||
|
import { QuizIdContext } from "./contexts/QuizIdContext";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
quizId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WidgetApp({ quizId }: Props) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<QuizIdContext.Provider value={quizId}>
|
||||||
|
<Box sx={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}>
|
||||||
|
<QuizAnswerer />
|
||||||
|
</Box>
|
||||||
|
</QuizIdContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
10
src/assets/icons/BlankImage.tsx
Normal file
10
src/assets/icons/BlankImage.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export default function BlankImage() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg width="100%" height="100%" viewBox="0 -70 800 535" fill="none" display="block" preserveAspectRatio="xMidYMax meet" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#F0F0F0" d="M555 47a47.003 47.003 0 0 1 29.014-43.422 46.999 46.999 0 0 1 61.408 61.408 46.997 46.997 0 0 1-76.656 15.248A47 47 0 0 1 555 47Z" />
|
||||||
|
<path fill="#F3F3F3" d="M641.874 240.665c7.74-7.74 20.263-7.82 28.102-.181L1051 611.837 779.035 883.805 383.869 498.67l258.005-258.005Z" />
|
||||||
|
<path fill="#EDEDED" d="M183.393 61.546c7.692-7.037 19.499-6.985 27.129.12l677.42 630.746-690.929 382.738L-397 592.531 183.393 61.546Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
11
src/contexts/QuizIdContext.ts
Normal file
11
src/contexts/QuizIdContext.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
export const QuizIdContext = createContext<string | null>(null);
|
||||||
|
|
||||||
|
export const useQuizId = () => {
|
||||||
|
const quizId = useContext(QuizIdContext);
|
||||||
|
if (quizId === null) throw new Error("quizId context is null");
|
||||||
|
|
||||||
|
return quizId;
|
||||||
|
};
|
19
src/main.tsx
19
src/main.tsx
@ -1,7 +1,24 @@
|
|||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
||||||
|
|
||||||
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
index: true,
|
||||||
|
element: <App />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":quizId",
|
||||||
|
element: <App />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
const root = createRoot(document.getElementById("root")!);
|
const root = createRoot(document.getElementById("root")!);
|
||||||
|
|
||||||
root.render(<App />);
|
root.render(<RouterProvider router={router} />);
|
||||||
|
@ -24,8 +24,8 @@ export interface GetQuizDataResponse {
|
|||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseQuizData(quizDataResponse: GetQuizDataResponse, quizId: string): QuizSettings {
|
export function parseQuizData(quizDataResponse: GetQuizDataResponse, quizId: string): Omit<QuizSettings, "recentlyCompleted"> {
|
||||||
const items: QuizSettings["items"] = quizDataResponse.items.map((item) => {
|
const items: QuizSettings["questions"] = quizDataResponse.items.map((item) => {
|
||||||
const content = JSON.parse(item.c);
|
const content = JSON.parse(item.c);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -51,5 +51,5 @@ export function parseQuizData(quizDataResponse: GetQuizDataResponse, quizId: str
|
|||||||
pausable: quizDataResponse.settings.pausable
|
pausable: quizDataResponse.settings.pausable
|
||||||
};
|
};
|
||||||
|
|
||||||
return { cnt: quizDataResponse.cnt, settings, items };
|
return { cnt: quizDataResponse.cnt, settings, questions: items };
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ export type FCField = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type QuizSettings = {
|
export type QuizSettings = {
|
||||||
items: AnyTypedQuizQuestion[];
|
questions: AnyTypedQuizQuestion[];
|
||||||
settings: {
|
settings: {
|
||||||
qid: string;
|
qid: string;
|
||||||
fp: boolean;
|
fp: boolean;
|
||||||
@ -43,6 +43,7 @@ export type QuizSettings = {
|
|||||||
cfg: QuizConfig;
|
cfg: QuizConfig;
|
||||||
};
|
};
|
||||||
cnt: number;
|
cnt: number;
|
||||||
|
recentlyCompleted: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface QuizConfig {
|
export interface QuizConfig {
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
import AddressIcon from "@icons/ContactFormIcon/AddressIcon";
|
import AddressIcon from "@icons/ContactFormIcon/AddressIcon";
|
||||||
import NameIcon from "@icons/ContactFormIcon/NameIcon";
|
|
||||||
import EmailIcon from "@icons/ContactFormIcon/EmailIcon";
|
import EmailIcon from "@icons/ContactFormIcon/EmailIcon";
|
||||||
|
import NameIcon from "@icons/ContactFormIcon/NameIcon";
|
||||||
import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon";
|
import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon";
|
||||||
import TextIcon from "@icons/ContactFormIcon/TextIcon";
|
import TextIcon from "@icons/ContactFormIcon/TextIcon";
|
||||||
import { Box, Button, InputAdornment, Link, TextField as MuiTextField, TextFieldProps, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Button, InputAdornment, Link, TextField as MuiTextField, TextFieldProps, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
|
|
||||||
|
|
||||||
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||||
import { FC, useRef, useState } from "react";
|
import { FC, useRef, useState } from "react";
|
||||||
|
|
||||||
import { sendFC } from "@api/quizRelase";
|
import { sendFC } from "@api/quizRelase";
|
||||||
import { NameplateLogo } from "@icons/NameplateLogo";
|
import { NameplateLogo } from "@icons/NameplateLogo";
|
||||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { ApologyPage } from "./ApologyPage";
|
import { ApologyPage } from "./ApologyPage";
|
||||||
import { checkEmptyData } from "./tools/checkEmptyData";
|
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<TextFieldProps>; // temporary fix ts(2590)
|
const TextField = MuiTextField as unknown as FC<TextFieldProps>; // temporary fix ts(2590)
|
||||||
@ -74,7 +73,7 @@ export const ContactForm = ({
|
|||||||
setShowResultForm,
|
setShowResultForm,
|
||||||
}: ContactFormProps) => {
|
}: ContactFormProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { settings, items } = useQuestionsStore();
|
const { settings, questions } = useQuizData();
|
||||||
|
|
||||||
const [ready, setReady] = useState(false);
|
const [ready, setReady] = useState(false);
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
@ -93,7 +92,7 @@ export const ContactForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const resultQuestion: QuizQuestionResult = items.find((question) => {
|
const resultQuestion: QuizQuestionResult = questions.find((question) => {
|
||||||
if (settings?.cfg.haveRoot) {
|
if (settings?.cfg.haveRoot) {
|
||||||
//ветвимся
|
//ветвимся
|
||||||
return (
|
return (
|
||||||
@ -110,8 +109,6 @@ export const ContactForm = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const inputHC = async () => {
|
const inputHC = async () => {
|
||||||
if (!settings) return;
|
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const FC = settings?.cfg.formContact.fields || settings?.cfg.formContact;
|
const FC = settings?.cfg.formContact.fields || settings?.cfg.formContact;
|
||||||
const body = {};
|
const body = {};
|
||||||
@ -157,7 +154,6 @@ export const ContactForm = ({
|
|||||||
}
|
}
|
||||||
let isWide = Object.keys(filteredFC).length > 2;
|
let isWide = Object.keys(filteredFC).length > 2;
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
|
||||||
if (!resultQuestion)
|
if (!resultQuestion)
|
||||||
return (
|
return (
|
||||||
<ApologyPage message="не получилось найти результат для этой ветки :(" />
|
<ApologyPage message="не получилось найти результат для этой ветки :(" />
|
||||||
@ -360,15 +356,13 @@ export const ContactForm = ({
|
|||||||
<NameplateLogo
|
<NameplateLogo
|
||||||
style={{
|
style={{
|
||||||
fontSize: "34px",
|
fontSize: "34px",
|
||||||
//@ts-ignore
|
color: quizThemes[settings.cfg.theme].isLight ? "#151515" : "#FFFFFF",
|
||||||
color: mode[settings.cfg.theme] ? "#151515" : "#FFFFFF",
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "20px",
|
fontSize: "20px",
|
||||||
//@ts-ignore
|
color: quizThemes[settings.cfg.theme].isLight ? "#4D4D4D" : "#F5F7FF",
|
||||||
color: mode[settings.cfg.theme] ? "#4D4D4D" : "#F5F7FF",
|
|
||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -393,7 +387,7 @@ const Inputs = ({
|
|||||||
adress,
|
adress,
|
||||||
setAdress,
|
setAdress,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const { settings } = useQuestionsStore();
|
const { settings } = useQuizData();
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const FC = settings?.cfg.formContact.fields || settings?.cfg.formContact;
|
const FC = settings?.cfg.formContact.fields || settings?.cfg.formContact;
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import { Box, Button, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Button, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
|
|
||||||
import { getQuestionById } from "@stores/quizData/actions";
|
|
||||||
|
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import type { AnyTypedQuizQuestion, QuizQuestionBase } from "../../model/questionTypes/shared";
|
import type { AnyTypedQuizQuestion, QuizQuestionBase } from "../../model/questionTypes/shared";
|
||||||
|
|
||||||
import { checkEmptyData } from "./tools/checkEmptyData";
|
import { checkEmptyData } from "./tools/checkEmptyData";
|
||||||
|
|
||||||
import type { QuizQuestionResult } from "@model/questionTypes/result";
|
import type { QuizQuestionResult } from "@model/questionTypes/result";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
|
||||||
import { useQuizViewStore } from "@stores/quizView/store";
|
import { useQuizViewStore } from "@stores/quizView/store";
|
||||||
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
type FooterProps = {
|
type FooterProps = {
|
||||||
setCurrentQuestion: (step: AnyTypedQuizQuestion) => void;
|
setCurrentQuestion: (step: AnyTypedQuizQuestion) => void;
|
||||||
@ -22,13 +20,13 @@ type FooterProps = {
|
|||||||
export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setShowResultForm }: FooterProps) => {
|
export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setShowResultForm }: FooterProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const { settings, items } = useQuestionsStore();
|
const { settings, questions } = useQuizData();
|
||||||
const answers = useQuizViewStore(state => state.answers);
|
const answers = useQuizViewStore(state => state.answers);
|
||||||
|
|
||||||
const [stepNumber, setStepNumber] = useState(1);
|
const [stepNumber, setStepNumber] = useState(1);
|
||||||
|
|
||||||
const isMobileMini = useMediaQuery(theme.breakpoints.down(382));
|
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(() => {
|
const getNextQuestionId = useCallback(() => {
|
||||||
console.log("Смотрим какой вопрос будет дальше. Что у нас сегодня вкусненького? Щя покажу от какого вопроса мы ищем следующий шаг");
|
console.log("Смотрим какой вопрос будет дальше. Что у нас сегодня вкусненького? Щя покажу от какого вопроса мы ищем следующий шаг");
|
||||||
@ -86,27 +84,27 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
}
|
}
|
||||||
//ничё не нашли, ищем резулт
|
//ничё не нашли, ищем резулт
|
||||||
console.log("ничё не нашли, ищем резулт ");
|
console.log("ничё не нашли, ищем резулт ");
|
||||||
return items.find(q => {
|
return questions.find(q => {
|
||||||
console.log('q.type === "result"', q.type === "result");
|
console.log('q.type === "result"', q.type === "result");
|
||||||
console.log('q.content.rule.parentId', q.content.rule.parentId);
|
console.log('q.content.rule.parentId', q.content.rule.parentId);
|
||||||
console.log('question.content.id', question.content.id);
|
console.log('question.content.id', question.content.id);
|
||||||
return q.type === "result" && q.content.rule.parentId === question.content.id;
|
return q.type === "result" && q.content.rule.parentId === question.content.id;
|
||||||
})?.id;
|
})?.id;
|
||||||
|
|
||||||
}, [answers, items, question]);
|
}, [answers, questions, question]);
|
||||||
|
|
||||||
const isPreviousButtonDisabled = useMemo(() => {
|
const isPreviousButtonDisabled = useMemo(() => {
|
||||||
// Логика для аргумента disabled у кнопки "Назад"
|
// Логика для аргумента disabled у кнопки "Назад"
|
||||||
if (isLinear) {
|
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;
|
return previousQuestion ? false : true;
|
||||||
} else {
|
} else {
|
||||||
return question?.content.rule.parentId === "root" ? true : false;
|
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(() => {
|
const isNextButtonDisabled = useMemo(() => {
|
||||||
// Логика для аргумента disabled у кнопки "Далее"
|
// Логика для аргумента disabled у кнопки "Далее"
|
||||||
@ -128,7 +126,8 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
if (nextQuestionId) {
|
if (nextQuestionId) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} 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) {
|
if (nextQuestion?.type) {
|
||||||
return false;
|
return false;
|
||||||
@ -137,7 +136,6 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
}, [answers, getNextQuestionId, isLinear, question.content, question.id]);
|
}, [answers, getNextQuestionId, isLinear, question.content, question.id]);
|
||||||
|
|
||||||
const showResult = (nextQuestion: QuizQuestionResult) => {
|
const showResult = (nextQuestion: QuizQuestionResult) => {
|
||||||
if (!settings) return;
|
|
||||||
if (!nextQuestion) return;
|
if (!nextQuestion) return;
|
||||||
|
|
||||||
const isEmpty = checkEmptyData({ resultData: nextQuestion });
|
const isEmpty = checkEmptyData({ resultData: nextQuestion });
|
||||||
@ -168,9 +166,9 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
if (isLinear) {
|
if (isLinear) {
|
||||||
setStepNumber(q => q - 1);
|
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) {
|
if (previousQuestion) {
|
||||||
setCurrentQuestion(previousQuestion);
|
setCurrentQuestion(previousQuestion);
|
||||||
@ -180,7 +178,8 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (question?.content.rule.parentId !== "root") {
|
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) {
|
if (parent?.type) {
|
||||||
setCurrentQuestion(parent);
|
setCurrentQuestion(parent);
|
||||||
} else {
|
} else {
|
||||||
@ -195,14 +194,14 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
if (isLinear) {
|
if (isLinear) {
|
||||||
setStepNumber(q => q + 1);
|
setStepNumber(q => q + 1);
|
||||||
|
|
||||||
const questionIndex = items.findIndex(({ id }) => id === question.id);
|
const questionIndex = questions.findIndex(({ id }) => id === question.id);
|
||||||
const nextQuestion = items[questionIndex + 1];
|
const nextQuestion = questions[questionIndex + 1];
|
||||||
|
|
||||||
if (nextQuestion && nextQuestion.type !== "result") {
|
if (nextQuestion && nextQuestion.type !== "result") {
|
||||||
setCurrentQuestion(nextQuestion);
|
setCurrentQuestion(nextQuestion);
|
||||||
} else {
|
} else {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
showResult(items.find(q => q.content.rule.parentId === "line"));
|
showResult(questions.find(q => q.content.rule.parentId === "line"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -211,7 +210,7 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
const nextQuestionId = getNextQuestionId();
|
const nextQuestionId = getNextQuestionId();
|
||||||
|
|
||||||
if (nextQuestionId) {
|
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") {
|
if (nextQuestion?.type && nextQuestion.type === "result") {
|
||||||
showResult(nextQuestion);
|
showResult(nextQuestion);
|
||||||
@ -278,7 +277,7 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Typography>Из</Typography>
|
<Typography>Из</Typography>
|
||||||
<Typography sx={{ fontWeight: "bold" }}>
|
<Typography sx={{ fontWeight: "bold" }}>
|
||||||
{items.filter(q => q.type !== "result").length}
|
{questions.filter(q => q.type !== "result").length}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { getQuestionById } from "@stores/quizData/actions";
|
|
||||||
|
|
||||||
import { ContactForm } from "./ContactForm";
|
import { ContactForm } from "./ContactForm";
|
||||||
import { Footer } from "./Footer";
|
import { Footer } from "./Footer";
|
||||||
import { ResultForm } from "./ResultForm";
|
import { ResultForm } from "./ResultForm";
|
||||||
@ -23,23 +21,22 @@ import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
|||||||
import { NameplateLogoFQ } from "@icons/NameplateLogoFQ";
|
import { NameplateLogoFQ } from "@icons/NameplateLogoFQ";
|
||||||
import { NameplateLogoFQDark } from "@icons/NameplateLogoFQDark";
|
import { NameplateLogoFQDark } from "@icons/NameplateLogoFQDark";
|
||||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
|
||||||
import { notReachable } from "@utils/notReachable";
|
import { notReachable } from "@utils/notReachable";
|
||||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
export const Question = () => {
|
export const Question = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const settings = useQuestionsStore(state => state.settings);
|
const { settings, questions } = useQuizData();
|
||||||
const questions = useQuestionsStore(state => state.items);
|
|
||||||
const [currentQuestion, setCurrentQuestion] = useState<AnyTypedQuizQuestion>();
|
const [currentQuestion, setCurrentQuestion] = useState<AnyTypedQuizQuestion>();
|
||||||
const [showContactForm, setShowContactForm] = useState<boolean>(false);
|
const [showContactForm, setShowContactForm] = useState<boolean>(false);
|
||||||
const [showResultForm, setShowResultForm] = useState<boolean>(false);
|
const [showResultForm, setShowResultForm] = useState<boolean>(false);
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (settings?.cfg.haveRoot) {//ветвимся
|
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) {
|
if (nextQuestion?.type) {
|
||||||
setCurrentQuestion(nextQuestion);
|
setCurrentQuestion(nextQuestion);
|
||||||
@ -49,10 +46,8 @@ export const Question = () => {
|
|||||||
} else {//идём прямо
|
} else {//идём прямо
|
||||||
setCurrentQuestion(questions[0]);
|
setCurrentQuestion(questions[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
|
||||||
if (!currentQuestion || currentQuestion.type === "result") return "не смог отобразить вопрос";
|
if (!currentQuestion || currentQuestion.type === "result") return "не смог отобразить вопрос";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -9,10 +9,10 @@ import {
|
|||||||
import { NameplateLogo } from "@icons/NameplateLogo";
|
import { NameplateLogo } from "@icons/NameplateLogo";
|
||||||
import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe";
|
import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe";
|
||||||
|
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
|
||||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
import { useCallback, useEffect, useMemo } from "react";
|
import { useCallback, useEffect, useMemo } from "react";
|
||||||
import type { QuizQuestionResult } from "../../model/questionTypes/result";
|
import type { QuizQuestionResult } from "../../model/questionTypes/result";
|
||||||
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
|
|
||||||
type ResultFormProps = {
|
type ResultFormProps = {
|
||||||
@ -29,34 +29,33 @@ export const ResultForm = ({
|
|||||||
}: ResultFormProps) => {
|
}: ResultFormProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
const { settings, items } = useQuestionsStore();
|
const { settings, questions } = useQuizData();
|
||||||
if (!settings) throw new Error("settings is null");
|
|
||||||
|
|
||||||
const resultQuestion = useMemo(() => {
|
const resultQuestion = useMemo(() => {
|
||||||
if (settings?.cfg.haveRoot) {
|
if (settings?.cfg.haveRoot) {
|
||||||
//ищём для ветвления
|
//ищём для ветвления
|
||||||
return (items.find(
|
return (questions.find(
|
||||||
(question): question is QuizQuestionResult =>
|
(question): question is QuizQuestionResult =>
|
||||||
question.type === "result" &&
|
question.type === "result" &&
|
||||||
question.content.rule.parentId === currentQuestion.content.id
|
question.content.rule.parentId === currentQuestion.content.id
|
||||||
) || items.find(
|
) || questions.find(
|
||||||
(question): question is QuizQuestionResult =>
|
(question): question is QuizQuestionResult =>
|
||||||
question.type === "result" &&
|
question.type === "result" &&
|
||||||
question.content.rule.parentId === "line"
|
question.content.rule.parentId === "line"
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
return items.find(
|
return questions.find(
|
||||||
(question): question is QuizQuestionResult =>
|
(question): question is QuizQuestionResult =>
|
||||||
question.type === "result" &&
|
question.type === "result" &&
|
||||||
question.content.rule.parentId === "line"
|
question.content.rule.parentId === "line"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [currentQuestion.content.id, items, settings?.cfg.haveRoot]);
|
}, [currentQuestion.content.id, questions, settings?.cfg.haveRoot]);
|
||||||
|
|
||||||
const followNextForm = useCallback(() => {
|
const followNextForm = useCallback(() => {
|
||||||
setShowResultForm(false);
|
setShowResultForm(false);
|
||||||
setShowContactForm(true);
|
setShowContactForm(true);
|
||||||
},[setShowContactForm, setShowResultForm]);
|
}, [setShowContactForm, setShowResultForm]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!resultQuestion) {
|
if (!resultQuestion) {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { Box, Button, ButtonBase, Link, Paper, Typography, useMediaQuery, useTheme } from "@mui/material";
|
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 { useUADevice } from "../../utils/hooks/useUADevice";
|
||||||
|
import { notReachable } from "../../utils/notReachable";
|
||||||
|
import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe";
|
||||||
|
|
||||||
import { NameplateLogo } from "@icons/NameplateLogo";
|
import { NameplateLogo } from "@icons/NameplateLogo";
|
||||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
|
||||||
import { QuizStartpageAlignType, QuizStartpageType } from "@model/settingsData";
|
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 {
|
interface Props {
|
||||||
@ -15,12 +15,10 @@ interface Props {
|
|||||||
|
|
||||||
export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { settings } = useQuestionsStore();
|
const { settings } = useQuizData();
|
||||||
const { isMobileDevice } = useUADevice();
|
const { isMobileDevice } = useUADevice();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
|
||||||
|
|
||||||
const handleCopyNumber = () => {
|
const handleCopyNumber = () => {
|
||||||
navigator.clipboard.writeText(settings.cfg.info.phonenumber);
|
navigator.clipboard.writeText(settings.cfg.info.phonenumber);
|
||||||
};
|
};
|
||||||
|
@ -1,56 +1,33 @@
|
|||||||
import { getData } from "@api/quizRelase";
|
|
||||||
import { QuizSettings } from "@model/settingsData";
|
|
||||||
import { Box, ThemeProvider } from "@mui/material";
|
import { Box, ThemeProvider } from "@mui/material";
|
||||||
import { setQuizData } from "@stores/quizData/actions";
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
|
||||||
import LoadingSkeleton from "@ui_kit/LoadingSkeleton";
|
|
||||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
import { enqueueSnackbar } from "notistack";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import useSWR from "swr";
|
|
||||||
import { ApologyPage } from "./ApologyPage";
|
import { ApologyPage } from "./ApologyPage";
|
||||||
import { Question } from "./Question";
|
import { Question } from "./Question";
|
||||||
import { StartPageViewPublication } from "./StartPageViewPublication";
|
import { StartPageViewPublication } from "./StartPageViewPublication";
|
||||||
|
|
||||||
import { parseQuizData } from "@model/api/getQuizData";
|
|
||||||
import { replaceSpacesToEmptyLines } from "./tools/replaceSpacesToEmptyLines";
|
|
||||||
|
|
||||||
type Props = {
|
export const ViewPage = () => {
|
||||||
quizId: string;
|
const { settings, questions, recentlyCompleted } = useQuizData();
|
||||||
};
|
|
||||||
|
|
||||||
export const ViewPage = ({ quizId }: Props) => {
|
|
||||||
const { isLoading, error } = useSWR(["quizData", quizId], params => getQuizData(params[1]), {
|
|
||||||
onSuccess: setQuizData,
|
|
||||||
});
|
|
||||||
const { settings, items, recentlyСompleted } = useQuestionsStore();
|
|
||||||
const [visualStartPage, setVisualStartPage] = useState<boolean>();
|
const [visualStartPage, setVisualStartPage] = useState<boolean>();
|
||||||
|
|
||||||
useEffect(() => {//установка фавиконки
|
useEffect(() => {
|
||||||
if (!settings) return;
|
|
||||||
|
|
||||||
const link = document.querySelector('link[rel="icon"]');
|
const link = document.querySelector('link[rel="icon"]');
|
||||||
if (link && settings.cfg.startpage.favIcon) {
|
if (link && settings.cfg.startpage.favIcon) {
|
||||||
link.setAttribute("href", settings?.cfg.startpage.favIcon);
|
link.setAttribute("href", settings?.cfg.startpage.favIcon);
|
||||||
}
|
}
|
||||||
//установка заголовка страницы
|
|
||||||
document.title = settings.name;
|
document.title = settings.name;
|
||||||
|
|
||||||
setVisualStartPage(!settings.cfg.noStartPage);
|
setVisualStartPage(!settings.cfg.noStartPage);
|
||||||
}, [settings]);
|
}, [settings]);
|
||||||
|
|
||||||
const questionsCount = items.filter(({ type }) => type !== null && type !== "result").length;
|
const questionsCount = questions.filter(({ type }) => type !== null && type !== "result").length;
|
||||||
|
|
||||||
if (error) {
|
|
||||||
console.log(error);
|
|
||||||
return <ApologyPage message="Что-то пошло не так" />;
|
|
||||||
}
|
|
||||||
if (isLoading || !settings) return <LoadingSkeleton />;
|
|
||||||
if (questionsCount === 0) return <ApologyPage message="Нет созданных вопросов" />;
|
if (questionsCount === 0) return <ApologyPage message="Нет созданных вопросов" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={quizThemes[settings.cfg.theme || "StandardTheme"]}>
|
<ThemeProvider theme={quizThemes[settings.cfg.theme || "StandardTheme"]}>
|
||||||
{recentlyСompleted ? (
|
{recentlyCompleted ? (
|
||||||
<ApologyPage message="Вы уже прошли этот опрос" />
|
<ApologyPage message="Вы уже прошли этот опрос" />
|
||||||
) : (
|
) : (
|
||||||
<Box>
|
<Box>
|
||||||
@ -63,21 +40,4 @@ export const ViewPage = ({ quizId }: Props) => {
|
|||||||
)}
|
)}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
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; };
|
|
||||||
}
|
|
||||||
|
@ -8,103 +8,101 @@ import type { QuizQuestionDate } from "../../../model/questionTypes/date";
|
|||||||
import CalendarIcon from "@icons/CalendarIcon";
|
import CalendarIcon from "@icons/CalendarIcon";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
|
|
||||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
type DateProps = {
|
type DateProps = {
|
||||||
currentQuestion: QuizQuestionDate;
|
currentQuestion: QuizQuestionDate;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Date = ({ currentQuestion }: DateProps) => {
|
export const Date = ({ currentQuestion }: DateProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const { settings } = useQuestionsStore();
|
const { settings } = useQuizData();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const answer = answers.find(
|
const answer = answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.id
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
)?.answer as string;
|
)?.answer as string;
|
||||||
const currentAnswer = moment(answer) || moment();
|
const currentAnswer = moment(answer) || moment();
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5" color={theme.palette.text.primary}>
|
<Typography variant="h5" color={theme.palette.text.primary}>
|
||||||
{currentQuestion.title}
|
{currentQuestion.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DatePicker
|
|
||||||
slots={{
|
|
||||||
openPickerIcon: () => (
|
|
||||||
<CalendarIcon
|
|
||||||
sx={{
|
sx={{
|
||||||
"& path": { stroke: theme.palette.primary.main },
|
display: "flex",
|
||||||
"& rect": { stroke: theme.palette.primary.main },
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
marginTop: "20px",
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
),
|
<DatePicker
|
||||||
}}
|
slots={{
|
||||||
value={ currentAnswer }
|
openPickerIcon: () => (
|
||||||
onChange={async (date) => {
|
<CalendarIcon
|
||||||
console.log(date)
|
sx={{
|
||||||
if (!date) {
|
"& path": { stroke: theme.palette.primary.main },
|
||||||
return;
|
"& rect": { stroke: theme.palette.primary.main },
|
||||||
}
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
value={currentAnswer}
|
||||||
|
onChange={async (date) => {
|
||||||
|
console.log(date);
|
||||||
|
if (!date) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: moment(date).format("YYYY.MM.DD"),
|
body: moment(date).format("YYYY.MM.DD"),
|
||||||
qid: settings.qid,
|
qid: settings.qid,
|
||||||
});
|
});
|
||||||
|
|
||||||
updateAnswer(
|
updateAnswer(
|
||||||
currentQuestion.id,
|
currentQuestion.id,
|
||||||
date
|
date
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
enqueueSnackbar("ответ не был засчитан");
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
openPickerButton: {
|
openPickerButton: {
|
||||||
sx: {
|
sx: {
|
||||||
p: 0,
|
p: 0,
|
||||||
},
|
},
|
||||||
"data-cy": "open-datepicker",
|
"data-cy": "open-datepicker",
|
||||||
},
|
},
|
||||||
layout: {
|
layout: {
|
||||||
sx: { backgroundColor: theme.palette.background.default },
|
sx: { backgroundColor: theme.palette.background.default },
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiInputBase-root": {
|
"& .MuiInputBase-root": {
|
||||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||||
? "white"
|
? "white"
|
||||||
: theme.palette.background.default,
|
: theme.palette.background.default,
|
||||||
borderRadius: "10px",
|
borderRadius: "10px",
|
||||||
maxWidth: "250px",
|
maxWidth: "250px",
|
||||||
pr: "22px",
|
pr: "22px",
|
||||||
"& input": {
|
"& input": {
|
||||||
py: "11px",
|
py: "11px",
|
||||||
pl: "20px",
|
pl: "20px",
|
||||||
lineHeight: "19px",
|
lineHeight: "19px",
|
||||||
},
|
},
|
||||||
"& fieldset": {
|
"& fieldset": {
|
||||||
borderColor: "#9A9AAF",
|
borderColor: "#9A9AAF",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -14,148 +14,146 @@ import RadioCheck from "@ui_kit/RadioCheck";
|
|||||||
import RadioIcon from "@ui_kit/RadioIcon";
|
import RadioIcon from "@ui_kit/RadioIcon";
|
||||||
|
|
||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
|
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import type { QuizQuestionEmoji } from "../../../model/questionTypes/emoji";
|
import type { QuizQuestionEmoji } from "../../../model/questionTypes/emoji";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
type EmojiProps = {
|
type EmojiProps = {
|
||||||
currentQuestion: QuizQuestionEmoji;
|
currentQuestion: QuizQuestionEmoji;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const { settings } = useQuestionsStore()
|
|
||||||
const { answers } = useQuizViewStore();
|
|
||||||
const { answer } =
|
|
||||||
answers.find(
|
|
||||||
({ questionId }) => questionId === currentQuestion.id
|
|
||||||
) ?? {};
|
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
const { settings } = useQuizData();
|
||||||
|
const { answers } = useQuizViewStore();
|
||||||
|
const { answer } =
|
||||||
|
answers.find(
|
||||||
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
|
) ?? {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name={currentQuestion.id}
|
name={currentQuestion.id}
|
||||||
value={currentQuestion.content.variants.findIndex(
|
value={currentQuestion.content.variants.findIndex(
|
||||||
({ id }) => answer === id
|
({ id }) => answer === id
|
||||||
)}
|
)}
|
||||||
onChange={({ target }) =>{
|
onChange={({ target }) => {
|
||||||
updateAnswer(
|
updateAnswer(
|
||||||
currentQuestion.id,
|
|
||||||
currentQuestion.content.variants[Number(target.value)].answer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexWrap: "wrap",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
marginTop: "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ display: "flex", width: "100%", gap: "42px", flexWrap: "wrap" }}>
|
|
||||||
{currentQuestion.content.variants.map((variant, index) => (
|
|
||||||
<FormControl
|
|
||||||
key={variant.id}
|
|
||||||
sx={{
|
|
||||||
borderRadius: "12px",
|
|
||||||
border: `1px solid`,
|
|
||||||
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
|
||||||
overflow: "hidden",
|
|
||||||
maxWidth: "317px",
|
|
||||||
width: "100%",
|
|
||||||
height: "255px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "193px",
|
|
||||||
background: "#ffffff",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{variant.extendedText && (
|
|
||||||
<Typography fontSize={"100px"}>
|
|
||||||
{variant.extendedText}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<FormControlLabel
|
|
||||||
key={variant.id}
|
|
||||||
sx={{
|
|
||||||
margin: 0,
|
|
||||||
padding: "15px",
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
display: "flex",
|
|
||||||
gap: "10px",
|
|
||||||
}}
|
|
||||||
value={index}
|
|
||||||
onClick={async (event) => {
|
|
||||||
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.id,
|
||||||
currentQuestion.content.variants[index].id
|
currentQuestion.content.variants[Number(target.value)].answer
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} catch (e) {
|
}
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
sx={{
|
||||||
}
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
if (answer === currentQuestion.content.variants[index].id) {
|
marginTop: "20px",
|
||||||
deleteAnswer(currentQuestion.id);
|
}}
|
||||||
try {
|
>
|
||||||
|
<Box sx={{ display: "flex", width: "100%", gap: "42px", flexWrap: "wrap" }}>
|
||||||
await sendAnswer({
|
{currentQuestion.content.variants.map((variant, index) => (
|
||||||
questionId: currentQuestion.id,
|
<FormControl
|
||||||
body: "",
|
key={variant.id}
|
||||||
qid: settings.qid
|
sx={{
|
||||||
})
|
borderRadius: "12px",
|
||||||
|
border: `1px solid`,
|
||||||
} catch (e) {
|
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
overflow: "hidden",
|
||||||
}
|
maxWidth: "317px",
|
||||||
}
|
width: "100%",
|
||||||
}}
|
height: "255px",
|
||||||
|
}}
|
||||||
control={
|
>
|
||||||
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />} />
|
<Box
|
||||||
}
|
sx={{
|
||||||
label={
|
display: "flex",
|
||||||
<Box sx={{ display: "flex", gap: "10px" }}>
|
alignItems: "center",
|
||||||
<Typography sx={{wordBreak: "break-word"}}>{variant.answer}</Typography>
|
height: "193px",
|
||||||
</Box>
|
background: "#ffffff",
|
||||||
}
|
}}
|
||||||
/>
|
>
|
||||||
</FormControl>
|
<Box
|
||||||
))}
|
sx={{
|
||||||
</Box>
|
width: "100%",
|
||||||
</RadioGroup>
|
display: "flex",
|
||||||
</Box>
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{variant.extendedText && (
|
||||||
|
<Typography fontSize={"100px"}>
|
||||||
|
{variant.extendedText}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<FormControlLabel
|
||||||
|
key={variant.id}
|
||||||
|
sx={{
|
||||||
|
margin: 0,
|
||||||
|
padding: "15px",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
display: "flex",
|
||||||
|
gap: "10px",
|
||||||
|
}}
|
||||||
|
value={index}
|
||||||
|
onClick={async (event) => {
|
||||||
|
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={
|
||||||
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main} />} icon={<RadioIcon />} />
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Box sx={{ display: "flex", gap: "10px" }}>
|
||||||
|
<Typography sx={{ wordBreak: "break-word" }}>{variant.answer}</Typography>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</RadioGroup>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
ButtonBase,
|
ButtonBase,
|
||||||
useTheme,
|
useTheme,
|
||||||
IconButton, useMediaQuery, Modal,
|
IconButton, useMediaQuery, Modal,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
|
import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
|
||||||
import { UPLOAD_FILE_TYPES_MAP } from "../tools/File";
|
|
||||||
|
|
||||||
import UploadIcon from "@icons/UploadIcon";
|
import UploadIcon from "@icons/UploadIcon";
|
||||||
import CloseBold from "@icons/CloseBold";
|
import CloseBold from "@icons/CloseBold";
|
||||||
@ -17,328 +16,326 @@ import type { DragEvent } from "react";
|
|||||||
import type { UploadFileType } from "@model/questionTypes/file";
|
import type { UploadFileType } from "@model/questionTypes/file";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { sendAnswer, sendFile } from "@api/quizRelase";
|
import { sendAnswer, sendFile } from "@api/quizRelase";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store"
|
|
||||||
import Info from "@icons/Info";
|
import Info from "@icons/Info";
|
||||||
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
type FileProps = {
|
type FileProps = {
|
||||||
currentQuestion: QuizQuestionFile;
|
currentQuestion: QuizQuestionFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CurrentModal = ({ status }: { status: "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | "" }) => {
|
const CurrentModal = ({ status }: { status: "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | ""; }) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'errorType':
|
case 'errorType':
|
||||||
return (<>
|
return (<>
|
||||||
<Typography>Выбран некорректный тип файла</Typography>
|
<Typography>Выбран некорректный тип файла</Typography>
|
||||||
</>)
|
</>);
|
||||||
case 'errorSize':
|
case 'errorSize':
|
||||||
return (<>
|
return (<>
|
||||||
<Typography>Файл слишком большой. Максимальный размер 50 МБ</Typography>
|
<Typography>Файл слишком большой. Максимальный размер 50 МБ</Typography>
|
||||||
</>)
|
</>);
|
||||||
default:
|
default:
|
||||||
return (<>
|
return (<>
|
||||||
<Typography>Допустимые расширения файлов:</Typography>
|
<Typography>Допустимые расширения файлов:</Typography>
|
||||||
<Typography>{
|
<Typography>{
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
ACCEPT_SEND_FILE_TYPES_MAP[status].join(" ")}</Typography>
|
ACCEPT_SEND_FILE_TYPES_MAP[status].join(" ")}</Typography>
|
||||||
</>)
|
</>);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const ACCEPT_SEND_FILE_TYPES_MAP = {
|
const ACCEPT_SEND_FILE_TYPES_MAP = {
|
||||||
picture: [
|
picture: [
|
||||||
".jpeg",
|
".jpeg",
|
||||||
".jpg",
|
".jpg",
|
||||||
".png",
|
".png",
|
||||||
".ico",
|
".ico",
|
||||||
".gif",
|
".gif",
|
||||||
".tiff",
|
".tiff",
|
||||||
".webp",
|
".webp",
|
||||||
".eps",
|
".eps",
|
||||||
".svg"
|
".svg"
|
||||||
],
|
],
|
||||||
video: [
|
video: [
|
||||||
".mp4",
|
".mp4",
|
||||||
".mov",
|
".mov",
|
||||||
".wmv",
|
".wmv",
|
||||||
".avi",
|
".avi",
|
||||||
".avchd",
|
".avchd",
|
||||||
".flv",
|
".flv",
|
||||||
".f4v",
|
".f4v",
|
||||||
".swf",
|
".swf",
|
||||||
".mkv",
|
".mkv",
|
||||||
".webm",
|
".webm",
|
||||||
".mpeg-2"
|
".mpeg-2"
|
||||||
],
|
],
|
||||||
audio: [
|
audio: [
|
||||||
".aac",
|
".aac",
|
||||||
".aiff",
|
".aiff",
|
||||||
".dsd",
|
".dsd",
|
||||||
".flac",
|
".flac",
|
||||||
".mp3",
|
".mp3",
|
||||||
".mqa",
|
".mqa",
|
||||||
".ogg",
|
".ogg",
|
||||||
".wav",
|
".wav",
|
||||||
".wma"
|
".wma"
|
||||||
],
|
],
|
||||||
document: [
|
document: [
|
||||||
".doc",
|
".doc",
|
||||||
".docx",
|
".docx",
|
||||||
".dotx",
|
".dotx",
|
||||||
".rtf",
|
".rtf",
|
||||||
".odt",
|
".odt",
|
||||||
".pdf",
|
".pdf",
|
||||||
".txt",
|
".txt",
|
||||||
".xls",
|
".xls",
|
||||||
".ppt",
|
".ppt",
|
||||||
".xlsx",
|
".xlsx",
|
||||||
".pptx",
|
".pptx",
|
||||||
".pages",
|
".pages",
|
||||||
],
|
],
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
const UPLOAD_FILE_DESCRIPTIONS_MAP: Record<
|
const UPLOAD_FILE_DESCRIPTIONS_MAP: Record<
|
||||||
UploadFileType,
|
UploadFileType,
|
||||||
{ title: string; description: string }
|
{ title: string; description: string; }
|
||||||
> = {
|
> = {
|
||||||
picture: {
|
picture: {
|
||||||
title: "Добавить изображение",
|
title: "Добавить изображение",
|
||||||
description: "Принимает изображения",
|
description: "Принимает изображения",
|
||||||
},
|
},
|
||||||
video: {
|
video: {
|
||||||
title: "Добавить видео",
|
title: "Добавить видео",
|
||||||
description: "Принимает .mp4 и .mov формат — максимум 100мб",
|
description: "Принимает .mp4 и .mov формат — максимум 100мб",
|
||||||
},
|
},
|
||||||
audio: { title: "Добавить аудиофайл", description: "Принимает аудиофайлы" },
|
audio: { title: "Добавить аудиофайл", description: "Принимает аудиофайлы" },
|
||||||
document: { title: "Добавить документ", description: "Принимает документы" },
|
document: { title: "Добавить документ", description: "Принимает документы" },
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const File = ({ currentQuestion }: FileProps) => {
|
export const File = ({ currentQuestion }: FileProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuizData();
|
||||||
const { answers } = useQuizViewStore();
|
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(
|
const answer = answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.id
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
)?.answer as string;
|
)?.answer as string;
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
||||||
const uploadFile = async ({ target }: ChangeEvent<HTMLInputElement>) => {
|
const uploadFile = async ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||||
if (!settings) return;
|
const file = target.files?.[0];
|
||||||
|
if (file) {
|
||||||
const file = target.files?.[0];
|
if (file.size <= 52428800) {
|
||||||
if (file) {
|
//проверяем на соответствие
|
||||||
if (file.size <= 52428800) {
|
console.log(file.name.toLowerCase());
|
||||||
//проверяем на соответствие
|
if (ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].find((ednding => {
|
||||||
console.log(file.name.toLowerCase())
|
console.log(ednding);
|
||||||
if (ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].find((ednding => {
|
console.log(file.name.toLowerCase().endsWith(ednding));
|
||||||
console.log(ednding)
|
return file.name.toLowerCase().endsWith(ednding);
|
||||||
console.log(file.name.toLowerCase().endsWith(ednding))
|
}))) {
|
||||||
return file.name.toLowerCase().endsWith(ednding)
|
|
||||||
}))) {
|
|
||||||
|
|
||||||
//Нужный формат
|
//Нужный формат
|
||||||
console.log(file)
|
console.log(file);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const data = await sendFile({
|
const data = await sendFile({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: {
|
body: {
|
||||||
file: file,
|
file: file,
|
||||||
name: file.name
|
name: file.name
|
||||||
},
|
},
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
});
|
||||||
console.log(data)
|
console.log(data);
|
||||||
|
|
||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
body: `https://storage.yandexcloud.net/squizanswer/${settings.qid}/${currentQuestion.id}/${data.data.fileIDMap[currentQuestion.id]}`,
|
body: `https://storage.yandexcloud.net/squizanswer/${settings.qid}/${currentQuestion.id}/${data.data.fileIDMap[currentQuestion.id]}`,
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
});
|
||||||
|
|
||||||
updateAnswer(
|
updateAnswer(
|
||||||
currentQuestion.id,
|
currentQuestion.id,
|
||||||
`${file.name}|${URL.createObjectURL(file)}`
|
`${file.name}|${URL.createObjectURL(file)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e);
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
//неподходящий формат
|
||||||
|
setStatusModal("errorType");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
setStatusModal("errorSize");
|
||||||
|
}
|
||||||
|
|
||||||
//неподходящий формат
|
|
||||||
setStatusModal("errorType")
|
|
||||||
}
|
}
|
||||||
} else {
|
};
|
||||||
|
|
||||||
setStatusModal("errorSize")
|
return (
|
||||||
}
|
<>
|
||||||
|
<Box>
|
||||||
}
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Box>
|
|
||||||
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "20px",
|
|
||||||
maxWidth: answer?.split("|")[0] ? "640px" : "600px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{answer?.split("|")[0] && (
|
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}>
|
|
||||||
<Typography color={theme.palette.text.primary}>Вы загрузили:</Typography>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
padding: "5px 5px 5px 16px",
|
|
||||||
backgroundColor: theme.palette.primary.main,
|
|
||||||
borderRadius: "8px",
|
|
||||||
color: "#FFFFFF",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
overflow: "hidden",
|
|
||||||
gap: "15px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{answer?.split("|")[0]}</Typography>
|
|
||||||
<IconButton
|
|
||||||
sx={{ p: 0 }}
|
|
||||||
onClick={() => {
|
|
||||||
updateAnswer(currentQuestion.id, "");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CloseBold />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!answer?.split("|")[0] && (
|
|
||||||
<Box sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center"
|
|
||||||
}}>
|
|
||||||
<ButtonBase component="label" sx={{ justifyContent: "flex-start", width: "100%" }}>
|
|
||||||
<input
|
|
||||||
onChange={uploadFile}
|
|
||||||
hidden
|
|
||||||
accept={ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].join(",")}
|
|
||||||
multiple
|
|
||||||
type="file"
|
|
||||||
/>
|
|
||||||
<Box
|
<Box
|
||||||
onDragOver={(event: DragEvent<HTMLDivElement>) =>
|
sx={{
|
||||||
event.preventDefault()
|
display: "flex",
|
||||||
}
|
flexDirection: "column",
|
||||||
sx={{
|
width: "100%",
|
||||||
width: "100%",
|
marginTop: "20px",
|
||||||
height: isMobile ? undefined : "120px",
|
maxWidth: answer?.split("|")[0] ? "640px" : "600px",
|
||||||
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",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<UploadIcon />
|
{answer?.split("|")[0] && (
|
||||||
<Box>
|
<Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}>
|
||||||
<Typography
|
<Typography color={theme.palette.text.primary}>Вы загрузили:</Typography>
|
||||||
sx={{
|
<Box
|
||||||
color: "#9A9AAF",
|
sx={{
|
||||||
// color: theme.palette.grey2.main,
|
padding: "5px 5px 5px 16px",
|
||||||
fontWeight: 500,
|
backgroundColor: theme.palette.primary.main,
|
||||||
}}
|
borderRadius: "8px",
|
||||||
>
|
color: "#FFFFFF",
|
||||||
{
|
display: "flex",
|
||||||
UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type]
|
alignItems: "center",
|
||||||
.title
|
overflow: "hidden",
|
||||||
}
|
gap: "15px",
|
||||||
</Typography>
|
}}
|
||||||
<Typography
|
>
|
||||||
sx={{
|
<Typography
|
||||||
color: "#9A9AAF",
|
sx={{
|
||||||
// color: theme.palette.grey2.main,
|
whiteSpace: "nowrap",
|
||||||
fontSize: "16px",
|
textOverflow: "ellipsis",
|
||||||
lineHeight: "19px",
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{
|
{answer?.split("|")[0]}</Typography>
|
||||||
UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type]
|
<IconButton
|
||||||
.description
|
sx={{ p: 0 }}
|
||||||
}
|
onClick={() => {
|
||||||
</Typography>
|
updateAnswer(currentQuestion.id, "");
|
||||||
</Box>
|
}}
|
||||||
|
>
|
||||||
|
<CloseBold />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!answer?.split("|")[0] && (
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center"
|
||||||
|
}}>
|
||||||
|
<ButtonBase component="label" sx={{ justifyContent: "flex-start", width: "100%" }}>
|
||||||
|
<input
|
||||||
|
onChange={uploadFile}
|
||||||
|
hidden
|
||||||
|
accept={ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].join(",")}
|
||||||
|
multiple
|
||||||
|
type="file"
|
||||||
|
/>
|
||||||
|
<Box
|
||||||
|
onDragOver={(event: DragEvent<HTMLDivElement>) =>
|
||||||
|
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",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<UploadIcon />
|
||||||
|
<Box>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
color: "#9A9AAF",
|
||||||
|
// color: theme.palette.grey2.main,
|
||||||
|
fontWeight: 500,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type]
|
||||||
|
.title
|
||||||
|
}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
color: "#9A9AAF",
|
||||||
|
// color: theme.palette.grey2.main,
|
||||||
|
fontSize: "16px",
|
||||||
|
lineHeight: "19px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type]
|
||||||
|
.description
|
||||||
|
}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</ButtonBase>
|
||||||
|
<Info sx={{ width: "40px", height: "40px" }} color={theme.palette.primary.main} onClick={() => setStatusModal(currentQuestion.content.type)} />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{answer && currentQuestion.content.type === "picture" && (
|
||||||
|
<img
|
||||||
|
src={answer.split("|")[1]}
|
||||||
|
alt=""
|
||||||
|
style={{
|
||||||
|
marginTop: "15px",
|
||||||
|
maxWidth: "300px",
|
||||||
|
maxHeight: "300px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{answer && currentQuestion.content.type === "video" && (
|
||||||
|
<video
|
||||||
|
src={answer.split("|")[1]}
|
||||||
|
style={{
|
||||||
|
marginTop: "15px",
|
||||||
|
maxWidth: "300px",
|
||||||
|
maxHeight: "300px",
|
||||||
|
objectFit: "cover",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</ButtonBase>
|
|
||||||
<Info sx={{ width: "40px", height: "40px" }} color={theme.palette.primary.main} onClick={() => setStatusModal(currentQuestion.content.type)} />
|
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
<Modal
|
||||||
{answer && currentQuestion.content.type === "picture" && (
|
open={Boolean(statusModal)}
|
||||||
<img
|
onClose={() => setStatusModal("")}
|
||||||
src={answer.split("|")[1]}
|
>
|
||||||
alt=""
|
<Box sx={{
|
||||||
style={{
|
position: 'absolute',
|
||||||
marginTop: "15px",
|
top: '50%',
|
||||||
maxWidth: "300px",
|
left: '50%',
|
||||||
maxHeight: "300px",
|
transform: 'translate(-50%, -50%)',
|
||||||
}}
|
width: isMobile ? 300 : 400,
|
||||||
/>
|
bgcolor: 'background.paper',
|
||||||
)}
|
borderRadius: 3,
|
||||||
{answer && currentQuestion.content.type === "video" && (
|
boxShadow: 24,
|
||||||
<video
|
p: 4,
|
||||||
src={answer.split("|")[1]}
|
}}>
|
||||||
style={{
|
<CurrentModal status={statusModal} />
|
||||||
marginTop: "15px",
|
</Box>
|
||||||
maxWidth: "300px",
|
</Modal>
|
||||||
maxHeight: "300px",
|
</>
|
||||||
objectFit: "cover",
|
);
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Modal
|
|
||||||
open={Boolean(statusModal)}
|
|
||||||
onClose={() => setStatusModal("")}
|
|
||||||
>
|
|
||||||
<Box sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: '50%',
|
|
||||||
left: '50%',
|
|
||||||
transform: 'translate(-50%, -50%)',
|
|
||||||
width: isMobile ? 300 : 400,
|
|
||||||
bgcolor: 'background.paper',
|
|
||||||
borderRadius: 3,
|
|
||||||
boxShadow: 24,
|
|
||||||
p: 4,
|
|
||||||
}}>
|
|
||||||
<CurrentModal status={statusModal} />
|
|
||||||
</Box>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
Radio,
|
Radio,
|
||||||
useTheme,
|
useTheme,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@stores/quizView/store";
|
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 type { QuizQuestionImages } from "../../../model/questionTypes/images";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store"
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
type ImagesProps = {
|
type ImagesProps = {
|
||||||
currentQuestion: QuizQuestionImages;
|
currentQuestion: QuizQuestionImages;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Images = ({ currentQuestion }: ImagesProps) => {
|
export const Images = ({ currentQuestion }: ImagesProps) => {
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuizData();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.id
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
) ?? {};
|
) ?? {};
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
return (
|
||||||
|
<Box>
|
||||||
return (
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box>
|
<RadioGroup
|
||||||
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
name={currentQuestion.id}
|
||||||
<RadioGroup
|
value={currentQuestion.content.variants.findIndex(
|
||||||
name={currentQuestion.id}
|
({ id }) => answer === id
|
||||||
value={currentQuestion.content.variants.findIndex(
|
)}
|
||||||
({ id }) => answer === id
|
|
||||||
)}
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexWrap: "wrap",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
marginTop: "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "grid",
|
|
||||||
gap: "15px",
|
|
||||||
gridTemplateColumns: isTablet
|
|
||||||
? isMobile
|
|
||||||
? "repeat(1, 1fr)"
|
|
||||||
: "repeat(2, 1fr)"
|
|
||||||
: "repeat(3, 1fr)",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{currentQuestion.content.variants.map((variant, index) => (
|
|
||||||
<Box
|
|
||||||
key={index}
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
borderRadius: "5px",
|
|
||||||
border: `1px solid`,
|
|
||||||
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
|
||||||
}}
|
|
||||||
onClick={async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
await sendAnswer({
|
|
||||||
questionId: currentQuestion.id,
|
|
||||||
body: `${currentQuestion.content.variants[index].answer} <img style="width:100%; max-width:250px; max-height:250px" src="${currentQuestion.content.variants[index].extendedText}"/>`,
|
|
||||||
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("ответ не был засчитан")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
|
||||||
<Box sx={{ width: "100%", height: "300px" }}>
|
|
||||||
{variant.extendedText && (
|
|
||||||
<img
|
|
||||||
src={variant.extendedText}
|
|
||||||
alt=""
|
|
||||||
style={{
|
|
||||||
display: "block",
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
objectFit: "cover",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<FormControlLabel
|
|
||||||
key={variant.id}
|
|
||||||
sx={{
|
sx={{
|
||||||
textAlign: "center",
|
display: "flex",
|
||||||
color: theme.palette.text.primary,
|
flexWrap: "wrap",
|
||||||
marginTop: "10px",
|
flexDirection: "row",
|
||||||
marginLeft: 0,
|
justifyContent: "space-between",
|
||||||
padding: "10px",
|
marginTop: "20px",
|
||||||
"& .MuiFormControlLabel-label": {
|
|
||||||
wordBreak: "break-word",
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
value={index}
|
>
|
||||||
control={
|
<Box
|
||||||
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main} />} icon={<RadioIcon />} />
|
sx={{
|
||||||
}
|
display: "grid",
|
||||||
label={variant.answer}
|
gap: "15px",
|
||||||
/>
|
gridTemplateColumns: isTablet
|
||||||
</Box>
|
? isMobile
|
||||||
))}
|
? "repeat(1, 1fr)"
|
||||||
|
: "repeat(2, 1fr)"
|
||||||
|
: "repeat(3, 1fr)",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{currentQuestion.content.variants.map((variant, index) => (
|
||||||
|
<Box
|
||||||
|
key={index}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
borderRadius: "5px",
|
||||||
|
border: `1px solid`,
|
||||||
|
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||||
|
}}
|
||||||
|
onClick={async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
await sendAnswer({
|
||||||
|
questionId: currentQuestion.id,
|
||||||
|
body: `${currentQuestion.content.variants[index].answer} <img style="width:100%; max-width:250px; max-height:250px" src="${currentQuestion.content.variants[index].extendedText}"/>`,
|
||||||
|
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("ответ не был засчитан");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||||
|
<Box sx={{ width: "100%", height: "300px" }}>
|
||||||
|
{variant.extendedText && (
|
||||||
|
<img
|
||||||
|
src={variant.extendedText}
|
||||||
|
alt=""
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
objectFit: "cover",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<FormControlLabel
|
||||||
|
key={variant.id}
|
||||||
|
sx={{
|
||||||
|
textAlign: "center",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
marginTop: "10px",
|
||||||
|
marginLeft: 0,
|
||||||
|
padding: "10px",
|
||||||
|
"& .MuiFormControlLabel-label": {
|
||||||
|
wordBreak: "break-word",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
value={index}
|
||||||
|
control={
|
||||||
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main} />} icon={<RadioIcon />} />
|
||||||
|
}
|
||||||
|
label={variant.answer}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</RadioGroup>
|
||||||
</Box>
|
</Box>
|
||||||
</RadioGroup>
|
);
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -12,14 +12,14 @@ import { enqueueSnackbar } from "notistack";
|
|||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
|
|
||||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
type NumberProps = {
|
type NumberProps = {
|
||||||
currentQuestion: QuizQuestionNumber;
|
currentQuestion: QuizQuestionNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Number = ({ currentQuestion }: NumberProps) => {
|
export const Number = ({ currentQuestion }: NumberProps) => {
|
||||||
const { settings } = useQuestionsStore();
|
const { settings } = useQuizData();
|
||||||
const [inputValue, setInputValue] = useState<string>("0");
|
const [inputValue, setInputValue] = useState<string>("0");
|
||||||
const [minRange, setMinRange] = useState<string>("0");
|
const [minRange, setMinRange] = useState<string>("0");
|
||||||
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
||||||
@ -105,8 +105,6 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5" color={theme.palette.text.primary}>
|
<Typography variant="h5" color={theme.palette.text.primary}>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
Rating as RatingComponent,
|
Rating as RatingComponent,
|
||||||
useTheme,
|
useTheme,
|
||||||
useMediaQuery
|
useMediaQuery
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
|
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 type { QuizQuestionRating } from "../../../model/questionTypes/rating";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
type RatingProps = {
|
type RatingProps = {
|
||||||
currentQuestion: QuizQuestionRating;
|
currentQuestion: QuizQuestionRating;
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonRatingForm = [
|
const buttonRatingForm = [
|
||||||
{
|
{
|
||||||
name: "star",
|
name: "star",
|
||||||
icon: (color: string) => <StarIconMini width={50} color={color} />,
|
icon: (color: string) => <StarIconMini width={50} color={color} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "trophie",
|
name: "trophie",
|
||||||
icon: (color: string) => <TropfyIcon color={color} />,
|
icon: (color: string) => <TropfyIcon color={color} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "flag",
|
name: "flag",
|
||||||
icon: (color: string) => <FlagIcon color={color} />,
|
icon: (color: string) => <FlagIcon color={color} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "heart",
|
name: "heart",
|
||||||
icon: (color: string) => <HeartIcon color={color} />,
|
icon: (color: string) => <HeartIcon color={color} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "like",
|
name: "like",
|
||||||
icon: (color: string) => <LikeIcon color={color} />,
|
icon: (color: string) => <LikeIcon color={color} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bubble",
|
name: "bubble",
|
||||||
icon: (color: string) => <LightbulbIcon color={color} />,
|
icon: (color: string) => <LightbulbIcon color={color} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hashtag",
|
name: "hashtag",
|
||||||
icon: (color: string) => <HashtagIcon color={color} />,
|
icon: (color: string) => <HashtagIcon color={color} />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const Rating = ({ currentQuestion }: RatingProps) => {
|
export const Rating = ({ currentQuestion }: RatingProps) => {
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuizData();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.id
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
) ?? {};
|
) ?? {};
|
||||||
const form = buttonRatingForm.find(
|
const form = buttonRatingForm.find(
|
||||||
({ name }) => name === currentQuestion.content.form
|
({ name }) => name === currentQuestion.content.form
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
return (
|
||||||
|
<Box>
|
||||||
return (
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box>
|
<Box
|
||||||
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
sx={{
|
||||||
<Box
|
display: "inline-flex",
|
||||||
sx={{
|
alignItems: "center",
|
||||||
display: "inline-flex",
|
gap: "20px",
|
||||||
alignItems: "center",
|
marginTop: "20px",
|
||||||
gap: "20px",
|
flexDirection: "column",
|
||||||
marginTop: "20px",
|
width: isMobile ? "100%" : undefined,
|
||||||
flexDirection: "column",
|
}}
|
||||||
width: isMobile ? "100%" : undefined,
|
>
|
||||||
}}
|
<Box
|
||||||
>
|
sx={{
|
||||||
<Box
|
display: "inline-block",
|
||||||
sx={{
|
width: "100%",
|
||||||
display: "inline-block",
|
}}
|
||||||
width: "100%",
|
>
|
||||||
}}
|
<RatingComponent
|
||||||
>
|
value={Number(answer || 0)}
|
||||||
<RatingComponent
|
onChange={async (_, value) => {
|
||||||
value={Number(answer || 0)}
|
|
||||||
onChange={async (_, value) => {
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: String(value) + " из " + currentQuestion.content.steps,
|
body: String(value) + " из " + currentQuestion.content.steps,
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
});
|
||||||
|
|
||||||
updateAnswer(currentQuestion.id, String(value))
|
updateAnswer(currentQuestion.id, String(value));
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
height: "50px",
|
height: "50px",
|
||||||
gap: isMobile ? undefined : "15px",
|
gap: isMobile ? undefined : "15px",
|
||||||
justifyContent: isMobile ? "space-between" : undefined,
|
justifyContent: isMobile ? "space-between" : undefined,
|
||||||
width: isMobile ? "100%" : undefined
|
width: isMobile ? "100%" : undefined
|
||||||
}}
|
}}
|
||||||
max={currentQuestion.content.steps}
|
max={currentQuestion.content.steps}
|
||||||
icon={form?.icon(theme.palette.primary.main)}
|
icon={form?.icon(theme.palette.primary.main)}
|
||||||
emptyIcon={form?.icon("#9A9AAF")}
|
emptyIcon={form?.icon("#9A9AAF")}
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
gap: 2,
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography sx={{
|
||||||
|
color: "#9A9AAF"
|
||||||
|
// color: theme.palette.grey2.main
|
||||||
|
}}>
|
||||||
|
{currentQuestion.content.ratingNegativeDescription}
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ color: "#9A9AAF" }}>
|
||||||
|
{currentQuestion.content.ratingPositiveDescription}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
);
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
gap: 2,
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography sx={{
|
|
||||||
color: "#9A9AAF"
|
|
||||||
// color: theme.palette.grey2.main
|
|
||||||
}}>
|
|
||||||
{currentQuestion.content.ratingNegativeDescription}
|
|
||||||
</Typography>
|
|
||||||
<Typography sx={{ color: "#9A9AAF" }}>
|
|
||||||
{currentQuestion.content.ratingPositiveDescription}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -7,75 +7,73 @@ import { useQuizViewStore, updateAnswer, deleteAnswer } from "@stores/quizView/s
|
|||||||
import type { QuizQuestionSelect } from "../../../model/questionTypes/select";
|
import type { QuizQuestionSelect } from "../../../model/questionTypes/select";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
import { useQuestionsStore } from "@stores/quizData/store"
|
|
||||||
type SelectProps = {
|
type SelectProps = {
|
||||||
currentQuestion: QuizQuestionSelect;
|
currentQuestion: QuizQuestionSelect;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Select = ({ currentQuestion }: SelectProps) => {
|
export const Select = ({ currentQuestion }: SelectProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuizData();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.id
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
) ?? {};
|
) ?? {};
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
return (
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
marginTop: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectComponent
|
||||||
|
placeholder={currentQuestion.content.default}
|
||||||
|
activeItemIndex={answer ? Number(answer) : -1}
|
||||||
|
items={currentQuestion.content.variants.map(({ answer }) => answer)}
|
||||||
|
colorMain={theme.palette.primary.main}
|
||||||
|
onChange={async (_, value) => {
|
||||||
|
if (value < 0) {
|
||||||
|
deleteAnswer(currentQuestion.id);
|
||||||
|
try {
|
||||||
|
|
||||||
return (
|
await sendAnswer({
|
||||||
<Box>
|
questionId: currentQuestion.id,
|
||||||
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
body: "",
|
||||||
<Box
|
qid: settings.qid
|
||||||
sx={{
|
});
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectComponent
|
|
||||||
placeholder={currentQuestion.content.default}
|
|
||||||
activeItemIndex={answer ? Number(answer) : -1}
|
|
||||||
items={currentQuestion.content.variants.map(({ answer }) => 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
} catch (e) {
|
||||||
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
await sendAnswer({
|
}
|
||||||
questionId: currentQuestion.id,
|
return;
|
||||||
body: String(currentQuestion.content.variants[Number(value)].answer),
|
}
|
||||||
qid: settings.qid
|
|
||||||
})
|
|
||||||
|
|
||||||
updateAnswer(currentQuestion.id, String(value));
|
try {
|
||||||
|
|
||||||
} catch (e) {
|
await sendAnswer({
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
questionId: currentQuestion.id,
|
||||||
}
|
body: String(currentQuestion.content.variants[Number(value)].answer),
|
||||||
|
qid: settings.qid
|
||||||
|
});
|
||||||
|
|
||||||
|
updateAnswer(currentQuestion.id, String(value));
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -6,63 +6,61 @@ import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
|
|||||||
|
|
||||||
import type { QuizQuestionText } from "../../../model/questionTypes/text";
|
import type { QuizQuestionText } from "../../../model/questionTypes/text";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store"
|
|
||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
type TextProps = {
|
type TextProps = {
|
||||||
currentQuestion: QuizQuestionText;
|
currentQuestion: QuizQuestionText;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Text = ({ currentQuestion }: TextProps) => {
|
export const Text = ({ currentQuestion }: TextProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuizData();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||||
|
|
||||||
const inputHC = useDebouncedCallback(async (text) => {
|
const inputHC = useDebouncedCallback(async (text) => {
|
||||||
if (!settings) return;
|
try {
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: text,
|
body: text,
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
}
|
}
|
||||||
}, 400);
|
}, 400);
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
placeholder={currentQuestion.content.placeholder}
|
placeholder={currentQuestion.content.placeholder}
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
value={answer || ""}
|
value={answer || ""}
|
||||||
onChange={async ({ target }) => {
|
onChange={async ({ target }) => {
|
||||||
updateAnswer(currentQuestion.id, target.value)
|
updateAnswer(currentQuestion.id, target.value);
|
||||||
inputHC(target.value)
|
inputHC(target.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sx={{
|
sx={{
|
||||||
"&:focus-visible": {
|
"&:focus-visible": {
|
||||||
borderColor: theme.palette.primary.main
|
borderColor: theme.palette.primary.main
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -29,7 +29,7 @@ import { quizThemes } from "@utils/themes/Publication/themePublication";
|
|||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import type { QuestionVariant } from "../../../model/questionTypes/shared";
|
import type { QuestionVariant } from "../../../model/questionTypes/shared";
|
||||||
import type { QuizQuestionVariant } from "../../../model/questionTypes/variant";
|
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<TextFieldProps>;
|
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
|
||||||
|
|
||||||
@ -135,11 +135,9 @@ const VariantItem = ({
|
|||||||
index,
|
index,
|
||||||
own = false,
|
own = false,
|
||||||
}: VariantItemProps) => {
|
}: VariantItemProps) => {
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuizData();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
key={variant.id}
|
key={variant.id}
|
||||||
@ -195,7 +193,7 @@ const VariantItem = ({
|
|||||||
? currentAnswer?.filter((item) => item !== variantId)
|
? currentAnswer?.filter((item) => item !== variantId)
|
||||||
: [...currentAnswer, variantId],
|
: [...currentAnswer, variantId],
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
});
|
||||||
|
|
||||||
updateAnswer(
|
updateAnswer(
|
||||||
currentQuestion.id,
|
currentQuestion.id,
|
||||||
@ -205,7 +203,7 @@ const VariantItem = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -218,12 +216,12 @@ const VariantItem = ({
|
|||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: currentQuestion.content.variants[index].answer,
|
body: currentQuestion.content.variants[index].answer,
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
});
|
||||||
|
|
||||||
updateAnswer(currentQuestion.id, variantId);
|
updateAnswer(currentQuestion.id, variantId);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (answer === variantId) {
|
if (answer === variantId) {
|
||||||
@ -233,10 +231,10 @@ const VariantItem = ({
|
|||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: "",
|
body: "",
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
});
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
}
|
}
|
||||||
deleteAnswer(currentQuestion.id);
|
deleteAnswer(currentQuestion.id);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
Radio,
|
Radio,
|
||||||
useTheme,
|
useTheme,
|
||||||
useMediaQuery
|
useMediaQuery
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import gag from "./gag.png"
|
|
||||||
|
|
||||||
import { useQuestionsStore } from "@stores/quizData/store"
|
|
||||||
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@stores/quizView/store";
|
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@stores/quizView/store";
|
||||||
|
|
||||||
import RadioCheck from "@ui_kit/RadioCheck";
|
import RadioCheck from "@ui_kit/RadioCheck";
|
||||||
@ -20,147 +16,151 @@ import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg";
|
|||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
|
import BlankImage from "@icons/BlankImage";
|
||||||
|
import { useQuizData } from "@utils/hooks/useQuizData";
|
||||||
|
|
||||||
type VarimgProps = {
|
type VarimgProps = {
|
||||||
currentQuestion: QuizQuestionVarImg;
|
currentQuestion: QuizQuestionVarImg;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuizData();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
|
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.id
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
) ?? {};
|
) ?? {};
|
||||||
const variant = currentQuestion.content.variants.find(
|
const variant = currentQuestion.content.variants.find(
|
||||||
({ id }) => answer === id
|
({ id }) => answer === id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!settings) throw new Error("settings is null");
|
return (
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
marginTop: "20px",
|
||||||
|
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||||
|
gap: isMobile ? "30px" : undefined
|
||||||
|
|
||||||
return (
|
}}>
|
||||||
<Box>
|
<RadioGroup
|
||||||
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
name={currentQuestion.id}
|
||||||
<Box sx={{
|
value={currentQuestion.content.variants.findIndex(
|
||||||
display: "flex",
|
({ id }) => answer === id
|
||||||
marginTop: "20px",
|
)}
|
||||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
sx={{
|
||||||
gap: isMobile ? "30px" : undefined
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
flexBasis: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%", gap: isMobile ? "20px" : undefined }}>
|
||||||
|
{currentQuestion.content.variants.map((variant, index) => (
|
||||||
|
<FormControlLabel
|
||||||
|
key={variant.id}
|
||||||
|
sx={{
|
||||||
|
marginBottom: "15px",
|
||||||
|
borderRadius: "5px",
|
||||||
|
padding: "15px",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
backgroundColor: quizThemes[settings.cfg.theme].isLight ? "white" : theme.palette.background.default,
|
||||||
|
border: `1px solid`,
|
||||||
|
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||||
|
display: "flex",
|
||||||
|
margin: isMobile ? 0 : undefined,
|
||||||
|
"& .MuiFormControlLabel-label": {
|
||||||
|
wordBreak: "break-word"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={index}
|
||||||
|
onClick={async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
}}>
|
|
||||||
<RadioGroup
|
|
||||||
name={currentQuestion.id}
|
|
||||||
value={currentQuestion.content.variants.findIndex(
|
|
||||||
({ id }) => answer === id
|
|
||||||
)}
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexWrap: "wrap",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
flexBasis: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%", gap: isMobile ? "20px" : undefined }}>
|
|
||||||
{currentQuestion.content.variants.map((variant, index) => (
|
|
||||||
<FormControlLabel
|
|
||||||
key={variant.id}
|
|
||||||
sx={{
|
|
||||||
marginBottom: "15px",
|
|
||||||
borderRadius: "5px",
|
|
||||||
padding: "15px",
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
backgroundColor: quizThemes[settings.cfg.theme].isLight ? "white" : theme.palette.background.default,
|
|
||||||
border: `1px solid`,
|
|
||||||
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
|
||||||
display: "flex",
|
|
||||||
margin: isMobile ? 0 : undefined,
|
|
||||||
"& .MuiFormControlLabel-label": {
|
|
||||||
wordBreak: "break-word"
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={index}
|
|
||||||
onClick={async(event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
await sendAnswer({
|
try {
|
||||||
questionId: currentQuestion.id,
|
|
||||||
body: `${currentQuestion.content.variants[index].answer} <img style="width:100%; max-width:250px; max-height:250px" src="${currentQuestion.content.variants[index].extendedText}"/>`,
|
|
||||||
qid: settings.qid
|
|
||||||
})
|
|
||||||
|
|
||||||
updateAnswer(
|
|
||||||
currentQuestion.id,
|
|
||||||
currentQuestion.content.variants[index].id
|
|
||||||
);
|
|
||||||
|
|
||||||
} catch (e) {
|
await sendAnswer({
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
questionId: currentQuestion.id,
|
||||||
}
|
body: `${currentQuestion.content.variants[index].answer} <img style="width:100%; max-width:250px; max-height:250px" src="${currentQuestion.content.variants[index].extendedText}"/>`,
|
||||||
|
qid: settings.qid
|
||||||
|
});
|
||||||
|
|
||||||
|
updateAnswer(
|
||||||
if (answer === currentQuestion.content.variants[index].id) {
|
currentQuestion.id,
|
||||||
try {
|
currentQuestion.content.variants[index].id
|
||||||
|
);
|
||||||
await sendAnswer({
|
|
||||||
questionId: currentQuestion.id,
|
} catch (e) {
|
||||||
body: "",
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
qid: settings.qid
|
}
|
||||||
})
|
|
||||||
|
|
||||||
} catch (e) {
|
if (answer === currentQuestion.content.variants[index].id) {
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
try {
|
||||||
}
|
|
||||||
deleteAnswer(currentQuestion.id);
|
await sendAnswer({
|
||||||
}
|
questionId: currentQuestion.id,
|
||||||
}}
|
body: "",
|
||||||
control={
|
qid: settings.qid
|
||||||
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />} />
|
});
|
||||||
}
|
|
||||||
label={variant.answer}
|
} catch (e) {
|
||||||
/>
|
enqueueSnackbar("ответ не был засчитан");
|
||||||
))}
|
}
|
||||||
</Box>
|
deleteAnswer(currentQuestion.id);
|
||||||
</RadioGroup>
|
}
|
||||||
{/* {(variant?.extendedText || currentQuestion.content.back) && ( */}
|
}}
|
||||||
<Box
|
control={
|
||||||
sx={{
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main} />} icon={<RadioIcon />} />
|
||||||
maxWidth: "450px",
|
}
|
||||||
width: "100%",
|
label={variant.answer}
|
||||||
height: "450px",
|
/>
|
||||||
border: "1px solid #9A9AAF",
|
))}
|
||||||
borderRadius: "12px",
|
</Box>
|
||||||
overflow: "hidden",
|
</RadioGroup>
|
||||||
display: "flex",
|
{/* {(variant?.extendedText || currentQuestion.content.back) && ( */}
|
||||||
alignItems: "center",
|
<Box
|
||||||
justifyContent: "center",
|
sx={{
|
||||||
backgroundColor: "#9A9AAF12",
|
maxWidth: "450px",
|
||||||
color: "#9A9AAF"
|
width: "100%",
|
||||||
}}
|
height: "450px",
|
||||||
>
|
border: "1px solid #9A9AAF",
|
||||||
{answer ? (
|
borderRadius: "12px",
|
||||||
<img
|
overflow: "hidden",
|
||||||
src={variant?.extendedText || gag}
|
display: "flex",
|
||||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
alignItems: "center",
|
||||||
alt=""
|
justifyContent: "center",
|
||||||
/>
|
backgroundColor: "#9A9AAF12",
|
||||||
) : (currentQuestion.content.replText !== " " && currentQuestion.content.replText.length > 0) ? currentQuestion.content.replText : variant?.extendedText || isMobile ? (
|
color: "#9A9AAF"
|
||||||
"Выберите вариант ответа ниже"
|
}}
|
||||||
) : (
|
>
|
||||||
"Выберите вариант ответа слева"
|
{answer ? (
|
||||||
)}
|
variant?.extendedText ? (
|
||||||
|
<img
|
||||||
</Box>
|
src={variant?.extendedText}
|
||||||
{/* )} */}
|
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||||
</Box>
|
alt=""
|
||||||
</Box>
|
/>
|
||||||
);
|
) : (
|
||||||
|
<BlankImage />
|
||||||
|
)
|
||||||
|
) : (currentQuestion.content.replText !== " " && currentQuestion.content.replText.length > 0) ? currentQuestion.content.replText : variant?.extendedText || isMobile ? (
|
||||||
|
"Выберите вариант ответа ниже"
|
||||||
|
) : (
|
||||||
|
"Выберите вариант ответа слева"
|
||||||
|
)}
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
{/* )} */}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 5.9 KiB |
@ -2,62 +2,62 @@ import { ChangeEvent, useEffect, useRef, useState } from "react";
|
|||||||
import { Box, Button, Typography } from "@mui/material";
|
import { Box, Button, Typography } from "@mui/material";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
QuizQuestionFile,
|
QuizQuestionFile,
|
||||||
UploadFileType,
|
UploadFileType,
|
||||||
} from "model/questionTypes/file";
|
} from "@model/questionTypes/file";
|
||||||
|
|
||||||
export const UPLOAD_FILE_TYPES_MAP: Record<UploadFileType, string> = {
|
const UPLOAD_FILE_TYPES_MAP: Record<UploadFileType, string> = {
|
||||||
picture: "image/*",
|
picture: "image/*",
|
||||||
video: "video/*",
|
video: "video/*",
|
||||||
audio: "audio/*",
|
audio: "audio/*",
|
||||||
document:
|
document:
|
||||||
".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,.pdf",
|
".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,.pdf",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: QuizQuestionFile;
|
question: QuizQuestionFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function File({ question }: Props) {
|
export default function File({ question }: Props) {
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
const [file, setFile] = useState<File | null>(null);
|
const [file, setFile] = useState<File | null>(null);
|
||||||
const [acceptedType, setAcceptedType] = useState<any>(
|
const [acceptedType, setAcceptedType] = useState<any>(
|
||||||
UPLOAD_FILE_TYPES_MAP.picture
|
UPLOAD_FILE_TYPES_MAP.picture
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAcceptedType(UPLOAD_FILE_TYPES_MAP[question.content.type]);
|
setAcceptedType(UPLOAD_FILE_TYPES_MAP[question.content.type]);
|
||||||
}, [question.content.type]);
|
}, [question.content.type]);
|
||||||
|
|
||||||
function handleFileChange(event: ChangeEvent<HTMLInputElement>) {
|
function handleFileChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
if (!event.target.files?.[0]) return setFile(null);
|
if (!event.target.files?.[0]) return setFile(null);
|
||||||
setFile(event.target.files[0]);
|
setFile(event.target.files[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
alignItems: "start",
|
alignItems: "start",
|
||||||
gap: 1,
|
gap: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h6" data-cy="question-title">{question.title}</Typography>
|
<Typography variant="h6" data-cy="question-title">{question.title}</Typography>
|
||||||
<Button variant="contained" onClick={() => fileInputRef.current?.click()}>
|
<Button variant="contained" onClick={() => fileInputRef.current?.click()}>
|
||||||
Загрузить файл
|
Загрузить файл
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
type="file"
|
type="file"
|
||||||
accept={acceptedType}
|
accept={acceptedType}
|
||||||
data-cy="file-upload-input"
|
data-cy="file-upload-input"
|
||||||
style={{
|
style={{
|
||||||
display: "none",
|
display: "none",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
{file && <Typography data-cy="chosen-file-name">Выбран файл: {file.name}</Typography>}
|
{file && <Typography data-cy="chosen-file-name">Выбран файл: {file.name}</Typography>}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
@ -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<QuizDataStore>()(
|
|
||||||
devtools(
|
|
||||||
() => initialState,
|
|
||||||
{
|
|
||||||
name: "QuizDataStore",
|
|
||||||
enabled: import.meta.env.DEV,
|
|
||||||
trace: import.meta.env.DEV,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
43
src/utils/handleComponentError.ts
Normal file
43
src/utils/handleComponentError.ts
Normal file
@ -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<typeof setTimeout>;
|
||||||
|
|
||||||
|
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 = [];
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
34
src/utils/hooks/useQuizData.ts
Normal file
34
src/utils/hooks/useQuizData.ts
Normal file
@ -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;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
|
import WidgetApp from "./WidgetApp";
|
||||||
import { Root, createRoot } from "react-dom/client";
|
import { Root, createRoot } from "react-dom/client";
|
||||||
import App from "./App";
|
|
||||||
|
|
||||||
|
|
||||||
let root: Root | undefined = undefined;
|
let root: Root | undefined = undefined;
|
||||||
@ -14,7 +14,7 @@ const widget = {
|
|||||||
|
|
||||||
root = createRoot(element);
|
root = createRoot(element);
|
||||||
|
|
||||||
root.render(<App widget quizId={quizId} />);
|
root.render(<WidgetApp quizId={quizId} />);
|
||||||
},
|
},
|
||||||
unmount() {
|
unmount() {
|
||||||
if (root) root.unmount();
|
if (root) root.unmount();
|
||||||
|
@ -19,25 +19,24 @@
|
|||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"baseUrl": "./src",
|
|
||||||
"paths": {
|
"paths": {
|
||||||
"@ui_kit/*": [
|
"@ui_kit/*": [
|
||||||
"./ui_kit/*"
|
"./src/ui_kit/*"
|
||||||
],
|
],
|
||||||
"@icons/*": [
|
"@icons/*": [
|
||||||
"./assets/icons/*"
|
"./src/assets/icons/*"
|
||||||
],
|
],
|
||||||
"@stores/*": [
|
"@stores/*": [
|
||||||
"./stores/*"
|
"./src/stores/*"
|
||||||
],
|
],
|
||||||
"@api/*": [
|
"@api/*": [
|
||||||
"./api/*"
|
"./src/api/*"
|
||||||
],
|
],
|
||||||
"@model/*": [
|
"@model/*": [
|
||||||
"./model/*"
|
"./src/model/*"
|
||||||
],
|
],
|
||||||
"@utils/*": [
|
"@utils/*": [
|
||||||
"./utils/*"
|
"./src/utils/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
27
yarn.lock
27
yarn.lock
@ -737,6 +737,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
|
||||||
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
|
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
|
||||||
|
|
||||||
|
"@remix-run/router@1.14.2":
|
||||||
|
version "1.14.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.14.2.tgz#4d58f59908d9197ba3179310077f25c88e49ed17"
|
||||||
|
integrity sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg==
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi@4.9.5":
|
"@rollup/rollup-android-arm-eabi@4.9.5":
|
||||||
version "4.9.5"
|
version "4.9.5"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.5.tgz#b752b6c88a14ccfcbdf3f48c577ccc3a7f0e66b9"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.5.tgz#b752b6c88a14ccfcbdf3f48c577ccc3a7f0e66b9"
|
||||||
@ -2709,6 +2714,13 @@ react-dom@^18.2.0:
|
|||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
scheduler "^0.23.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:
|
react-is@^16.13.1, react-is@^16.7.0:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
@ -2724,6 +2736,21 @@ react-refresh@^0.14.0:
|
|||||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
|
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
|
||||||
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
|
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
|
||||||
|
|
||||||
|
react-router-dom@^6.21.3:
|
||||||
|
version "6.21.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.21.3.tgz#ef3a7956a3699c7b82c21fcb3dbc63c313ed8c5d"
|
||||||
|
integrity sha512-kNzubk7n4YHSrErzjLK72j0B5i969GsuCGazRl3G6j1zqZBLjuSlYBdVdkDOgzGdPIffUOc9nmgiadTEVoq91g==
|
||||||
|
dependencies:
|
||||||
|
"@remix-run/router" "1.14.2"
|
||||||
|
react-router "6.21.3"
|
||||||
|
|
||||||
|
react-router@6.21.3:
|
||||||
|
version "6.21.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.21.3.tgz#8086cea922c2bfebbb49c6594967418f1f167d70"
|
||||||
|
integrity sha512-a0H638ZXULv1OdkmiK6s6itNhoy33ywxmUFT/xtSoVyf9VnC7n7+VT4LjVzdIHSaF5TIh9ylUgxMXksHTgGrKg==
|
||||||
|
dependencies:
|
||||||
|
"@remix-run/router" "1.14.2"
|
||||||
|
|
||||||
react-transition-group@^4.4.5:
|
react-transition-group@^4.4.5:
|
||||||
version "4.4.5"
|
version "4.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
|
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
|
||||||
|
Loading…
Reference in New Issue
Block a user