fix some types
remove some ts-ignore's refactor stores
This commit is contained in:
parent
606472d182
commit
af1264a170
@ -1,7 +1,7 @@
|
|||||||
|
import { GetQuizDataResponse } from "@model/api/getQuizData";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
import type { AxiosError } from "axios";
|
import type { AxiosError } from "axios";
|
||||||
import type { GetDataResponse } from "../model/settingsData";
|
|
||||||
|
|
||||||
let SESSIONS = "";
|
let SESSIONS = "";
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ export const publicationMakeRequest = ({ url, body }: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function getData(quizId: string): Promise<{
|
export async function getData(quizId: string): Promise<{
|
||||||
data: GetDataResponse | null;
|
data: GetQuizDataResponse | null;
|
||||||
isRecentlyCompleted: boolean;
|
isRecentlyCompleted: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
}> {
|
}> {
|
||||||
@ -29,7 +29,7 @@ export async function getData(quizId: string): Promise<{
|
|||||||
: "ef836ff8-35b1-4031-9acf-af5766bac2b2";
|
: "ef836ff8-35b1-4031-9acf-af5766bac2b2";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data, headers } = await axios<GetDataResponse>(
|
const { data, headers } = await axios<GetQuizDataResponse>(
|
||||||
`https://s.hbpn.link/answer/settings`,
|
`https://s.hbpn.link/answer/settings`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
55
src/model/api/getQuizData.ts
Normal file
55
src/model/api/getQuizData.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
|
import { QuizSettings } from "@model/settingsData";
|
||||||
|
|
||||||
|
export interface GetQuizDataResponse {
|
||||||
|
cnt: number;
|
||||||
|
settings: {
|
||||||
|
fp: boolean;
|
||||||
|
rep: boolean;
|
||||||
|
name: string;
|
||||||
|
cfg: string;
|
||||||
|
lim: number;
|
||||||
|
due: number;
|
||||||
|
delay: number;
|
||||||
|
pausable: boolean;
|
||||||
|
};
|
||||||
|
items: {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
desc: string;
|
||||||
|
typ: string;
|
||||||
|
req: boolean;
|
||||||
|
p: number;
|
||||||
|
c: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseQuizData(quizDataResponse: GetQuizDataResponse, quizId: string): QuizSettings {
|
||||||
|
const items: QuizSettings["items"] = quizDataResponse.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
|
||||||
|
} as unknown as AnyTypedQuizQuestion;
|
||||||
|
});
|
||||||
|
|
||||||
|
const settings: QuizSettings["settings"] = {
|
||||||
|
qid: quizId,
|
||||||
|
fp: quizDataResponse.settings.fp,
|
||||||
|
rep: quizDataResponse.settings.rep,
|
||||||
|
name: quizDataResponse.settings.name,
|
||||||
|
cfg: JSON.parse(quizDataResponse?.settings.cfg),
|
||||||
|
lim: quizDataResponse.settings.lim,
|
||||||
|
due: quizDataResponse.settings.due,
|
||||||
|
delay: quizDataResponse.settings.delay,
|
||||||
|
pausable: quizDataResponse.settings.pausable
|
||||||
|
};
|
||||||
|
|
||||||
|
return { cnt: quizDataResponse.cnt, settings, items };
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
import { AnyTypedQuizQuestion } from "./questionTypes/shared";
|
||||||
// import { QuizConfig } from "@model/quizSettings";
|
|
||||||
|
|
||||||
export type QuizStartpageType = "standard" | "expanded" | "centered" | null;
|
export type QuizStartpageType = "standard" | "expanded" | "centered" | null;
|
||||||
|
|
||||||
@ -9,55 +8,50 @@ export type QuizType = "quiz" | "form";
|
|||||||
|
|
||||||
export type QuizResultsType = true | null;
|
export type QuizResultsType = true | null;
|
||||||
|
|
||||||
|
export type QuizTheme =
|
||||||
|
| "StandardTheme"
|
||||||
|
| "StandardDarkTheme"
|
||||||
|
| "PinkTheme"
|
||||||
|
| "PinkDarkTheme"
|
||||||
|
| "BlackWhiteTheme"
|
||||||
|
| "OliveTheme"
|
||||||
|
| "YellowTheme"
|
||||||
|
| "GoldDarkTheme"
|
||||||
|
| "PurpleTheme"
|
||||||
|
| "BlueTheme"
|
||||||
|
| "BlueDarkTheme";
|
||||||
|
|
||||||
export type FCField = {
|
export type FCField = {
|
||||||
text: string
|
text: string;
|
||||||
innerText: string
|
innerText: string;
|
||||||
key: string
|
key: string;
|
||||||
required: boolean
|
required: boolean;
|
||||||
used: boolean
|
used: boolean;
|
||||||
}
|
};
|
||||||
export interface GetDataResponse {
|
|
||||||
cnt: number;
|
export type QuizSettings = {
|
||||||
|
items: AnyTypedQuizQuestion[];
|
||||||
settings: {
|
settings: {
|
||||||
|
qid: string;
|
||||||
fp: boolean;
|
fp: boolean;
|
||||||
rep: boolean;
|
rep: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
cfg: string;
|
|
||||||
lim: number;
|
lim: number;
|
||||||
due: number;
|
due: number;
|
||||||
delay: number;
|
delay: number;
|
||||||
pausable: boolean;
|
pausable: boolean;
|
||||||
|
cfg: QuizConfig;
|
||||||
};
|
};
|
||||||
items: GetItems[];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type QuestionsStore = {
|
|
||||||
items: (AnyTypedQuizQuestion)[];
|
|
||||||
settings: Settings;
|
|
||||||
cnt: number;
|
cnt: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface Settings {
|
|
||||||
|
|
||||||
fp: boolean;
|
|
||||||
rep: boolean;
|
|
||||||
name: string;
|
|
||||||
lim: number;
|
|
||||||
due: number;
|
|
||||||
delay: number;
|
|
||||||
pausable: boolean;
|
|
||||||
cfg: QuizConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QuizConfig {
|
export interface QuizConfig {
|
||||||
type: QuizType;
|
type: QuizType;
|
||||||
noStartPage: boolean;
|
noStartPage: boolean;
|
||||||
startpageType: QuizStartpageType;
|
startpageType: QuizStartpageType;
|
||||||
results: QuizResultsType;
|
results: QuizResultsType;
|
||||||
haveRoot: string;
|
haveRoot: string;
|
||||||
theme: "StandardTheme" | "StandardDarkTheme" | "PinkTheme" | "PinkDarkTheme" | "BlackWhiteTheme" | "OliveTheme" | "YellowTheme" | "GoldDarkTheme" | "PurpleTheme" | "BlueTheme" | "BlueDarkTheme";
|
theme: QuizTheme;
|
||||||
resultInfo: {
|
resultInfo: {
|
||||||
when: "email" | "";
|
when: "email" | "";
|
||||||
share: boolean;
|
share: boolean;
|
||||||
@ -84,7 +78,6 @@ export interface QuizConfig {
|
|||||||
cycle: boolean;
|
cycle: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
formContact: {
|
formContact: {
|
||||||
title: string;
|
title: string;
|
||||||
desc: string;
|
desc: string;
|
||||||
@ -93,7 +86,7 @@ export interface QuizConfig {
|
|||||||
phone: FCField;
|
phone: FCField;
|
||||||
text: FCField;
|
text: FCField;
|
||||||
address: FCField;
|
address: FCField;
|
||||||
button: string
|
button: string;
|
||||||
};
|
};
|
||||||
info: {
|
info: {
|
||||||
phonenumber: string;
|
phonenumber: string;
|
||||||
@ -105,26 +98,12 @@ export interface QuizConfig {
|
|||||||
meta: string;
|
meta: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetItems {
|
|
||||||
id: number;
|
|
||||||
title: string;
|
|
||||||
desc: string;
|
|
||||||
typ: string;
|
|
||||||
req: boolean;
|
|
||||||
p: number;
|
|
||||||
c: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QuizItems {
|
export interface QuizItems {
|
||||||
|
description: string;
|
||||||
description: string;
|
id: number;
|
||||||
id: number;
|
page: number;
|
||||||
page: number;
|
required: boolean;
|
||||||
required: boolean;
|
title: string;
|
||||||
title: string;
|
type: string;
|
||||||
type: string;
|
content: unknown;
|
||||||
content: QuizItemsContent
|
}
|
||||||
}
|
|
||||||
export interface QuizItemsContent {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -1,379 +1,363 @@
|
|||||||
import { Box, Typography, Button, useMediaQuery, TextField, Link, InputAdornment, useTheme } from "@mui/material";
|
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 AddressIcon from "@icons/ContactFormIcon/AddressIcon";
|
import { Box, Button, InputAdornment, Link, TextField as MuiTextField, TextFieldProps, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
|
||||||
|
|
||||||
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { FC, useRef, useState } from "react";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
|
||||||
|
|
||||||
import { checkEmptyData } from "./tools/checkEmptyData";
|
|
||||||
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
|
||||||
import { enqueueSnackbar } from "notistack";
|
|
||||||
import { sendFC } from "@api/quizRelase";
|
import { sendFC } from "@api/quizRelase";
|
||||||
import { NameplateLogo } from "@icons/NameplateLogo";
|
import { NameplateLogo } from "@icons/NameplateLogo";
|
||||||
import { modes } from "../../utils/themes/Publication/themePublication";
|
|
||||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||||
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { ApologyPage } from "./ApologyPage";
|
import { ApologyPage } from "./ApologyPage";
|
||||||
|
import { checkEmptyData } from "./tools/checkEmptyData";
|
||||||
|
import { useQuestionsStore } from "@stores/quizData/store";
|
||||||
|
|
||||||
|
|
||||||
|
const TextField = MuiTextField as unknown as FC<TextFieldProps>; // temporary fix ts(2590)
|
||||||
|
|
||||||
const EMAIL_REGEXP = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/iu;
|
const EMAIL_REGEXP = /^(([^<>()[\].,:\s@"]+(\.[^<>()[\].,:\s@"]+)*)|(".+"))@(([^<>()[\].,:\s@"]+\.)+[^<>()[\].,:\s@"]{2,})$/iu;
|
||||||
|
|
||||||
|
type ContactType =
|
||||||
|
| "name"
|
||||||
|
| "email"
|
||||||
|
| "phone"
|
||||||
|
| "text"
|
||||||
|
| "adress";
|
||||||
|
|
||||||
type ContactFormProps = {
|
type ContactFormProps = {
|
||||||
currentQuestion: any;
|
currentQuestion: any;
|
||||||
showResultForm: boolean;
|
showResultForm: boolean;
|
||||||
setShowContactForm: (show: boolean) => void;
|
setShowContactForm: (show: boolean) => void;
|
||||||
setShowResultForm: (show: boolean) => void;
|
setShowResultForm: (show: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const icons = [
|
|
||||||
{ type: "name", icon: NameIcon, defaultText: "Введите имя", defaultTitle: "имя", backendName: "name" },
|
|
||||||
{ type: "email", icon: EmailIcon, defaultText: "Введите Email", defaultTitle: "Email", backendName: "email" },
|
|
||||||
{ type: "phone", icon: PhoneIcon, defaultText: "Введите номер телефона", defaultTitle: "номер телефона", backendName: "phone" },
|
|
||||||
{ type: "text", icon: TextIcon, defaultText: "Введите фамилию", defaultTitle: "фамилию", backendName: "adress" },
|
|
||||||
{ type: "address", icon: AddressIcon, defaultText: "Введите адрес", defaultTitle: "адрес", backendName: "adress" },
|
|
||||||
]
|
|
||||||
|
|
||||||
export const ContactForm = ({
|
export const ContactForm = ({
|
||||||
currentQuestion,
|
currentQuestion,
|
||||||
showResultForm,
|
showResultForm,
|
||||||
setShowContactForm,
|
setShowContactForm,
|
||||||
setShowResultForm,
|
setShowResultForm,
|
||||||
}: ContactFormProps) => {
|
}: ContactFormProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { settings, items } = useQuestionsStore()
|
const { settings, items } = useQuestionsStore()
|
||||||
|
|
||||||
const [ready, setReady] = useState(false)
|
const [ready, setReady] = useState(false)
|
||||||
const [name, setName] = useState("")
|
const [name, setName] = useState("")
|
||||||
const [email, setEmail] = useState("")
|
const [email, setEmail] = useState("")
|
||||||
const [phone, setPhone] = useState("")
|
const [phone, setPhone] = useState("")
|
||||||
const [text, setText] = useState("")
|
const [text, setText] = useState("")
|
||||||
const [adress, setAdress] = useState("")
|
const [adress, setAdress] = useState("")
|
||||||
|
|
||||||
const fireOnce = useRef(true)
|
const fireOnce = useRef(true)
|
||||||
const [fire, setFire] = useState(false)
|
const [fire, setFire] = useState(false)
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(850));
|
const isMobile = useMediaQuery(theme.breakpoints.down(850));
|
||||||
|
|
||||||
const followNextForm = () => {
|
const resultQuestion: QuizQuestionResult = items.find((question): question is QuizQuestionResult => {
|
||||||
setShowContactForm(false);
|
if (settings?.cfg.haveRoot) { //ветвимся
|
||||||
setShowResultForm(true);
|
return (
|
||||||
};
|
question.type === "result" &&
|
||||||
const mode = modes;
|
question.content.rule.parentId === currentQuestion.content.id
|
||||||
//@ts-ignore
|
);
|
||||||
const resultQuestion: QuizQuestionResult = items.find((question) => {
|
} else {// не ветвимся
|
||||||
if (settings?.cfg.haveRoot) { //ветвимся
|
return (
|
||||||
return (
|
question.type === "result" &&
|
||||||
question.type === "result" &&
|
question.content.rule.parentId === "line"
|
||||||
//@ts-ignore
|
);
|
||||||
question.content.rule.parentId === currentQuestion.content.id
|
}
|
||||||
)
|
})!;
|
||||||
} else {// не ветвимся
|
|
||||||
return (
|
const inputHC = async () => {
|
||||||
question.type === "result" &&
|
if (!settings) return;
|
||||||
question.content.rule.parentId === "line"
|
|
||||||
)
|
const body: Partial<Record<ContactType, string>> = {};
|
||||||
|
|
||||||
|
if (name.length > 0) body.name = name;
|
||||||
|
if (email.length > 0) body.email = email;
|
||||||
|
if (phone.length > 0) body.phone = phone;
|
||||||
|
if (text.length > 0) body.text = text;
|
||||||
|
if (adress.length > 0) body.adress = adress;
|
||||||
|
|
||||||
|
if (Object.keys(body).length > 0) {
|
||||||
|
try {
|
||||||
|
await sendFC({
|
||||||
|
questionId: resultQuestion?.id,
|
||||||
|
body: body,
|
||||||
|
qid: settings.qid
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
enqueueSnackbar("ответ не был засчитан")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const inputHC = async () => {
|
|
||||||
|
|
||||||
const body = {}
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
if (name.length > 0) body.name = name
|
const FCcopy: any = settings?.cfg.formContact.fields || settings?.cfg.formContact;
|
||||||
//@ts-ignore
|
|
||||||
if (email.length > 0) body.email = email
|
|
||||||
//@ts-ignore
|
|
||||||
if (phone.length > 0) body.phone = phone
|
|
||||||
//@ts-ignore
|
|
||||||
if (text.length > 0) body.text = text
|
|
||||||
//@ts-ignore
|
|
||||||
if (adress.length > 0) body.adress = adress
|
|
||||||
|
|
||||||
if (Object.keys(body).length > 0) {
|
|
||||||
try {
|
|
||||||
await sendFC({
|
|
||||||
questionId: resultQuestion?.id,
|
|
||||||
body: body,
|
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
|
||||||
})
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const filteredFC: any = {};
|
||||||
|
for (const i in FCcopy) {
|
||||||
|
const field = FCcopy[i];
|
||||||
|
console.log(filteredFC);
|
||||||
|
if (field.used) {
|
||||||
|
filteredFC[i] = field;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
const isWide = Object.keys(filteredFC).length > 2;
|
||||||
//@ts-ignore
|
|
||||||
let FCcopy: any = settings?.cfg.formContact.fields || settings?.cfg.formContact;
|
|
||||||
|
|
||||||
let filteredFC: any = {}
|
if (!settings) throw new Error("settings is null");
|
||||||
for (let i in FCcopy) {
|
if (!resultQuestion) return <ApologyPage message="не получилось найти результат для этой ветки :(" />
|
||||||
let field = FCcopy[i]
|
|
||||||
console.log(filteredFC)
|
|
||||||
if (field.used) {
|
|
||||||
filteredFC[i] = field
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let isWide = Object.keys(filteredFC).length > 2
|
|
||||||
console.log(isWide)
|
|
||||||
|
|
||||||
if (!resultQuestion) return <ApologyPage message="не получилось найти результат для этой ветки :(" />
|
return (
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
height: "100vh",
|
|
||||||
overflow: "auto",
|
|
||||||
"&::-webkit-scrollbar": { width: "0", display: "none", msOverflowStyle: "none" },
|
|
||||||
scrollbarWidth: "none",
|
|
||||||
msOverflowStyle: "none"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: isWide && !isMobile ? "100%" : (isMobile ? undefined : "530px"),
|
|
||||||
borderRadius: "4px",
|
|
||||||
height: "90vh",
|
|
||||||
display: isWide && !isMobile ? "flex" : undefined
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: isWide && !isMobile ? "100%" : undefined,
|
display: "flex",
|
||||||
display: "flex",
|
alignItems: "center",
|
||||||
flexDirection: "column",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
backgroundColor: theme.palette.background.default,
|
||||||
justifyContent: "center",
|
height: "100vh",
|
||||||
borderRight: isWide && !isMobile ? "1px solid gray" : undefined
|
overflow: "auto",
|
||||||
|
"&::-webkit-scrollbar": { width: "0", display: "none", msOverflowStyle: "none" },
|
||||||
|
scrollbarWidth: "none",
|
||||||
|
msOverflowStyle: "none"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
textAlign: "center",
|
width: isWide && !isMobile ? "100%" : (isMobile ? undefined : "530px"),
|
||||||
m: "20px 0",
|
borderRadius: "4px",
|
||||||
fontSize: "28px",
|
height: "90vh",
|
||||||
color: theme.palette.text.primary
|
display: isWide && !isMobile ? "flex" : undefined
|
||||||
}}
|
|
||||||
>
|
|
||||||
{settings?.cfg.formContact.title || "Заполните форму, чтобы получить результаты теста"}
|
|
||||||
|
|
||||||
</Typography>
|
|
||||||
{
|
|
||||||
settings?.cfg.formContact.desc &&
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
textAlign: "center",
|
|
||||||
m: "20px 0",
|
|
||||||
fontSize: "18px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{settings?.cfg.formContact.desc}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
flexDirection: "column",
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
p: "30px"
|
|
||||||
}}>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
my: "20px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Inputs
|
|
||||||
name={name} setName={setName}
|
|
||||||
email={email} setEmail={setEmail}
|
|
||||||
phone={phone} setPhone={setPhone}
|
|
||||||
text={text} setText={setText}
|
|
||||||
adress={adress} setAdress={setAdress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{
|
|
||||||
// resultQuestion &&
|
|
||||||
// settings?.cfg.resultInfo.when === "after" &&
|
|
||||||
(
|
|
||||||
<Button
|
|
||||||
disabled={!(ready && !fire)}
|
|
||||||
variant="contained"
|
|
||||||
onClick={async () => {
|
|
||||||
//@ts-ignore
|
|
||||||
const FC: any = settings?.cfg.formContact.fields || settings?.cfg.formContact
|
|
||||||
if (FC["email"].used === EMAIL_REGEXP.test(email)) {//почта валидна
|
|
||||||
setFire(true)
|
|
||||||
|
|
||||||
|
|
||||||
if (fireOnce.current) {
|
|
||||||
if (
|
|
||||||
name.length > 0 ||
|
|
||||||
email.length > 0 ||
|
|
||||||
phone.length > 0 ||
|
|
||||||
text.length > 0 ||
|
|
||||||
adress.length > 0
|
|
||||||
) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
await inputHC()
|
|
||||||
fireOnce.current = false
|
|
||||||
enqueueSnackbar("Данные успешно отправлены")
|
|
||||||
} catch (e) {
|
|
||||||
enqueueSnackbar("повторите попытку позже")
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((settings?.cfg.resultInfo.showResultForm === "after" || settings?.cfg.resultInfo.when === "email") && !checkEmptyData({ resultData: resultQuestion })) {
|
|
||||||
setShowContactForm(false)
|
|
||||||
setShowResultForm(true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
enqueueSnackbar("Пожалуйста, заполните поля")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setFire(false)
|
|
||||||
} else {
|
|
||||||
enqueueSnackbar("введена некорректная почта")
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{settings?.cfg.formContact?.button || "Получить результаты"}
|
<Box
|
||||||
</Button>
|
sx={{
|
||||||
)}
|
width: isWide && !isMobile ? "100%" : undefined,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
borderRight: isWide && !isMobile ? "1px solid gray" : undefined
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
textAlign: "center",
|
||||||
|
m: "20px 0",
|
||||||
|
fontSize: "28px",
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{settings?.cfg.formContact.title || "Заполните форму, чтобы получить результаты теста"}
|
||||||
|
|
||||||
<Box
|
</Typography>
|
||||||
sx={{
|
{
|
||||||
display: "flex",
|
settings?.cfg.formContact.desc &&
|
||||||
mt: "20px",
|
<Typography
|
||||||
width: isMobile ? "300px" : "450px",
|
sx={{
|
||||||
}}
|
color: theme.palette.text.primary,
|
||||||
>
|
textAlign: "center",
|
||||||
<CustomCheckbox label="" handleChange={({ target }) => { setReady(target.checked) }} checked={ready} colorIcon={theme.palette.primary.main} />
|
m: "20px 0",
|
||||||
<Typography sx={{ color: theme.palette.text.primary }}>
|
fontSize: "18px"
|
||||||
С 
|
}}
|
||||||
<Link href={"https://shub.pena.digital/ppdd"} target="_blank">
|
>
|
||||||
Положением об обработке персональных данных </Link>
|
{settings?.cfg.formContact.desc}
|
||||||
 и 
|
</Typography>
|
||||||
<Link href={"https://shub.pena.digital/docs/privacy"} target="_blank"> Политикой конфиденциальности </Link>
|
}
|
||||||
 ознакомлен
|
</Box>
|
||||||
</Typography>
|
|
||||||
|
|
||||||
</Box>
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
mt: "20px",
|
justifyContent: "center",
|
||||||
gap: "15px"
|
flexDirection: "column",
|
||||||
}}
|
backgroundColor: theme.palette.background.default,
|
||||||
>
|
p: "30px"
|
||||||
<NameplateLogo style={{
|
}}>
|
||||||
fontSize: "34px",
|
|
||||||
//@ts-ignore
|
<Box
|
||||||
color: mode[settings.cfg.theme] ? "#151515" : "#FFFFFF"
|
sx={{
|
||||||
}} />
|
display: "flex",
|
||||||
<Typography sx={{
|
flexDirection: "column",
|
||||||
fontSize: "20px",
|
my: "20px"
|
||||||
//@ts-ignore
|
}}
|
||||||
color: mode[settings.cfg.theme] ? "#4D4D4D" : "#F5F7FF", whiteSpace: "nowrap"
|
>
|
||||||
}}>
|
<Inputs
|
||||||
Сделано на PenaQuiz
|
name={name} setName={setName}
|
||||||
</Typography>
|
email={email} setEmail={setEmail}
|
||||||
</Box>
|
phone={phone} setPhone={setPhone}
|
||||||
</Box>
|
text={text} setText={setText}
|
||||||
</Box >
|
adress={adress} setAdress={setAdress}
|
||||||
</Box >
|
/>
|
||||||
);
|
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{
|
||||||
|
// resultQuestion &&
|
||||||
|
// settings?.cfg.resultInfo.when === "after" &&
|
||||||
|
(
|
||||||
|
<Button
|
||||||
|
disabled={!(ready && !fire)}
|
||||||
|
variant="contained"
|
||||||
|
onClick={async () => {
|
||||||
|
//@ts-ignore
|
||||||
|
const FC: any = settings?.cfg.formContact.fields || settings?.cfg.formContact
|
||||||
|
if (FC["email"].used === EMAIL_REGEXP.test(email)) {//почта валидна
|
||||||
|
setFire(true)
|
||||||
|
|
||||||
|
|
||||||
|
if (fireOnce.current) {
|
||||||
|
if (
|
||||||
|
name.length > 0 ||
|
||||||
|
email.length > 0 ||
|
||||||
|
phone.length > 0 ||
|
||||||
|
text.length > 0 ||
|
||||||
|
adress.length > 0
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
await inputHC()
|
||||||
|
fireOnce.current = false
|
||||||
|
enqueueSnackbar("Данные успешно отправлены")
|
||||||
|
} catch (e) {
|
||||||
|
enqueueSnackbar("повторите попытку позже")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((settings?.cfg.resultInfo.showResultForm === "after" || settings?.cfg.resultInfo.when === "email") && !checkEmptyData({ resultData: resultQuestion })) {
|
||||||
|
setShowContactForm(false)
|
||||||
|
setShowResultForm(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
enqueueSnackbar("Пожалуйста, заполните поля")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFire(false)
|
||||||
|
} else {
|
||||||
|
enqueueSnackbar("введена некорректная почта")
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{settings?.cfg.formContact?.button || "Получить результаты"}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
mt: "20px",
|
||||||
|
width: isMobile ? "300px" : "450px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CustomCheckbox label="" handleChange={({ target }) => { setReady(target.checked) }} checked={ready} colorIcon={theme.palette.primary.main} />
|
||||||
|
<Typography sx={{ color: theme.palette.text.primary }}>
|
||||||
|
С 
|
||||||
|
<Link href={"https://shub.pena.digital/ppdd"} target="_blank">
|
||||||
|
Положением об обработке персональных данных </Link>
|
||||||
|
 и 
|
||||||
|
<Link href={"https://shub.pena.digital/docs/privacy"} target="_blank"> Политикой конфиденциальности </Link>
|
||||||
|
 ознакомлен
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
mt: "20px",
|
||||||
|
gap: "15px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NameplateLogo style={{
|
||||||
|
fontSize: "34px",
|
||||||
|
color: quizThemes[settings.cfg.theme].isLight ? "#151515" : "#FFFFFF"
|
||||||
|
}} />
|
||||||
|
<Typography sx={{
|
||||||
|
fontSize: "20px",
|
||||||
|
color: quizThemes[settings.cfg.theme].isLight ? "#4D4D4D" : "#F5F7FF", whiteSpace: "nowrap"
|
||||||
|
}}>
|
||||||
|
Сделано на PenaQuiz
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box >
|
||||||
|
</Box >
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Inputs = ({
|
const Inputs = ({
|
||||||
name, setName,
|
name, setName,
|
||||||
email, setEmail,
|
email, setEmail,
|
||||||
phone, setPhone,
|
phone, setPhone,
|
||||||
text, setText,
|
text, setText,
|
||||||
adress, setAdress
|
adress, setAdress
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const { settings, items } = useQuestionsStore()
|
const { settings } = useQuestionsStore()
|
||||||
|
|
||||||
console.log("______________________EMAIL_REGEXP.test(email)")
|
|
||||||
console.log(EMAIL_REGEXP.test(email))
|
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const FC: any = settings?.cfg.formContact.fields || settings?.cfg.formContact
|
const FC: any = settings?.cfg.formContact.fields || settings?.cfg.formContact
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const Name = <CustomInput onChange={({ target }) => setName(target.value)} id={name} title={FC["name"].innerText || "Введите имя"} desc={FC["name"].text || "имя"} Icon={NameIcon} />
|
const Name = <CustomInput onChange={({ target }) => setName(target.value)} id={name} title={FC["name"].innerText || "Введите имя"} desc={FC["name"].text || "имя"} Icon={NameIcon} />
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const Email = <CustomInput
|
const Email = <CustomInput
|
||||||
|
|
||||||
error = {!EMAIL_REGEXP.test(email)}
|
error = {!EMAIL_REGEXP.test(email)}
|
||||||
label={!EMAIL_REGEXP.test(email) ? "" : "Некорректная почта"}
|
label={!EMAIL_REGEXP.test(email) ? "" : "Некорректная почта"}
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
onChange={({ target }) => setEmail(target.value)} id={email} title={FC["email"].innerText || "Введите Email"} desc={FC["email"].text || "Email"} Icon={EmailIcon} />
|
onChange={({ target }) => setEmail(target.value)} id={email} title={FC["email"].innerText || "Введите Email"} desc={FC["email"].text || "Email"} Icon={EmailIcon} />
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const Phone = <CustomInput onChange={({ target }) => setPhone(target.value)} id={phone} title={FC["phone"].innerText || "Введите номер телефона"} desc={FC["phone"].text || "номер телефона"} Icon={PhoneIcon} />
|
const Phone = <CustomInput onChange={({ target }) => setPhone(target.value)} id={phone} title={FC["phone"].innerText || "Введите номер телефона"} desc={FC["phone"].text || "номер телефона"} Icon={PhoneIcon} />
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const Text = <CustomInput onChange={({ target }) => setText(target.value)} id={text} title={FC["text"].innerText || "Введите фамилию"} desc={FC["text"].text || "фамилию"} Icon={TextIcon} />
|
const Text = <CustomInput onChange={({ target }) => setText(target.value)} id={text} title={FC["text"].innerText || "Введите фамилию"} desc={FC["text"].text || "фамилию"} Icon={TextIcon} />
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const Adress = <CustomInput onChange={({ target }) => setAdress(target.value)} id={adress} title={FC["address"].innerText || "Введите адрес"} desc={FC["address"].text || "адрес"} Icon={AddressIcon} />
|
const Adress = <CustomInput onChange={({ target }) => setAdress(target.value)} id={adress} title={FC["address"].innerText || "Введите адрес"} desc={FC["address"].text || "адрес"} Icon={AddressIcon} />
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
if (Object.values(FC).some((data) => data.used)) {
|
if (Object.values(FC).some((data) => data.used)) {
|
||||||
return <>
|
return <>
|
||||||
{FC["name"].used ? Name : <></>}
|
{FC["name"].used ? Name : <></>}
|
||||||
{FC["email"].used ? Email : <></>}
|
{FC["email"].used ? Email : <></>}
|
||||||
{FC["phone"].used ? Phone : <></>}
|
{FC["phone"].used ? Phone : <></>}
|
||||||
{FC["text"].used ? Text : <></>}
|
{FC["text"].used ? Text : <></>}
|
||||||
{FC["address"].used ? Adress : <></>}
|
{FC["address"].used ? Adress : <></>}
|
||||||
</>
|
</>
|
||||||
} else {
|
} else {
|
||||||
return <>
|
return <>
|
||||||
{Name}
|
{Name}
|
||||||
{Email}
|
{Email}
|
||||||
{Phone}
|
{Phone}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomInput = ({ title, desc, Icon, onChange }: any) => {
|
const CustomInput = ({ title, desc, Icon, onChange }: any) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
//@ts-ignore
|
|
||||||
return <Box m="15px 0">
|
|
||||||
<Typography mb="7px" color={theme.palette.text.primary}>{title}</Typography>
|
|
||||||
|
|
||||||
<TextField
|
return (
|
||||||
onChange={onChange}
|
<Box m="15px 0">
|
||||||
sx={{
|
<Typography mb="7px" color={theme.palette.text.primary}>{title}</Typography>
|
||||||
width: isMobile ? "300px" : "350px",
|
|
||||||
}}
|
<TextField
|
||||||
placeholder={desc}
|
onChange={onChange}
|
||||||
InputProps={{
|
sx={{
|
||||||
startAdornment: <InputAdornment position="start"><Icon color="gray" /></InputAdornment>,
|
width: isMobile ? "300px" : "350px",
|
||||||
}}
|
}}
|
||||||
/>
|
placeholder={desc}
|
||||||
</Box>
|
InputProps={{
|
||||||
|
startAdornment: <InputAdornment position="start"><Icon color="gray" /></InputAdornment>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,322 +1,298 @@
|
|||||||
import { useState, useEffect } from "react";
|
|
||||||
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 type { AnyTypedQuizQuestion, QuizQuestionBase } from "../../model/questionTypes/shared";
|
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
|
||||||
import { getQuestionById } from "@stores/quizData/actions";
|
import { getQuestionById } from "@stores/quizData/actions";
|
||||||
import { useQuizViewStore } from "@stores/quizView/store";
|
|
||||||
import { enqueueSnackbar } from "notistack";
|
|
||||||
import { NameplateLogoFQ } from "@icons/NameplateLogoFQ";
|
|
||||||
import { NameplateLogoFQDark } from "@icons/NameplateLogoFQDark";
|
|
||||||
|
|
||||||
import { modes } from "../../utils/themes/Publication/themePublication";
|
import { enqueueSnackbar } from "notistack";
|
||||||
|
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";
|
||||||
|
|
||||||
type FooterProps = {
|
type FooterProps = {
|
||||||
setCurrentQuestion: (step: AnyTypedQuizQuestion) => void;
|
setCurrentQuestion: (step: AnyTypedQuizQuestion) => void;
|
||||||
question: AnyTypedQuizQuestion;
|
question: AnyTypedQuizQuestion;
|
||||||
setShowContactForm: (show: boolean) => void;
|
setShowContactForm: (show: boolean) => void;
|
||||||
setShowResultForm: (show: boolean) => void;
|
setShowResultForm: (show: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
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, items } = useQuestionsStore();
|
||||||
const { answers } = useQuizViewStore();
|
const answers = useQuizViewStore(state => state.answers);
|
||||||
|
|
||||||
const mode = modes;
|
const [stepNumber, setStepNumber] = useState(1);
|
||||||
|
|
||||||
const [stepNumber, setStepNumber] = useState(1);
|
const isMobileMini = useMediaQuery(theme.breakpoints.down(382));
|
||||||
const [disablePreviousButton, setDisablePreviousButton] = useState<boolean>(false);
|
const isLinear = !items.some(({ content }) => content.rule.parentId === "root");
|
||||||
const [disableNextButton, setDisableNextButton] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const isMobileMini = useMediaQuery(theme.breakpoints.down(382));
|
const getNextQuestionId = useCallback(() => {
|
||||||
const linear = !items.find(({ content }) => content.rule.parentId === "root");
|
console.log("Смотрим какой вопрос будет дальше. Что у нас сегодня вкусненького? Щя покажу от какого вопроса мы ищем следующий шаг");
|
||||||
|
console.log(question);
|
||||||
|
console.log("От вот этого /|");
|
||||||
|
let readyBeNextQuestion = "";
|
||||||
|
|
||||||
useEffect(() => {
|
//вопрос обязателен, анализируем ответ и условия ветвления
|
||||||
// Логика для аргумента disabled у кнопки "Назад"
|
if (answers.length) {
|
||||||
if (linear) {
|
const answer = answers.find(({ questionId }) => questionId === question.id);
|
||||||
const questionIndex = items.findIndex(({ id }) => id === question.id);
|
|
||||||
|
|
||||||
const previousQuestion = items[questionIndex - 1];
|
|
||||||
|
|
||||||
if (previousQuestion) {
|
(question as QuizQuestionBase).content.rule.main.forEach(({ next, rules }) => {
|
||||||
setDisablePreviousButton(false);
|
const longerArray = Math.max(
|
||||||
} else {
|
rules[0].answers.length,
|
||||||
setDisablePreviousButton(true);
|
answer?.answer && Array.isArray(answer?.answer) ? answer?.answer.length : [answer?.answer].length
|
||||||
}
|
);
|
||||||
} else {
|
|
||||||
if (question?.content.rule.parentId === "root") {
|
|
||||||
setDisablePreviousButton(true);
|
|
||||||
} else {
|
|
||||||
setDisablePreviousButton(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Логика для аргумента disabled у кнопки "Далее"
|
for (
|
||||||
const answer = answers.find(({ questionId }) => questionId === question.id);
|
let i = 0;
|
||||||
|
i < longerArray;
|
||||||
|
i++ // Цикл по всем элементам бОльшего массива
|
||||||
|
) {
|
||||||
|
if (Array.isArray(answer?.answer)) {
|
||||||
|
if (answer?.answer.find((item) => String(item === rules[0].answers[i]))) {
|
||||||
|
readyBeNextQuestion = next; // Если хоть один элемент отличается, массивы не равны
|
||||||
|
}
|
||||||
|
|
||||||
if ("required" in question.content && question.content.required && answer) {
|
return;
|
||||||
setDisableNextButton(false);
|
}
|
||||||
|
|
||||||
return;
|
if (String(rules[0].answers[i]) === answer?.answer) {
|
||||||
}
|
readyBeNextQuestion = next; // Если хоть один элемент отличается, массивы не равны
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if ("required" in question.content && question.content.required && !answer) {
|
if (readyBeNextQuestion) return readyBeNextQuestion;
|
||||||
setDisableNextButton(true);
|
}
|
||||||
|
|
||||||
return;
|
if (!question.required) {//вопрос не обязателен и не нашли совпадений между ответами и условиями ветвления
|
||||||
}
|
console.log("вопрос не обязателен ищем дальше");
|
||||||
|
const defaultQ = question.content.rule.default;
|
||||||
|
if (defaultQ.length > 1 && defaultQ !== " ") return defaultQ;
|
||||||
|
//Вопросы типа страница, ползунок, своё поле для ввода и дата не могут иметь больше 1 ребёнка. Пользователь не может настроить там дефолт
|
||||||
|
//Кинуть на ребёнка надо даже если там нет дефолта
|
||||||
|
if (
|
||||||
|
(question?.type === "date" ||
|
||||||
|
question?.type === "text" ||
|
||||||
|
question?.type === "number" ||
|
||||||
|
question?.type === "page") && question.content.rule.children.length === 1
|
||||||
|
) return question.content.rule.children[0];
|
||||||
|
|
||||||
if (linear) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextQuestionId = getNextQuestionId();
|
|
||||||
if (nextQuestionId) {
|
|
||||||
setDisableNextButton(false);
|
|
||||||
} else {
|
|
||||||
const nextQuestion = getQuestionById(question.content.rule.default);
|
|
||||||
|
|
||||||
if (nextQuestion?.type) {
|
|
||||||
setDisableNextButton(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [question, answers]);
|
|
||||||
|
|
||||||
const showResult = (nextQuestion: QuizQuestionResult) => {
|
|
||||||
const isEmpty = checkEmptyData({ resultData: nextQuestion })
|
|
||||||
console.log("isEmpty", isEmpty)
|
|
||||||
|
|
||||||
if (nextQuestion) {
|
|
||||||
if (nextQuestion && settings?.cfg.resultInfo.showResultForm === "before") {
|
|
||||||
if (isEmpty) {
|
|
||||||
setShowContactForm(true); //до+пустая = кидать на ФК
|
|
||||||
|
|
||||||
} else {
|
|
||||||
setShowResultForm(true); //до+заполнена = показать
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
//ничё не нашли, ищем резулт
|
||||||
if (nextQuestion && settings?.cfg.resultInfo.showResultForm === "after") {
|
console.log("ничё не нашли, ищем резулт ");
|
||||||
if (isEmpty) {
|
return items.find(q => {
|
||||||
setShowContactForm(true); //после+пустая
|
console.log('q.type === "result"', q.type === "result");
|
||||||
|
console.log('q.content.rule.parentId', q.content.rule.parentId);
|
||||||
|
console.log('question.content.id', question.content.id);
|
||||||
|
return q.type === "result" && q.content.rule.parentId === question.content.id;
|
||||||
|
})?.id;
|
||||||
|
|
||||||
|
}, [answers, items, question]);
|
||||||
|
|
||||||
|
const isPreviousButtonDisabled = useMemo(() => {
|
||||||
|
// Логика для аргумента disabled у кнопки "Назад"
|
||||||
|
if (isLinear) {
|
||||||
|
const questionIndex = items.findIndex(({ id }) => id === question.id);
|
||||||
|
|
||||||
|
const previousQuestion = items[questionIndex - 1];
|
||||||
|
|
||||||
|
return previousQuestion ? false : true;
|
||||||
} else {
|
} else {
|
||||||
setShowContactForm(true); //после+заполнена = показать ФК
|
return question?.content.rule.parentId === "root" ? true : false;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}, [items, isLinear, question?.content.rule.parentId, question.id]);
|
||||||
}
|
|
||||||
|
|
||||||
};
|
const isNextButtonDisabled = useMemo(() => {
|
||||||
|
// Логика для аргумента disabled у кнопки "Далее"
|
||||||
|
const answer = answers.find(({ questionId }) => questionId === question.id);
|
||||||
|
|
||||||
const getNextQuestionId = () => {
|
if ("required" in question.content && question.content.required && answer) {
|
||||||
console.log("Смотрим какой вопрос будет дальше. Что у нас сегодня вкусненького? Щя покажу от какого вопроса мы ищем следующий шаг")
|
return false;
|
||||||
console.log(question)
|
}
|
||||||
console.log("От вот этого /|")
|
|
||||||
let readyBeNextQuestion = "";
|
|
||||||
|
|
||||||
//вопрос обязателен, анализируем ответ и условия ветвления
|
if ("required" in question.content && question.content.required && !answer) {
|
||||||
if (answers.length) {
|
return true;
|
||||||
const answer = answers.find(({ questionId }) => questionId === question.id);
|
}
|
||||||
|
|
||||||
|
if (isLinear) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
(question as QuizQuestionBase).content.rule.main.forEach(({ next, rules }) => {
|
const nextQuestionId = getNextQuestionId();
|
||||||
let longerArray = Math.max(
|
if (nextQuestionId) {
|
||||||
rules[0].answers.length,
|
return false;
|
||||||
answer?.answer && Array.isArray(answer?.answer) ? answer?.answer.length : [answer?.answer].length
|
} else {
|
||||||
);
|
const nextQuestion = getQuestionById(question.content.rule.default);
|
||||||
|
|
||||||
for (
|
if (nextQuestion?.type) {
|
||||||
var i = 0;
|
return false;
|
||||||
i < longerArray;
|
}
|
||||||
i++ // Цикл по всем элементам бОльшего массива
|
}
|
||||||
) {
|
}, [answers, getNextQuestionId, isLinear, question.content, question.id]);
|
||||||
if (Array.isArray(answer?.answer)) {
|
|
||||||
if (answer?.answer.find((item) => String(item === rules[0].answers[i]))) {
|
const showResult = (nextQuestion: QuizQuestionResult) => {
|
||||||
readyBeNextQuestion = next; // Если хоть один элемент отличается, массивы не равны
|
if (!settings) return;
|
||||||
|
if (!nextQuestion) return;
|
||||||
|
|
||||||
|
const isEmpty = checkEmptyData({ resultData: nextQuestion });
|
||||||
|
|
||||||
|
if (nextQuestion) {
|
||||||
|
if (nextQuestion && settings?.cfg.resultInfo.showResultForm === "before") {
|
||||||
|
if (isEmpty) {
|
||||||
|
setShowContactForm(true); //до+пустая = кидать на ФК
|
||||||
|
|
||||||
|
} else {
|
||||||
|
setShowResultForm(true); //до+заполнена = показать
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextQuestion && settings?.cfg.resultInfo.showResultForm === "after") {
|
||||||
|
if (isEmpty) {
|
||||||
|
setShowContactForm(true); //после+пустая
|
||||||
|
|
||||||
|
} else {
|
||||||
|
setShowContactForm(true); //после+заполнена = показать ФК
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const followPreviousStep = () => {
|
||||||
|
if (isLinear) {
|
||||||
|
setStepNumber(q => q - 1);
|
||||||
|
|
||||||
|
const questionIndex = items.findIndex(({ id }) => id === question.id);
|
||||||
|
|
||||||
|
const previousQuestion = items[questionIndex - 1];
|
||||||
|
|
||||||
|
if (previousQuestion) {
|
||||||
|
setCurrentQuestion(previousQuestion);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (String(rules[0].answers[i]) === answer?.answer) {
|
|
||||||
readyBeNextQuestion = next; // Если хоть один элемент отличается, массивы не равны
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (readyBeNextQuestion) return readyBeNextQuestion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!question.required) {//вопрос не обязателен и не нашли совпадений между ответами и условиями ветвления
|
|
||||||
console.log("вопрос не обязателен ищем дальше")
|
|
||||||
const defaultQ = question.content.rule.default
|
|
||||||
if (defaultQ.length > 1 && defaultQ !== " ") return defaultQ
|
|
||||||
//Вопросы типа страница, ползунок, своё поле для ввода и дата не могут иметь больше 1 ребёнка. Пользователь не может настроить там дефолт
|
|
||||||
//Кинуть на ребёнка надо даже если там нет дефолта
|
|
||||||
if (
|
|
||||||
(question?.type === "date" ||
|
|
||||||
question?.type === "text" ||
|
|
||||||
question?.type === "number" ||
|
|
||||||
question?.type === "page") && question.content.rule.children.length === 1
|
|
||||||
) return question.content.rule.children[0]
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
//ничё не нашли, ищем резулт
|
|
||||||
console.log("ничё не нашли, ищем резулт ")
|
|
||||||
return items.find(q => {
|
|
||||||
console.log('q.type === "result"', q.type === "result")
|
|
||||||
console.log('q.content.rule.parentId', q.content.rule.parentId)
|
|
||||||
//@ts-ignore
|
|
||||||
console.log('question.content.id', question.content.id)
|
|
||||||
//@ts-ignore
|
|
||||||
return q.type === "result" && q.content.rule.parentId === question.content.id
|
|
||||||
})?.id
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const followPreviousStep = () => {
|
|
||||||
if (linear) {
|
|
||||||
setStepNumber(q => q - 1)
|
|
||||||
|
|
||||||
const questionIndex = items.findIndex(({ id }) => id === question.id);
|
|
||||||
|
|
||||||
const previousQuestion = items[questionIndex - 1];
|
|
||||||
|
|
||||||
if (previousQuestion) {
|
|
||||||
setCurrentQuestion(previousQuestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (question?.content.rule.parentId !== "root") {
|
|
||||||
const parent = getQuestionById(question?.content.rule.parentId);
|
|
||||||
if (parent?.type) {
|
|
||||||
setCurrentQuestion(parent);
|
|
||||||
} else {
|
|
||||||
enqueueSnackbar("не могу получить предыдущий вопрос");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
enqueueSnackbar("вы находитесь на первом вопросе");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const followNextStep = () => {
|
|
||||||
if (linear) {
|
|
||||||
setStepNumber(q => q + 1)
|
|
||||||
|
|
||||||
const questionIndex = items.findIndex(({ id }) => id === question.id);
|
|
||||||
const nextQuestion = items[questionIndex + 1];
|
|
||||||
|
|
||||||
if (nextQuestion && nextQuestion.type !== "result") {
|
|
||||||
setCurrentQuestion(nextQuestion);
|
|
||||||
} else {
|
|
||||||
//@ts-ignore
|
|
||||||
showResult(items.find(q => q.content.rule.parentId === "line"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextQuestionId = getNextQuestionId();
|
|
||||||
console.log(nextQuestionId)
|
|
||||||
|
|
||||||
if (nextQuestionId) {
|
|
||||||
const nextQuestion = getQuestionById(nextQuestionId);
|
|
||||||
console.log(nextQuestion)
|
|
||||||
|
|
||||||
if (nextQuestion?.type && nextQuestion.type === "result") {
|
|
||||||
showResult(nextQuestion);
|
|
||||||
} else {
|
|
||||||
//@ts-ignore
|
|
||||||
setCurrentQuestion(nextQuestion);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
enqueueSnackbar("не могу получить последующий вопрос");
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: "relative",
|
|
||||||
padding: "15px 0",
|
|
||||||
borderTop: `1px solid ${theme.palette.grey[400]}`,
|
|
||||||
height: '75px',
|
|
||||||
|
|
||||||
display: "flex"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
maxWidth: "1000px",
|
|
||||||
padding: "0 10px",
|
|
||||||
margin: "0 auto",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "10px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/*{mode[settings.cfg.theme] ? (*/}
|
|
||||||
{/* <NameplateLogoFQ style={{ fontSize: "34px", width:"200px", height:"auto" }} />*/}
|
|
||||||
{/*):(*/}
|
|
||||||
{/* <NameplateLogoFQDark style={{ fontSize: "34px", width:"200px", height:"auto" }} />*/}
|
|
||||||
{/*)}*/}
|
|
||||||
{linear &&
|
|
||||||
<>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "10px",
|
|
||||||
marginRight: "auto",
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography>Шаг</Typography>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
fontWeight: "bold",
|
|
||||||
borderRadius: "50%",
|
|
||||||
width: "30px",
|
|
||||||
height: "30px",
|
|
||||||
color: "#FFF",
|
|
||||||
background: theme.palette.primary.main,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{stepNumber}
|
|
||||||
</Typography>
|
|
||||||
<Typography>Из</Typography>
|
|
||||||
<Typography sx={{ fontWeight: "bold" }}>
|
|
||||||
{items.filter(q => q.type !== "result").length}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (question?.content.rule.parentId !== "root") {
|
||||||
|
const parent = getQuestionById(question?.content.rule.parentId);
|
||||||
|
if (parent?.type) {
|
||||||
|
setCurrentQuestion(parent);
|
||||||
|
} else {
|
||||||
|
enqueueSnackbar("не могу получить предыдущий вопрос");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
enqueueSnackbar("вы находитесь на первом вопросе");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const followNextStep = () => {
|
||||||
|
if (isLinear) {
|
||||||
|
setStepNumber(q => q + 1);
|
||||||
|
|
||||||
|
const questionIndex = items.findIndex(({ id }) => id === question.id);
|
||||||
|
const nextQuestion = items[questionIndex + 1];
|
||||||
|
|
||||||
|
if (nextQuestion && nextQuestion.type !== "result") {
|
||||||
|
setCurrentQuestion(nextQuestion);
|
||||||
|
} else {
|
||||||
|
//@ts-ignore
|
||||||
|
showResult(items.find(q => q.content.rule.parentId === "line"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextQuestionId = getNextQuestionId();
|
||||||
|
|
||||||
|
if (nextQuestionId) {
|
||||||
|
const nextQuestion = getQuestionById(nextQuestionId);
|
||||||
|
|
||||||
|
if (nextQuestion?.type && nextQuestion.type === "result") {
|
||||||
|
showResult(nextQuestion);
|
||||||
|
} else {
|
||||||
|
//@ts-ignore
|
||||||
|
setCurrentQuestion(nextQuestion);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
enqueueSnackbar("не могу получить последующий вопрос");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
position: "relative",
|
||||||
alignItems: "center",
|
padding: "15px 0",
|
||||||
gap: "10px",
|
borderTop: `1px solid ${theme.palette.grey[400]}`,
|
||||||
marginRight: "auto",
|
height: '75px',
|
||||||
// color: theme.palette.grey1.main,
|
display: "flex",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* <Typography>Шаг</Typography>
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: "1000px",
|
||||||
|
padding: "0 10px",
|
||||||
|
margin: "0 auto",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/*{mode[settings.cfg.theme] ? (*/}
|
||||||
|
{/* <NameplateLogoFQ style={{ fontSize: "34px", width:"200px", height:"auto" }} />*/}
|
||||||
|
{/*):(*/}
|
||||||
|
{/* <NameplateLogoFQDark style={{ fontSize: "34px", width:"200px", height:"auto" }} />*/}
|
||||||
|
{/*)}*/}
|
||||||
|
{isLinear &&
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "10px",
|
||||||
|
marginRight: "auto",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography>Шаг</Typography>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
fontWeight: "bold",
|
||||||
|
borderRadius: "50%",
|
||||||
|
width: "30px",
|
||||||
|
height: "30px",
|
||||||
|
color: "#FFF",
|
||||||
|
background: theme.palette.primary.main,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{stepNumber}
|
||||||
|
</Typography>
|
||||||
|
<Typography>Из</Typography>
|
||||||
|
<Typography sx={{ fontWeight: "bold" }}>
|
||||||
|
{items.filter(q => q.type !== "result").length}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "10px",
|
||||||
|
marginRight: "auto",
|
||||||
|
// color: theme.palette.grey1.main,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* <Typography>Шаг</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -331,36 +307,33 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{stepNumber} */}
|
{stepNumber} */}
|
||||||
{/* </Typography> */}
|
{/* </Typography> */}
|
||||||
{/* <Typography>Из</Typography>
|
{/* <Typography>Из</Typography>
|
||||||
<Typography sx={{ fontWeight: "bold" }}>
|
<Typography sx={{ fontWeight: "bold" }}>
|
||||||
{questions.length}
|
{questions.length}
|
||||||
</Typography> */}
|
</Typography> */}
|
||||||
|
</Box>
|
||||||
|
<Button
|
||||||
|
disabled={isPreviousButtonDisabled}
|
||||||
|
variant="contained"
|
||||||
|
sx={{ fontSize: "16px", padding: "10px 15px", }}
|
||||||
|
onClick={followPreviousStep}
|
||||||
|
>
|
||||||
|
{isMobileMini ? (
|
||||||
|
"←"
|
||||||
|
) : (
|
||||||
|
"← Назад"
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
disabled={isNextButtonDisabled}
|
||||||
|
variant="contained"
|
||||||
|
sx={{ fontSize: "16px", padding: "10px 15px" }}
|
||||||
|
onClick={followNextStep}
|
||||||
|
>
|
||||||
|
Далее →
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Button
|
);
|
||||||
disabled={disablePreviousButton}
|
|
||||||
variant="contained"
|
|
||||||
sx={{ fontSize: "16px", padding: "10px 15px",}}
|
|
||||||
onClick={followPreviousStep}
|
|
||||||
>
|
|
||||||
{isMobileMini ? (
|
|
||||||
"←"
|
|
||||||
) : (
|
|
||||||
"← Назад"
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
disabled={disableNextButton}
|
|
||||||
variant="contained"
|
|
||||||
sx={{ fontSize: "16px", padding: "10px 15px" }}
|
|
||||||
onClick={followNextStep}
|
|
||||||
>
|
|
||||||
Далее →
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1,141 +1,140 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import {Box, useMediaQuery, useTheme} from "@mui/material";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { useQuestionsStore } from "@stores/quizData/store"
|
|
||||||
import { getQuestionById } from "@stores/quizData/actions";
|
import { getQuestionById } from "@stores/quizData/actions";
|
||||||
|
|
||||||
import { Variant } from "./questions/Variant";
|
import { ContactForm } from "./ContactForm";
|
||||||
import { Images } from "./questions/Images";
|
import { Footer } from "./Footer";
|
||||||
import { Varimg } from "./questions/Varimg";
|
import { ResultForm } from "./ResultForm";
|
||||||
import { Emoji } from "./questions/Emoji";
|
|
||||||
import { Text } from "./questions/Text";
|
|
||||||
import { Select } from "./questions/Select";
|
|
||||||
import { Date } from "./questions/Date";
|
import { Date } from "./questions/Date";
|
||||||
import { Number } from "./questions/Number";
|
import { Emoji } from "./questions/Emoji";
|
||||||
import { File } from "./questions/File";
|
import { File } from "./questions/File";
|
||||||
|
import { Images } from "./questions/Images";
|
||||||
|
import { Number } from "./questions/Number";
|
||||||
import { Page } from "./questions/Page";
|
import { Page } from "./questions/Page";
|
||||||
import { Rating } from "./questions/Rating";
|
import { Rating } from "./questions/Rating";
|
||||||
import { Footer } from "./Footer";
|
import { Select } from "./questions/Select";
|
||||||
import { ContactForm } from "./ContactForm";
|
import { Text } from "./questions/Text";
|
||||||
import { ResultForm } from "./ResultForm";
|
import { Variant } from "./questions/Variant";
|
||||||
|
import { Varimg } from "./questions/Varimg";
|
||||||
|
|
||||||
import type { QuestionType } from "@model/questionTypes/shared";
|
|
||||||
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
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 {modes} from "../../utils/themes/Publication/themePublication";
|
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||||
|
import { useQuestionsStore } from "@stores/quizData/store";
|
||||||
|
import { notReachable } from "@utils/notReachable";
|
||||||
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
|
|
||||||
type QuestionProps = {
|
export const Question = () => {
|
||||||
questions: AnyTypedQuizQuestion[];
|
const theme = useTheme();
|
||||||
};
|
const settings = useQuestionsStore(state => state.settings);
|
||||||
|
const questions = useQuestionsStore(state => state.items);
|
||||||
|
const [currentQuestion, setCurrentQuestion] = useState<AnyTypedQuizQuestion>();
|
||||||
|
const [showContactForm, setShowContactForm] = useState<boolean>(false);
|
||||||
|
const [showResultForm, setShowResultForm] = useState<boolean>(false);
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
|
|
||||||
const QUESTIONS_MAP: any = {
|
useEffect(() => {
|
||||||
variant: Variant,
|
|
||||||
images: Images,
|
|
||||||
varimg: Varimg,
|
|
||||||
emoji: Emoji,
|
|
||||||
text: Text,
|
|
||||||
select: Select,
|
|
||||||
date: Date,
|
|
||||||
number: Number,
|
|
||||||
file: File,
|
|
||||||
page: Page,
|
|
||||||
rating: Rating,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Question = ({ questions }: QuestionProps) => {
|
if (settings?.cfg.haveRoot) {//ветвимся
|
||||||
const { settings } = useQuestionsStore()
|
const nextQuestion = getQuestionById(settings?.cfg.haveRoot || "");
|
||||||
const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
|
||||||
const [currentQuestion, setCurrentQuestion] = useState<AnyTypedQuizQuestion>();
|
|
||||||
const [showContactForm, setShowContactForm] = useState<boolean>(false);
|
|
||||||
const [showResultForm, setShowResultForm] = useState<boolean>(false);
|
|
||||||
const mode = modes;
|
|
||||||
console.log("currentQuestion ", currentQuestion)
|
|
||||||
useEffect(() => {
|
|
||||||
|
|
||||||
if (settings?.cfg.haveRoot) {//ветвимся
|
if (nextQuestion?.type) {
|
||||||
const nextQuestion = getQuestionById(settings?.cfg.haveRoot || "");
|
setCurrentQuestion(nextQuestion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (nextQuestion?.type) {
|
} else {//идём прямо
|
||||||
setCurrentQuestion(nextQuestion);
|
setCurrentQuestion(questions[0]);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
} else {//идём прямо
|
}, []);
|
||||||
setCurrentQuestion(questions[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, []);
|
if (!settings) throw new Error("settings is null");
|
||||||
|
if (!currentQuestion || currentQuestion.type === "result") return "не смог отобразить вопрос";
|
||||||
|
|
||||||
if (!currentQuestion) return <>не смог отобразить вопрос</>;
|
return (
|
||||||
|
|
||||||
const QuestionComponent =
|
|
||||||
QUESTIONS_MAP[currentQuestion.type as Exclude<QuestionType, "nonselected">];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
height: isMobile ? undefined : "100vh"
|
|
||||||
}}
|
|
||||||
|
|
||||||
>
|
|
||||||
{!showContactForm && !showResultForm && (
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
height: "calc(100vh - 75px)",
|
backgroundColor: theme.palette.background.default,
|
||||||
width: "100%",
|
height: isMobile ? undefined : "100vh"
|
||||||
maxWidth: "1440px",
|
}}
|
||||||
padding: "40px 25px 20px",
|
|
||||||
margin: "0 auto",
|
|
||||||
overflow: "auto",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "space-between"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<QuestionComponent currentQuestion={currentQuestion} />
|
{!showContactForm && !showResultForm && (
|
||||||
{mode[settings?.cfg.theme] ? (
|
<Box
|
||||||
<NameplateLogoFQ style={{ fontSize: "34px", width: "200px", height: "auto" }} />
|
sx={{
|
||||||
) : (
|
height: "calc(100vh - 75px)",
|
||||||
<NameplateLogoFQDark style={{ fontSize: "34px", width: "200px", height: "auto" }} />
|
width: "100%",
|
||||||
)}
|
maxWidth: "1440px",
|
||||||
|
padding: "40px 25px 20px",
|
||||||
|
margin: "0 auto",
|
||||||
|
overflow: "auto",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<QuestionByType question={currentQuestion} />
|
||||||
|
{quizThemes[settings.cfg.theme].isLight ? (
|
||||||
|
<NameplateLogoFQ style={{ fontSize: "34px", width: "200px", height: "auto" }} />
|
||||||
|
) : (
|
||||||
|
<NameplateLogoFQDark style={{ fontSize: "34px", width: "200px", height: "auto" }} />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{showResultForm && settings?.cfg.resultInfo.showResultForm === "before" && (
|
||||||
|
<ResultForm
|
||||||
|
currentQuestion={currentQuestion}
|
||||||
|
showContactForm={showContactForm}
|
||||||
|
setShowContactForm={setShowContactForm}
|
||||||
|
setShowResultForm={setShowResultForm}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showContactForm && (
|
||||||
|
<ContactForm
|
||||||
|
currentQuestion={currentQuestion}
|
||||||
|
showResultForm={showResultForm}
|
||||||
|
setShowContactForm={setShowContactForm}
|
||||||
|
setShowResultForm={setShowResultForm}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showResultForm && settings?.cfg.resultInfo.showResultForm === "after" && (
|
||||||
|
<ResultForm
|
||||||
|
currentQuestion={currentQuestion}
|
||||||
|
showContactForm={showContactForm}
|
||||||
|
setShowContactForm={setShowContactForm}
|
||||||
|
setShowResultForm={setShowResultForm}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!showContactForm && !showResultForm && (
|
||||||
|
<Footer
|
||||||
|
question={currentQuestion}
|
||||||
|
setCurrentQuestion={setCurrentQuestion}
|
||||||
|
setShowContactForm={setShowContactForm}
|
||||||
|
setShowResultForm={setShowResultForm}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
);
|
||||||
{showResultForm && settings?.cfg.resultInfo.showResultForm === "before" && (
|
|
||||||
<ResultForm
|
|
||||||
currentQuestion={currentQuestion}
|
|
||||||
showContactForm={showContactForm}
|
|
||||||
setShowContactForm={setShowContactForm}
|
|
||||||
setShowResultForm={setShowResultForm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{showContactForm && (
|
|
||||||
<ContactForm
|
|
||||||
currentQuestion={currentQuestion}
|
|
||||||
showResultForm={showResultForm}
|
|
||||||
setShowContactForm={setShowContactForm}
|
|
||||||
setShowResultForm={setShowResultForm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{showResultForm && settings?.cfg.resultInfo.showResultForm === "after" && (
|
|
||||||
<ResultForm
|
|
||||||
currentQuestion={currentQuestion}
|
|
||||||
showContactForm={showContactForm}
|
|
||||||
setShowContactForm={setShowContactForm}
|
|
||||||
setShowResultForm={setShowResultForm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!showContactForm && !showResultForm && (
|
|
||||||
<Footer
|
|
||||||
question={currentQuestion}
|
|
||||||
setCurrentQuestion={setCurrentQuestion}
|
|
||||||
setShowContactForm={setShowContactForm}
|
|
||||||
setShowResultForm={setShowResultForm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function QuestionByType({ question }: {
|
||||||
|
question: Exclude<AnyTypedQuizQuestion, QuizQuestionResult>;
|
||||||
|
}) {
|
||||||
|
switch (question.type) {
|
||||||
|
case "variant": return <Variant currentQuestion={question} />;
|
||||||
|
case "images": return <Images currentQuestion={question} />;
|
||||||
|
case "varimg": return <Varimg currentQuestion={question} />;
|
||||||
|
case "emoji": return <Emoji currentQuestion={question} />;
|
||||||
|
case "text": return <Text currentQuestion={question} />;
|
||||||
|
case "select": return <Select currentQuestion={question} />;
|
||||||
|
case "date": return <Date currentQuestion={question} />;
|
||||||
|
case "number": return <Number currentQuestion={question} />;
|
||||||
|
case "file": return <File currentQuestion={question} />;
|
||||||
|
case "page": return <Page currentQuestion={question} />;
|
||||||
|
case "rating": return <Rating currentQuestion={question} />;
|
||||||
|
default: return notReachable(question);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,234 +1,228 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Button,
|
||||||
Button,
|
Typography,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
|
||||||
|
|
||||||
import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe";
|
|
||||||
import { NameplateLogo } from "@icons/NameplateLogo";
|
import { NameplateLogo } from "@icons/NameplateLogo";
|
||||||
import { modes } from "../../utils/themes/Publication/themePublication";
|
import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe";
|
||||||
|
|
||||||
|
import { useQuestionsStore } from "@stores/quizData/store";
|
||||||
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
|
import { useCallback, useEffect, useMemo } from "react";
|
||||||
import type { QuizQuestionResult } from "../../model/questionTypes/result";
|
import type { QuizQuestionResult } from "../../model/questionTypes/result";
|
||||||
|
|
||||||
|
|
||||||
type ResultFormProps = {
|
type ResultFormProps = {
|
||||||
currentQuestion: any;
|
currentQuestion: any;
|
||||||
showContactForm: boolean;
|
showContactForm: boolean;
|
||||||
setShowContactForm: (show: boolean) => void;
|
setShowContactForm: (show: boolean) => void;
|
||||||
setShowResultForm: (show: boolean) => void;
|
setShowResultForm: (show: boolean) => void;
|
||||||
};
|
};
|
||||||
export const ResultForm = ({
|
export const ResultForm = ({
|
||||||
currentQuestion,
|
currentQuestion,
|
||||||
showContactForm,
|
showContactForm,
|
||||||
setShowContactForm,
|
setShowContactForm,
|
||||||
setShowResultForm,
|
setShowResultForm,
|
||||||
}: 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, items } = useQuestionsStore();
|
||||||
const mode = modes;
|
if (!settings) throw new Error("settings is null");
|
||||||
|
|
||||||
const searchResult = (): QuizQuestionResult => {
|
const resultQuestion = useMemo(() => {
|
||||||
if (Boolean(settings?.cfg.haveRoot)) {
|
if (settings?.cfg.haveRoot) {
|
||||||
//ищём для ветвления
|
//ищём для ветвления
|
||||||
return (items.find(
|
return (items.find(
|
||||||
(question) =>
|
(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(
|
||||||
items.find(
|
(question): question is QuizQuestionResult =>
|
||||||
(question) =>
|
question.type === "result" &&
|
||||||
question.type === "result" &&
|
question.content.rule.parentId === "line"
|
||||||
question.content.rule.parentId === "line"
|
));
|
||||||
)) as QuizQuestionResult;
|
} else {
|
||||||
} else {
|
return items.find(
|
||||||
return items.find(
|
(question): question is QuizQuestionResult =>
|
||||||
(question) =>
|
question.type === "result" &&
|
||||||
question.type === "result" &&
|
question.content.rule.parentId === "line"
|
||||||
question.content.rule.parentId === "line"
|
);
|
||||||
) as QuizQuestionResult;
|
}
|
||||||
}
|
}, [currentQuestion.content.id, items, settings?.cfg.haveRoot]);
|
||||||
};
|
|
||||||
const resultQuestion = searchResult();
|
const followNextForm = useCallback(() => {
|
||||||
|
setShowResultForm(false);
|
||||||
|
setShowContactForm(true);
|
||||||
|
},[setShowContactForm, setShowResultForm]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!resultQuestion) {
|
||||||
|
followNextForm();
|
||||||
|
}
|
||||||
|
}, [followNextForm, resultQuestion]);
|
||||||
|
|
||||||
|
if (!resultQuestion) return null;
|
||||||
|
|
||||||
const followNextForm = () => {
|
|
||||||
setShowResultForm(false);
|
|
||||||
setShowContactForm(true);
|
|
||||||
};
|
|
||||||
console.log(resultQuestion);
|
|
||||||
if (resultQuestion === null || resultQuestion === undefined) {
|
|
||||||
followNextForm();
|
|
||||||
return <></>;
|
|
||||||
} else {
|
|
||||||
return (
|
return (
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
height: "100vh",
|
|
||||||
width: "100vw",
|
|
||||||
pt: "28px",
|
|
||||||
overflow: "auto",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "start",
|
|
||||||
width: isMobile ? "100%" : "490px",
|
|
||||||
padding: isMobile ? "0 16px" : undefined,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
//@ts-ignore
|
|
||||||
!resultQuestion?.content.useImage &&
|
|
||||||
resultQuestion.content.video && (
|
|
||||||
<YoutubeEmbedIframe
|
|
||||||
//@ts-ignore
|
|
||||||
videoUrl={resultQuestion.content.video}
|
|
||||||
containerSX={{
|
|
||||||
width: isMobile ? "100%" : "490px",
|
|
||||||
height: isMobile ? "100%" : "280px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
//@ts-ignore
|
|
||||||
resultQuestion?.content.useImage && resultQuestion.content.back && (
|
|
||||||
<Box
|
|
||||||
component="img"
|
|
||||||
src={resultQuestion.content.back}
|
|
||||||
sx={{
|
|
||||||
width: isMobile ? "100%" : "490px",
|
|
||||||
height: isMobile ? "100%" : "280px",
|
|
||||||
}}
|
|
||||||
></Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{resultQuestion.description !== "" &&
|
|
||||||
resultQuestion.description !== " " && (
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "23px",
|
|
||||||
fontWeight: 700,
|
|
||||||
m: "20px 0",
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{resultQuestion.description}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Typography
|
|
||||||
sx={{
|
sx={{
|
||||||
m: "20px 0",
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{resultQuestion.title}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
{
|
|
||||||
//@ts-ignore
|
|
||||||
resultQuestion.content.text !== "" &&
|
|
||||||
//@ts-ignore
|
|
||||||
resultQuestion.content.text !== " " && (
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "18px",
|
|
||||||
m: "20px 0",
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
//@ts-ignore
|
|
||||||
resultQuestion.content.text
|
|
||||||
}
|
|
||||||
</Typography>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box width="100%">
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
width: "100%",
|
|
||||||
justifyContent: "end",
|
|
||||||
px: "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
mt: "15px",
|
justifyContent: "space-between",
|
||||||
gap: "10px",
|
height: "100vh",
|
||||||
}}
|
width: "100vw",
|
||||||
>
|
pt: "28px",
|
||||||
<NameplateLogo
|
overflow: "auto",
|
||||||
style={{
|
|
||||||
fontSize: "34px",
|
|
||||||
color: mode[settings.cfg.theme] ? "#000000" : "#F5F7FF",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "20px",
|
|
||||||
//@ts-ignore
|
|
||||||
color: mode[settings.cfg.theme] ? "#4D4D4D" : "#F5F7FF",
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Сделано на PenaQuiz
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
boxShadow: "0 0 15px 0 rgba(0,0,0,.08)",
|
|
||||||
width: "100%",
|
|
||||||
flexDirection: "column",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
p: "20px",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{settings?.cfg.resultInfo.showResultForm === "before" && (
|
<Box
|
||||||
<Button
|
|
||||||
onClick={followNextForm}
|
|
||||||
variant="contained"
|
|
||||||
sx={{
|
sx={{
|
||||||
p: "10px 20px",
|
display: "flex",
|
||||||
width: "210px",
|
flexDirection: "column",
|
||||||
height: "50px",
|
alignItems: "start",
|
||||||
|
width: isMobile ? "100%" : "490px",
|
||||||
|
padding: isMobile ? "0 16px" : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{resultQuestion.content.hint.text || "Узнать подробнее"}
|
{
|
||||||
</Button>
|
!resultQuestion?.content.useImage && resultQuestion.content.video && (
|
||||||
)}
|
<YoutubeEmbedIframe
|
||||||
{settings?.cfg.resultInfo.showResultForm === "after" &&
|
videoUrl={resultQuestion.content.video}
|
||||||
resultQuestion.content.redirect && (
|
containerSX={{
|
||||||
<Button
|
width: isMobile ? "100%" : "490px",
|
||||||
href={resultQuestion.content.redirect}
|
height: isMobile ? "100%" : "280px",
|
||||||
variant="contained"
|
}}
|
||||||
sx={{ p: "10px 20px", width: "210px", height: "50px" }}
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
resultQuestion?.content.useImage && resultQuestion.content.back && (
|
||||||
|
<Box
|
||||||
|
component="img"
|
||||||
|
src={resultQuestion.content.back}
|
||||||
|
sx={{
|
||||||
|
width: isMobile ? "100%" : "490px",
|
||||||
|
height: isMobile ? "100%" : "280px",
|
||||||
|
}}
|
||||||
|
></Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{resultQuestion.description !== "" &&
|
||||||
|
resultQuestion.description !== " " && (
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "23px",
|
||||||
|
fontWeight: 700,
|
||||||
|
m: "20px 0",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{resultQuestion.description}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
m: "20px 0",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{resultQuestion.content.hint.text || "Перейти на сайт"}
|
{resultQuestion.title}
|
||||||
</Button>
|
</Typography>
|
||||||
)}
|
|
||||||
</Box>
|
{
|
||||||
|
resultQuestion.content.text !== "" &&
|
||||||
|
resultQuestion.content.text !== " " && (
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "18px",
|
||||||
|
m: "20px 0",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
resultQuestion.content.text
|
||||||
|
}
|
||||||
|
</Typography>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box width="100%">
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
width: "100%",
|
||||||
|
justifyContent: "end",
|
||||||
|
px: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
mt: "15px",
|
||||||
|
gap: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NameplateLogo
|
||||||
|
style={{
|
||||||
|
fontSize: "34px",
|
||||||
|
color: quizThemes[settings.cfg.theme].isLight ? "#000000" : "#F5F7FF",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "20px",
|
||||||
|
color: quizThemes[settings.cfg.theme].isLight ? "#4D4D4D" : "#F5F7FF",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Сделано на PenaQuiz
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
boxShadow: "0 0 15px 0 rgba(0,0,0,.08)",
|
||||||
|
width: "100%",
|
||||||
|
flexDirection: "column",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
p: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{settings?.cfg.resultInfo.showResultForm === "before" && (
|
||||||
|
<Button
|
||||||
|
onClick={followNextForm}
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
p: "10px 20px",
|
||||||
|
width: "210px",
|
||||||
|
height: "50px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{resultQuestion.content.hint.text || "Узнать подробнее"}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{settings?.cfg.resultInfo.showResultForm === "after" &&
|
||||||
|
resultQuestion.content.redirect && (
|
||||||
|
<Button
|
||||||
|
href={resultQuestion.content.redirect}
|
||||||
|
variant="contained"
|
||||||
|
sx={{ p: "10px 20px", width: "210px", height: "50px" }}
|
||||||
|
>
|
||||||
|
{resultQuestion.content.hint.text || "Перейти на сайт"}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -1,473 +1,471 @@
|
|||||||
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 YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe";
|
||||||
import { QuizStartpageAlignType, QuizStartpageType } from "@model/quizSettings";
|
|
||||||
import { notReachable } from "../../utils/notReachable";
|
import { notReachable } from "../../utils/notReachable";
|
||||||
import { useUADevice } from "../../utils/hooks/useUADevice";
|
import { useUADevice } from "../../utils/hooks/useUADevice";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store";
|
|
||||||
import { NameplateLogo } from "@icons/NameplateLogo";
|
import { NameplateLogo } from "@icons/NameplateLogo";
|
||||||
import { modes } from "../../utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
|
import { QuizStartpageAlignType, QuizStartpageType } from "@model/settingsData";
|
||||||
|
import { useQuestionsStore } from "@stores/quizData/store";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
setVisualStartPage: (a: boolean) => void;
|
setVisualStartPage: (a: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuestionsStore();
|
||||||
const mode = modes;
|
const { isMobileDevice } = useUADevice();
|
||||||
const { isMobileDevice } = useUADevice();
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
|
||||||
|
|
||||||
if (!settings) return null;
|
if (!settings) throw new Error("settings is null");
|
||||||
|
|
||||||
console.log(settings);
|
const handleCopyNumber = () => {
|
||||||
|
navigator.clipboard.writeText(settings.cfg.info.phonenumber);
|
||||||
|
};
|
||||||
|
|
||||||
const handleCopyNumber = () => {
|
const background =
|
||||||
navigator.clipboard.writeText(settings.cfg.info.phonenumber);
|
settings.cfg.startpage.background.type === "image" ? (
|
||||||
};
|
settings.cfg.startpage.background.desktop ? (
|
||||||
|
|
||||||
const background =
|
|
||||||
settings.cfg.startpage.background.type === "image" ? (
|
|
||||||
settings.cfg.startpage.background.desktop ? (
|
|
||||||
<img
|
|
||||||
src={settings.cfg.startpage.background.desktop}
|
|
||||||
alt=""
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
objectFit: "cover",
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
) : settings.cfg.startpage.background.type === "video" ? (
|
|
||||||
settings.cfg.startpage.background.video ? (
|
|
||||||
<YoutubeEmbedIframe
|
|
||||||
videoUrl={settings.cfg.startpage.background.video}
|
|
||||||
containerSX={{
|
|
||||||
width:
|
|
||||||
settings.cfg.startpageType === "centered"
|
|
||||||
? "550px"
|
|
||||||
: settings.cfg.startpageType === "expanded"
|
|
||||||
? "100vw"
|
|
||||||
: "100%",
|
|
||||||
height:
|
|
||||||
settings.cfg.startpageType === "centered"
|
|
||||||
? "275px"
|
|
||||||
: settings.cfg.startpageType === "expanded"
|
|
||||||
? "100vh"
|
|
||||||
: "100%",
|
|
||||||
borderRadius: settings.cfg.startpageType === "centered" ? "10px" : "0",
|
|
||||||
overflow: "hidden",
|
|
||||||
"& iframe": {
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
transform:
|
|
||||||
settings.cfg.startpageType === "centered"
|
|
||||||
? ""
|
|
||||||
: settings.cfg.startpageType === "expanded"
|
|
||||||
? "scale(1.5)"
|
|
||||||
: "scale(2.4)",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper
|
|
||||||
className="settings-preview-draghandle"
|
|
||||||
sx={{
|
|
||||||
height: "100vh",
|
|
||||||
width: "100vw",
|
|
||||||
background:
|
|
||||||
settings.cfg.startpageType === "expanded" && !isMobile
|
|
||||||
? settings.cfg.startpage.position === "left"
|
|
||||||
? "linear-gradient(90deg,#272626,transparent)"
|
|
||||||
: settings.cfg.startpage.position === "center"
|
|
||||||
? "linear-gradient(180deg,transparent,#272626)"
|
|
||||||
: "linear-gradient(270deg,#272626,transparent)"
|
|
||||||
: theme.palette.background.default,
|
|
||||||
|
|
||||||
color: settings.cfg.startpageType === "expanded" ? "white" : "black",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<QuizPreviewLayoutByType
|
|
||||||
quizHeaderBlock={
|
|
||||||
<Box p={settings.cfg.startpageType === "standard" ? "" : "16px"}>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "20px",
|
|
||||||
mb: "7px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{settings.cfg.startpage.logo && (
|
|
||||||
<img
|
<img
|
||||||
src={settings.cfg.startpage.logo}
|
src={settings.cfg.startpage.background.desktop}
|
||||||
style={{
|
alt=""
|
||||||
height: "37px",
|
style={{
|
||||||
maxWidth: "43px",
|
width: "100%",
|
||||||
objectFit: "cover",
|
height: "100%",
|
||||||
}}
|
objectFit: "cover",
|
||||||
alt=""
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
) : null
|
||||||
<Typography
|
) : settings.cfg.startpage.background.type === "video" ? (
|
||||||
sx={{
|
settings.cfg.startpage.background.video ? (
|
||||||
fontSize: "14px",
|
<YoutubeEmbedIframe
|
||||||
color: settings.cfg.startpageType === "expanded"
|
videoUrl={settings.cfg.startpage.background.video}
|
||||||
&& !isMobile ? "white" : theme.palette.text.primary
|
containerSX={{
|
||||||
}}
|
width:
|
||||||
>{settings.cfg.info.orgname}</Typography>
|
settings.cfg.startpageType === "centered"
|
||||||
</Box>
|
? "550px"
|
||||||
<Link mb="16px" href={settings.cfg.info.site}>
|
: settings.cfg.startpageType === "expanded"
|
||||||
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
|
? "100vw"
|
||||||
{settings.cfg.info.site}
|
: "100%",
|
||||||
</Typography>
|
height:
|
||||||
</Link>
|
settings.cfg.startpageType === "centered"
|
||||||
</Box>
|
? "275px"
|
||||||
}
|
: settings.cfg.startpageType === "expanded"
|
||||||
quizMainBlock={
|
? "100vh"
|
||||||
<>
|
: "100%",
|
||||||
<Box
|
borderRadius: settings.cfg.startpageType === "centered" ? "10px" : "0",
|
||||||
sx={{
|
overflow: "hidden",
|
||||||
display: "flex",
|
"& iframe": {
|
||||||
flexDirection: "column",
|
width: "100%",
|
||||||
justifyContent: "center",
|
height: "100%",
|
||||||
alignItems:
|
transform:
|
||||||
settings.cfg.startpageType === "centered"
|
settings.cfg.startpageType === "centered"
|
||||||
? "center"
|
? ""
|
||||||
: settings.cfg.startpageType === "expanded"
|
: settings.cfg.startpageType === "expanded"
|
||||||
? settings.cfg.startpage.position === "center"
|
? "scale(1.5)"
|
||||||
? "center"
|
: "scale(2.4)",
|
||||||
: "start"
|
},
|
||||||
: "start",
|
}}
|
||||||
mt: "28px",
|
/>
|
||||||
width: "100%",
|
) : null
|
||||||
}}
|
) : null;
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontWeight: "bold",
|
|
||||||
fontSize: "26px",
|
|
||||||
fontStyle: "normal",
|
|
||||||
fontStretch: "normal",
|
|
||||||
lineHeight: "1.2",
|
|
||||||
overflowWrap: "break-word",
|
|
||||||
width: "100%",
|
|
||||||
textAlign: settings.cfg.startpageType === "centered" ? "center" : "-moz-initial",
|
|
||||||
color: settings.cfg.startpageType === "expanded" && !isMobile ? "white" : theme.palette.text.primary
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{settings.name}
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "16px",
|
|
||||||
m: "16px 0",
|
|
||||||
overflowWrap: "break-word",
|
|
||||||
width: "100%",
|
|
||||||
textAlign: settings.cfg.startpageType === "centered" ? "center" : "-moz-initial",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{settings.cfg.startpage.description}
|
|
||||||
</Typography>
|
|
||||||
<Box width={settings.cfg.startpageType === "standard" ? "100%" : "auto"}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
sx={{
|
|
||||||
fontSize: "16px",
|
|
||||||
padding: "10px 15px",
|
|
||||||
width: settings.cfg.startpageType === "standard" ? "100%" : "auto",
|
|
||||||
}}
|
|
||||||
onClick={() => setVisualStartPage(false)}
|
|
||||||
>
|
|
||||||
{settings.cfg.startpage.button.trim() ? settings.cfg.startpage.button : "Пройти тест"}
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
return (
|
||||||
sx={{
|
<Paper
|
||||||
mt: "46px",
|
className="settings-preview-draghandle"
|
||||||
display: "flex",
|
sx={{
|
||||||
alignItems: "center",
|
height: "100vh",
|
||||||
justifyContent: "space-between",
|
width: "100vw",
|
||||||
width: "100%",
|
background:
|
||||||
flexDirection: isMobile ? "column" : "row"
|
settings.cfg.startpageType === "expanded" && !isMobile
|
||||||
}}
|
? settings.cfg.startpage.position === "left"
|
||||||
>
|
? "linear-gradient(90deg,#272626,transparent)"
|
||||||
<Box sx={{ maxWidth: "300px" }}>
|
: settings.cfg.startpage.position === "center"
|
||||||
{settings.cfg.info.clickable ? (
|
? "linear-gradient(180deg,transparent,#272626)"
|
||||||
isMobileDevice ? (
|
: "linear-gradient(270deg,#272626,transparent)"
|
||||||
<Link href={`tel:${settings.cfg.info.phonenumber}`}>
|
: theme.palette.background.default,
|
||||||
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
|
|
||||||
{settings.cfg.info.phonenumber}
|
|
||||||
</Typography>
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<ButtonBase onClick={handleCopyNumber}>
|
|
||||||
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
|
|
||||||
{settings.cfg.info.phonenumber}
|
|
||||||
</Typography>
|
|
||||||
</ButtonBase>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
|
|
||||||
{settings.cfg.info.phonenumber}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
<Typography sx={{ width: "100%",
|
|
||||||
overflowWrap: "break-word",
|
|
||||||
fontSize: "12px", textAlign: "end",
|
|
||||||
color:
|
|
||||||
settings.cfg.startpageType === "expanded" && !isMobile
|
|
||||||
? "white"
|
|
||||||
: theme.palette.text.primary,
|
|
||||||
}}>
|
|
||||||
{settings.cfg.info.law}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
color: settings.cfg.startpageType === "expanded" ? "white" : "black",
|
||||||
sx={{
|
}}
|
||||||
display: "flex",
|
>
|
||||||
alignItems: "center",
|
<QuizPreviewLayoutByType
|
||||||
gap: "15px"
|
quizHeaderBlock={
|
||||||
}}
|
<Box p={settings.cfg.startpageType === "standard" ? "" : "16px"}>
|
||||||
>
|
<Box
|
||||||
<NameplateLogo style={{ fontSize: "34px", color: settings.cfg.startpageType === "expanded" && !isMobile ? "#FFFFFF" : (mode[settings.cfg.theme] ? "#151515" : "#FFFFFF") }} />
|
sx={{
|
||||||
<Typography sx={{ fontSize: "20px", color: settings.cfg.startpageType === "expanded" && !isMobile ? "#F5F7FF" : (mode[settings.cfg.theme] ? "#4D4D4D" : "#F5F7FF"), whiteSpace: "nowrap", }}>
|
display: "flex",
|
||||||
Сделано на PenaQuiz
|
alignItems: "center",
|
||||||
</Typography>
|
gap: "20px",
|
||||||
</Box>
|
mb: "7px",
|
||||||
</Box>
|
}}
|
||||||
</>
|
>
|
||||||
}
|
{settings.cfg.startpage.logo && (
|
||||||
backgroundBlock={background}
|
<img
|
||||||
startpageType={settings.cfg.startpageType}
|
src={settings.cfg.startpage.logo}
|
||||||
alignType={settings.cfg.startpage.position}
|
style={{
|
||||||
/>
|
height: "37px",
|
||||||
</Paper>
|
maxWidth: "43px",
|
||||||
);
|
objectFit: "cover",
|
||||||
|
}}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "14px",
|
||||||
|
color: settings.cfg.startpageType === "expanded"
|
||||||
|
&& !isMobile ? "white" : theme.palette.text.primary
|
||||||
|
}}
|
||||||
|
>{settings.cfg.info.orgname}</Typography>
|
||||||
|
</Box>
|
||||||
|
<Link mb="16px" href={settings.cfg.info.site}>
|
||||||
|
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
|
||||||
|
{settings.cfg.info.site}
|
||||||
|
</Typography>
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
quizMainBlock={
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems:
|
||||||
|
settings.cfg.startpageType === "centered"
|
||||||
|
? "center"
|
||||||
|
: settings.cfg.startpageType === "expanded"
|
||||||
|
? settings.cfg.startpage.position === "center"
|
||||||
|
? "center"
|
||||||
|
: "start"
|
||||||
|
: "start",
|
||||||
|
mt: "28px",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontWeight: "bold",
|
||||||
|
fontSize: "26px",
|
||||||
|
fontStyle: "normal",
|
||||||
|
fontStretch: "normal",
|
||||||
|
lineHeight: "1.2",
|
||||||
|
overflowWrap: "break-word",
|
||||||
|
width: "100%",
|
||||||
|
textAlign: settings.cfg.startpageType === "centered" ? "center" : "-moz-initial",
|
||||||
|
color: settings.cfg.startpageType === "expanded" && !isMobile ? "white" : theme.palette.text.primary
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{settings.name}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "16px",
|
||||||
|
m: "16px 0",
|
||||||
|
overflowWrap: "break-word",
|
||||||
|
width: "100%",
|
||||||
|
textAlign: settings.cfg.startpageType === "centered" ? "center" : "-moz-initial",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{settings.cfg.startpage.description}
|
||||||
|
</Typography>
|
||||||
|
<Box width={settings.cfg.startpageType === "standard" ? "100%" : "auto"}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
fontSize: "16px",
|
||||||
|
padding: "10px 15px",
|
||||||
|
width: settings.cfg.startpageType === "standard" ? "100%" : "auto",
|
||||||
|
}}
|
||||||
|
onClick={() => setVisualStartPage(false)}
|
||||||
|
>
|
||||||
|
{settings.cfg.startpage.button.trim() ? settings.cfg.startpage.button : "Пройти тест"}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
mt: "46px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
flexDirection: isMobile ? "column" : "row"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ maxWidth: "300px" }}>
|
||||||
|
{settings.cfg.info.clickable ? (
|
||||||
|
isMobileDevice ? (
|
||||||
|
<Link href={`tel:${settings.cfg.info.phonenumber}`}>
|
||||||
|
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
|
||||||
|
{settings.cfg.info.phonenumber}
|
||||||
|
</Typography>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<ButtonBase onClick={handleCopyNumber}>
|
||||||
|
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
|
||||||
|
{settings.cfg.info.phonenumber}
|
||||||
|
</Typography>
|
||||||
|
</ButtonBase>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
|
||||||
|
{settings.cfg.info.phonenumber}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
<Typography sx={{
|
||||||
|
width: "100%",
|
||||||
|
overflowWrap: "break-word",
|
||||||
|
fontSize: "12px", textAlign: "end",
|
||||||
|
color:
|
||||||
|
settings.cfg.startpageType === "expanded" && !isMobile
|
||||||
|
? "white"
|
||||||
|
: theme.palette.text.primary,
|
||||||
|
}}>
|
||||||
|
{settings.cfg.info.law}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "15px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NameplateLogo style={{ fontSize: "34px", color: settings.cfg.startpageType === "expanded" && !isMobile ? "#FFFFFF" : (quizThemes[settings.cfg.theme].isLight ? "#151515" : "#FFFFFF") }} />
|
||||||
|
<Typography sx={{ fontSize: "20px", color: settings.cfg.startpageType === "expanded" && !isMobile ? "#F5F7FF" : (quizThemes[settings.cfg.theme].isLight ? "#4D4D4D" : "#F5F7FF"), whiteSpace: "nowrap", }}>
|
||||||
|
Сделано на PenaQuiz
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
backgroundBlock={background}
|
||||||
|
startpageType={settings.cfg.startpageType}
|
||||||
|
alignType={settings.cfg.startpage.position}
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function QuizPreviewLayoutByType({
|
function QuizPreviewLayoutByType({
|
||||||
quizHeaderBlock,
|
quizHeaderBlock,
|
||||||
quizMainBlock,
|
quizMainBlock,
|
||||||
backgroundBlock,
|
backgroundBlock,
|
||||||
startpageType,
|
startpageType,
|
||||||
alignType,
|
alignType,
|
||||||
}: {
|
}: {
|
||||||
quizHeaderBlock: JSX.Element;
|
quizHeaderBlock: JSX.Element;
|
||||||
quizMainBlock: JSX.Element;
|
quizMainBlock: JSX.Element;
|
||||||
backgroundBlock: JSX.Element | null;
|
backgroundBlock: JSX.Element | null;
|
||||||
startpageType: QuizStartpageType;
|
startpageType: QuizStartpageType;
|
||||||
alignType: QuizStartpageAlignType;
|
alignType: QuizStartpageAlignType;
|
||||||
}) {
|
}) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
function StartPageMobile() {
|
function StartPageMobile() {
|
||||||
return (
|
return (
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column-reverse",
|
|
||||||
flexGrow: 1,
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
height: "100vh",
|
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
p: "25px",
|
|
||||||
height: "80%"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{quizHeaderBlock}
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
height: "80%",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
width: "100%"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{quizMainBlock}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{backgroundBlock}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
switch (startpageType) {
|
|
||||||
case null:
|
|
||||||
case "standard": {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{isMobile ? (
|
|
||||||
<StartPageMobile />
|
|
||||||
) : (
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: alignType === "left" ? (isMobile ? "column-reverse" : "row") : "row-reverse",
|
|
||||||
flexGrow: 1,
|
|
||||||
justifyContent: isMobile ? "flex-end" : undefined,
|
|
||||||
height: "100vh",
|
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
sx={{
|
||||||
width: isMobile ? "100%" : "40%",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
p: "25px",
|
|
||||||
height: isMobile ? "80%" : undefined
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{quizHeaderBlock}
|
|
||||||
{quizMainBlock}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: isMobile ? "100%" : "60%",
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{backgroundBlock}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</>
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case "expanded": {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{isMobile ? (
|
|
||||||
<StartPageMobile />
|
|
||||||
) : (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: "relative",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: startpageAlignTypeToJustifyContent[alignType],
|
|
||||||
flexGrow: 1,
|
|
||||||
height: "100%",
|
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "40%",
|
|
||||||
position: "relative",
|
|
||||||
padding: "16px",
|
|
||||||
zIndex: 3,
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: alignType === "center" ? "center" : "start",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{quizHeaderBlock}
|
|
||||||
{quizMainBlock}
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
zIndex: -1,
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
height: "100%",
|
|
||||||
width: "100%",
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{backgroundBlock}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case "centered": {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{isMobile ? (
|
|
||||||
<StartPageMobile />
|
|
||||||
) : (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
padding: "16px",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "100%",
|
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{quizHeaderBlock}
|
|
||||||
{backgroundBlock && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "60%",
|
|
||||||
overflow: "hidden",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center"
|
flexDirection: "column-reverse",
|
||||||
}}
|
flexGrow: 1,
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
height: "100vh",
|
||||||
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
p: "25px",
|
||||||
|
height: "80%"
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{backgroundBlock}
|
{quizHeaderBlock}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
height: "80%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{quizMainBlock}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{backgroundBlock}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
|
||||||
{quizMainBlock}
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
</>
|
|
||||||
|
|
||||||
);
|
|
||||||
|
switch (startpageType) {
|
||||||
|
case null:
|
||||||
|
case "standard": {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isMobile ? (
|
||||||
|
<StartPageMobile />
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: alignType === "left" ? (isMobile ? "column-reverse" : "row") : "row-reverse",
|
||||||
|
flexGrow: 1,
|
||||||
|
justifyContent: isMobile ? "flex-end" : undefined,
|
||||||
|
height: "100vh",
|
||||||
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: isMobile ? "100%" : "40%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
p: "25px",
|
||||||
|
height: isMobile ? "80%" : undefined
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{quizHeaderBlock}
|
||||||
|
{quizMainBlock}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: isMobile ? "100%" : "60%",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{backgroundBlock}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case "expanded": {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isMobile ? (
|
||||||
|
<StartPageMobile />
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "relative",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: startpageAlignTypeToJustifyContent[alignType],
|
||||||
|
flexGrow: 1,
|
||||||
|
height: "100%",
|
||||||
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "40%",
|
||||||
|
position: "relative",
|
||||||
|
padding: "16px",
|
||||||
|
zIndex: 3,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: alignType === "center" ? "center" : "start",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{quizHeaderBlock}
|
||||||
|
{quizMainBlock}
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
zIndex: -1,
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
height: "100%",
|
||||||
|
width: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{backgroundBlock}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case "centered": {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isMobile ? (
|
||||||
|
<StartPageMobile />
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
padding: "16px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100%",
|
||||||
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{quizHeaderBlock}
|
||||||
|
{backgroundBlock && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "60%",
|
||||||
|
overflow: "hidden",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{backgroundBlock}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{quizMainBlock}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
notReachable(startpageType);
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
notReachable(startpageType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const startpageAlignTypeToJustifyContent: Record<QuizStartpageAlignType, "start" | "center" | "end"> = {
|
const startpageAlignTypeToJustifyContent: Record<QuizStartpageAlignType, "start" | "center" | "end"> = {
|
||||||
left: "start",
|
left: "start",
|
||||||
center: "center",
|
center: "center",
|
||||||
right: "end",
|
right: "end",
|
||||||
};
|
};
|
||||||
|
@ -1,123 +1,84 @@
|
|||||||
|
import { getData } from "@api/quizRelase";
|
||||||
|
import { QuizSettings } from "@model/settingsData";
|
||||||
|
import { Box, ThemeProvider } from "@mui/material";
|
||||||
|
import { setQuizData } from "@stores/quizData/actions";
|
||||||
|
import { useQuestionsStore } from "@stores/quizData/store";
|
||||||
|
import LoadingSkeleton from "@ui_kit/LoadingSkeleton";
|
||||||
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Box, Skeleton, ThemeProvider } from "@mui/material";
|
import useSWR from "swr";
|
||||||
|
import { ApologyPage } from "./ApologyPage";
|
||||||
import { StartPageViewPublication } from "./StartPageViewPublication";
|
|
||||||
import { Question } from "./Question";
|
import { Question } from "./Question";
|
||||||
import { ApologyPage } from "./ApologyPage"
|
import { StartPageViewPublication } from "./StartPageViewPublication";
|
||||||
|
|
||||||
import { useQuestionsStore } from "@stores/quizData/store"
|
|
||||||
import { getData } from "@api/quizRelase"
|
|
||||||
|
|
||||||
import type { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
|
||||||
import { useGetSettings } from "../../utils/hooks/useGetSettings";
|
|
||||||
import { themesPublication } from "../../utils/themes/Publication/themePublication";
|
|
||||||
import { replaceSpacesToEmptyLines } from "./tools/replaceSpacesToEmptyLines";
|
import { replaceSpacesToEmptyLines } from "./tools/replaceSpacesToEmptyLines";
|
||||||
|
import { parseQuizData } from "@model/api/getQuizData";
|
||||||
|
|
||||||
|
|
||||||
const QID =
|
const QID =
|
||||||
process.env.NODE_ENV === "production" ?
|
import.meta.env.PROD ?
|
||||||
window.location.pathname.replace(/\//g, '')
|
window.location.pathname.replace(/\//g, '')
|
||||||
:
|
:
|
||||||
"0bed8483-3016-4bca-b8e0-a72c3146f18b"
|
"0bed8483-3016-4bca-b8e0-a72c3146f18b";
|
||||||
|
|
||||||
|
|
||||||
export const ViewPage = () => {
|
export const ViewPage = () => {
|
||||||
const { settings, cnt, items } = useQuestionsStore()
|
const { isLoading, error } = useSWR(["quizData", QID], params => getQuizData(params[1]), {
|
||||||
console.log("КВИЗ ", settings)
|
onSuccess: setQuizData,
|
||||||
console.log("ВОПРОСЫ ", items)
|
});
|
||||||
|
const { settings, items, recentlyСompleted } = useQuestionsStore();
|
||||||
|
const [visualStartPage, setVisualStartPage] = useState<boolean>();
|
||||||
|
|
||||||
|
useEffect(() => {//установка фавиконки
|
||||||
|
if (!settings) return;
|
||||||
|
|
||||||
const [visualStartPage, setVisualStartPage] = useState<boolean>();
|
const link = document.querySelector('link[rel="icon"]');
|
||||||
const [errormessage, setErrormessage] = useState<string>("");
|
if (link && settings.cfg.startpage.favIcon) {
|
||||||
|
link.setAttribute("href", settings?.cfg.startpage.favIcon);
|
||||||
useEffect(() => {
|
|
||||||
async function get() {
|
|
||||||
try {
|
|
||||||
let data = await getData(QID)
|
|
||||||
|
|
||||||
console.log(data)
|
|
||||||
//@ts-ignore
|
|
||||||
const settings = data.settings
|
|
||||||
|
|
||||||
console.log(data)
|
|
||||||
//@ts-ignore
|
|
||||||
data.settings = {
|
|
||||||
//@ts-ignore
|
|
||||||
qid: QID,
|
|
||||||
fp: settings.fp,
|
|
||||||
rep: settings.rep,
|
|
||||||
name: settings.name,
|
|
||||||
//@ts-ignore
|
|
||||||
cfg: JSON.parse(data?.settings.cfg),
|
|
||||||
lim: settings.lim,
|
|
||||||
due: settings.due,
|
|
||||||
delay: settings.delay,
|
|
||||||
pausable: settings.pausable
|
|
||||||
}
|
}
|
||||||
console.log(data)
|
|
||||||
//@ts-ignore
|
|
||||||
data.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
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
console.log(data)
|
|
||||||
console.log(JSON.stringify({data: data}).replaceAll(/\\\" \\\"/g, '""').replaceAll(/\" \"/g, '""'))
|
|
||||||
console.log(JSON.parse(JSON.stringify({data: data}).replaceAll(/\\\" \\\"/g, '""').replaceAll(/\" \"/g, '""')).data)
|
|
||||||
|
|
||||||
data = replaceSpacesToEmptyLines(data)
|
setVisualStartPage(!settings.cfg.noStartPage);
|
||||||
|
}, [settings]);
|
||||||
|
|
||||||
useQuestionsStore.setState(JSON.parse(JSON.stringify({data: data}).replaceAll(/\\\" \\\"/g, '""').replaceAll(/\" \"/g, '""')).data)
|
const questionsCount = items.filter(({ type }) => type !== null && type !== "result").length;
|
||||||
|
|
||||||
} catch (e) {
|
if (error) {
|
||||||
|
console.log(error);
|
||||||
//@ts-ignore
|
return <ApologyPage message="Что-то пошло не так" />;
|
||||||
if (e?.response?.status === 423) setErrormessage("квиз не активирован")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
get()
|
if (isLoading || !settings) return <LoadingSkeleton />;
|
||||||
}, [])
|
if (questionsCount === 0) return <ApologyPage message="Нет созданных вопросов" />;
|
||||||
|
|
||||||
|
return (
|
||||||
useEffect(() => {//установка фавиконки
|
<ThemeProvider theme={quizThemes[settings.cfg.theme || "StandardTheme"]}>
|
||||||
if (Object.values(settings).length > 0) {
|
{recentlyСompleted ? (
|
||||||
|
<ApologyPage message="Вы уже прошли этот опрос" />
|
||||||
const link = document.querySelector('link[rel="icon"]');
|
) : (
|
||||||
if (link && settings?.cfg.startpage.favIcon) {
|
<Box>
|
||||||
link.setAttribute("href", settings?.cfg.startpage.favIcon);
|
{visualStartPage ? (
|
||||||
}
|
<StartPageViewPublication setVisualStartPage={setVisualStartPage} />
|
||||||
|
) : (
|
||||||
setVisualStartPage(!settings?.cfg.noStartPage);
|
<Question />
|
||||||
}
|
)}
|
||||||
}, [settings]);
|
</Box>
|
||||||
|
)}
|
||||||
|
</ThemeProvider>
|
||||||
const filteredQuestions = (
|
);
|
||||||
items.filter(({ type }) => type) as AnyTypedQuizQuestion[]
|
|
||||||
).sort((previousItem, item) => previousItem.page - item.page);
|
|
||||||
|
|
||||||
|
|
||||||
if (errormessage) return <ApologyPage message={errormessage} />
|
|
||||||
if (visualStartPage === undefined) return <Skeleton sx={{ bgcolor: 'grey', width: "100vw", height: "100vh" }} variant="rectangular" />;
|
|
||||||
if (cnt === 0 || (cnt === 1 && items[0].type === "result")) return <ApologyPage message="Нет созданных вопросов" />
|
|
||||||
return (
|
|
||||||
<ThemeProvider theme={themesPublication?.[settings?.cfg.theme || "StandardTheme"]}>
|
|
||||||
<Box>
|
|
||||||
{
|
|
||||||
visualStartPage ?
|
|
||||||
<StartPageViewPublication setVisualStartPage={setVisualStartPage} />
|
|
||||||
:
|
|
||||||
<Question questions={filteredQuestions} />
|
|
||||||
}
|
|
||||||
</Box>
|
|
||||||
</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; };
|
||||||
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { DatePicker } from "@mui/x-date-pickers";
|
import { DatePicker } from "@mui/x-date-pickers";
|
||||||
import { Box, Typography, useTheme } from "@mui/material";
|
import { Box, Typography, useTheme } from "@mui/material";
|
||||||
import { modes } from "../../../utils/themes/Publication/themePublication";
|
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
|
import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
|
||||||
|
|
||||||
@ -9,6 +8,8 @@ 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 { useQuestionsStore } from "@stores/quizData/store";
|
import { useQuestionsStore } from "@stores/quizData/store";
|
||||||
|
|
||||||
type DateProps = {
|
type DateProps = {
|
||||||
@ -17,7 +18,6 @@ type DateProps = {
|
|||||||
|
|
||||||
export const Date = ({ currentQuestion }: DateProps) => {
|
export const Date = ({ currentQuestion }: DateProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const mode = modes;
|
|
||||||
|
|
||||||
const { settings } = useQuestionsStore();
|
const { settings } = useQuestionsStore();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
@ -26,6 +26,8 @@ export const Date = ({ currentQuestion }: DateProps) => {
|
|||||||
)?.answer as string;
|
)?.answer as string;
|
||||||
const [day, month, year] = answer?.split(".") || [];
|
const [day, month, year] = answer?.split(".") || [];
|
||||||
|
|
||||||
|
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}>
|
||||||
@ -41,7 +43,6 @@ export const Date = ({ currentQuestion }: DateProps) => {
|
|||||||
>
|
>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
slots={{
|
slots={{
|
||||||
//@ts-ignore
|
|
||||||
openPickerIcon: () => (
|
openPickerIcon: () => (
|
||||||
<CalendarIcon
|
<CalendarIcon
|
||||||
sx={{
|
sx={{
|
||||||
@ -72,7 +73,6 @@ export const Date = ({ currentQuestion }: DateProps) => {
|
|||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid,
|
qid: settings.qid,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ export const Date = ({ currentQuestion }: DateProps) => {
|
|||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiInputBase-root": {
|
"& .MuiInputBase-root": {
|
||||||
backgroundColor: mode[settings.cfg.theme]
|
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||||
? "white"
|
? "white"
|
||||||
: theme.palette.background.default,
|
: theme.palette.background.default,
|
||||||
borderRadius: "10px",
|
borderRadius: "10px",
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
FormControl,
|
||||||
RadioGroup,
|
FormControlLabel,
|
||||||
FormControlLabel,
|
Radio,
|
||||||
Radio,
|
RadioGroup,
|
||||||
useTheme,
|
Typography,
|
||||||
FormControl,
|
useTheme
|
||||||
useMediaQuery
|
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { modes } from "../../../utils/themes/Publication/themePublication";
|
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@stores/quizView/store";
|
import { deleteAnswer, updateAnswer, useQuizViewStore } from "@stores/quizView/store";
|
||||||
|
|
||||||
import RadioCheck from "@ui_kit/RadioCheck";
|
import RadioCheck from "@ui_kit/RadioCheck";
|
||||||
import RadioIcon from "@ui_kit/RadioIcon";
|
import RadioIcon from "@ui_kit/RadioIcon";
|
||||||
|
|
||||||
import type { QuizQuestionEmoji } from "../../../model/questionTypes/emoji";
|
|
||||||
import { enqueueSnackbar } from "notistack";
|
|
||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store"
|
|
||||||
|
import { enqueueSnackbar } from "notistack";
|
||||||
|
import type { QuizQuestionEmoji } from "../../../model/questionTypes/emoji";
|
||||||
|
import { useQuestionsStore } from "@stores/quizData/store";
|
||||||
|
|
||||||
type EmojiProps = {
|
type EmojiProps = {
|
||||||
currentQuestion: QuizQuestionEmoji;
|
currentQuestion: QuizQuestionEmoji;
|
||||||
@ -26,9 +25,7 @@ type EmojiProps = {
|
|||||||
|
|
||||||
export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const mode = modes;
|
|
||||||
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuestionsStore()
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const { answer } =
|
const { answer } =
|
||||||
@ -36,6 +33,8 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
|||||||
({ questionId }) => questionId === currentQuestion.id
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
) ?? {};
|
) ?? {};
|
||||||
|
|
||||||
|
if (!settings) throw new Error("settings is null");
|
||||||
|
|
||||||
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>
|
||||||
@ -45,7 +44,6 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
|||||||
({ id }) => answer === id
|
({ id }) => answer === id
|
||||||
)}
|
)}
|
||||||
onChange={({ target }) =>{
|
onChange={({ target }) =>{
|
||||||
console.log(currentQuestion.content.variants[Number(target.value)])
|
|
||||||
updateAnswer(
|
updateAnswer(
|
||||||
currentQuestion.id,
|
currentQuestion.id,
|
||||||
currentQuestion.content.variants[Number(target.value)].answer
|
currentQuestion.content.variants[Number(target.value)].answer
|
||||||
@ -114,7 +112,6 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
|||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: currentQuestion.content.variants[index].answer,
|
body: currentQuestion.content.variants[index].answer,
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -136,7 +133,6 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
|||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: "",
|
body: "",
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -147,7 +143,6 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
control={
|
control={
|
||||||
//@ts-ignore
|
|
||||||
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />} />
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />} />
|
||||||
}
|
}
|
||||||
label={
|
label={
|
||||||
|
@ -23,7 +23,7 @@ type FileProps = {
|
|||||||
currentQuestion: QuizQuestionFile;
|
currentQuestion: QuizQuestionFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UPLOAD_FILE_DESCRIPTIONS_MAP: Record<
|
const UPLOAD_FILE_DESCRIPTIONS_MAP: Record<
|
||||||
UploadFileType,
|
UploadFileType,
|
||||||
{ title: string; description: string }
|
{ title: string; description: string }
|
||||||
> = {
|
> = {
|
||||||
@ -49,6 +49,8 @@ export const File = ({ currentQuestion }: FileProps) => {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
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];
|
const file = target.files?.[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
|
|
||||||
@ -60,7 +62,6 @@ export const File = ({ currentQuestion }: FileProps) => {
|
|||||||
file: `${file.name}|${URL.createObjectURL(file)}`,
|
file: `${file.name}|${URL.createObjectURL(file)}`,
|
||||||
name: file.name
|
name: file.name
|
||||||
},
|
},
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@ export const Images = ({ currentQuestion }: ImagesProps) => {
|
|||||||
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 (
|
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>
|
||||||
@ -77,7 +79,6 @@ export const Images = ({ currentQuestion }: ImagesProps) => {
|
|||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: currentQuestion.content.variants[index].answer,
|
body: currentQuestion.content.variants[index].answer,
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -98,7 +99,6 @@ export const Images = ({ currentQuestion }: ImagesProps) => {
|
|||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: "",
|
body: "",
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -138,7 +138,6 @@ export const Images = ({ currentQuestion }: ImagesProps) => {
|
|||||||
}}
|
}}
|
||||||
value={index}
|
value={index}
|
||||||
control={
|
control={
|
||||||
//@ts-ignore
|
|
||||||
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main} />} icon={<RadioIcon />} />
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main} />} icon={<RadioIcon />} />
|
||||||
}
|
}
|
||||||
label={variant.answer}
|
label={variant.answer}
|
||||||
|
@ -10,218 +10,221 @@ import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
|
|||||||
import type { QuizQuestionNumber } from "../../../model/questionTypes/number";
|
import type { QuizQuestionNumber } from "../../../model/questionTypes/number";
|
||||||
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 { modes } from "../../../utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
|
import { useQuestionsStore } from "@stores/quizData/store";
|
||||||
|
|
||||||
type NumberProps = {
|
type NumberProps = {
|
||||||
currentQuestion: QuizQuestionNumber;
|
currentQuestion: QuizQuestionNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Number = ({ currentQuestion }: NumberProps) => {
|
export const Number = ({ currentQuestion }: NumberProps) => {
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuestionsStore()
|
||||||
const [minRange, setMinRange] = useState<string>("0");
|
const [minRange, setMinRange] = useState<string>("0");
|
||||||
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
|
|
||||||
|
|
||||||
const updateMinRangeDebounced = useDebouncedCallback(async (value, crowded = false) => {
|
const updateMinRangeDebounced = useDebouncedCallback(async (value, crowded = false) => {
|
||||||
if (crowded) {
|
if (!settings) return null;
|
||||||
setMinRange(maxRange);
|
|
||||||
}
|
if (crowded) {
|
||||||
|
setMinRange(maxRange);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: value,
|
body: value,
|
||||||
//@ts-ignore
|
qid: settings.qid
|
||||||
qid: settings.qid
|
})
|
||||||
})
|
|
||||||
|
|
||||||
updateAnswer(currentQuestion.id, value);
|
updateAnswer(currentQuestion.id, value);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
enqueueSnackbar("ответ не был засчитан")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}, 1000);
|
}, 1000);
|
||||||
const updateMaxRangeDebounced = useDebouncedCallback(async (value, crowded = false) => {
|
const updateMaxRangeDebounced = useDebouncedCallback(async (value, crowded = false) => {
|
||||||
if (crowded) {
|
if (!settings) return null;
|
||||||
setMaxRange(minRange);
|
|
||||||
}
|
if (crowded) {
|
||||||
try {
|
setMaxRange(minRange);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
|
||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: value,
|
body: value,
|
||||||
//@ts-ignore
|
qid: settings.qid
|
||||||
qid: settings.qid
|
})
|
||||||
})
|
|
||||||
|
|
||||||
updateAnswer(currentQuestion.id, value);
|
updateAnswer(currentQuestion.id, value);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
enqueueSnackbar("ответ не был засчитан")
|
||||||
}
|
}
|
||||||
|
|
||||||
}, 1000);
|
}, 1000);
|
||||||
const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string;
|
const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string;
|
||||||
|
|
||||||
const mode = modes;
|
const min = window.Number(currentQuestion.content.range.split("—")[0]);
|
||||||
const min = window.Number(currentQuestion.content.range.split("—")[0]);
|
const max = window.Number(currentQuestion.content.range.split("—")[1]);
|
||||||
const max = window.Number(currentQuestion.content.range.split("—")[1]);
|
const sliderValue = answer || currentQuestion.content.start + "—" + max;
|
||||||
const sliderValue = answer || currentQuestion.content.start + "—" + max;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (answer) {
|
if (answer) {
|
||||||
setMinRange(answer.split("—")[0]);
|
setMinRange(answer.split("—")[0]);
|
||||||
setMaxRange(answer.split("—")[1]);
|
setMaxRange(answer.split("—")[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!answer) {
|
if (!answer) {
|
||||||
setMinRange(String(currentQuestion.content.start));
|
setMinRange(String(currentQuestion.content.start));
|
||||||
setMaxRange(String(max));
|
setMaxRange(String(max));
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
if (!settings) throw new Error("settings is null");
|
||||||
<Box>
|
|
||||||
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
return (
|
||||||
<Box
|
<Box>
|
||||||
sx={{
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
display: "flex",
|
<Box
|
||||||
flexDirection: "column",
|
sx={{
|
||||||
width: "100%",
|
display: "flex",
|
||||||
marginTop: "20px",
|
flexDirection: "column",
|
||||||
gap: "30px",
|
width: "100%",
|
||||||
paddingRight: isMobile ? "10px" : undefined
|
marginTop: "20px",
|
||||||
}}
|
gap: "30px",
|
||||||
>
|
paddingRight: isMobile ? "10px" : undefined
|
||||||
<CustomSlider
|
}}
|
||||||
value={
|
>
|
||||||
currentQuestion.content.chooseRange
|
<CustomSlider
|
||||||
? sliderValue.split("—").length || 0 > 1
|
value={
|
||||||
? sliderValue.split("—").map((item) => window.Number(item))
|
currentQuestion.content.chooseRange
|
||||||
: [min, min + 1]
|
? sliderValue.split("—").length || 0 > 1
|
||||||
: window.Number(sliderValue.split("—")[0])
|
? sliderValue.split("—").map((item) => window.Number(item))
|
||||||
}
|
: [min, min + 1]
|
||||||
min={min}
|
: window.Number(sliderValue.split("—")[0])
|
||||||
max={max}
|
}
|
||||||
step={currentQuestion.content.step || 1}
|
min={min}
|
||||||
onChange={async (_, value) => {
|
max={max}
|
||||||
|
step={currentQuestion.content.step || 1}
|
||||||
|
onChange={async (_, value) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const range = String(value).replace(",", "—").replace (/\D/, '');
|
const range = String(value).replace(",", "—").replace (/\D/, '');
|
||||||
|
|
||||||
|
|
||||||
updateAnswer(currentQuestion.id, range);
|
updateAnswer(currentQuestion.id, range);
|
||||||
updateMinRangeDebounced(range, true);
|
updateMinRangeDebounced(range, true);
|
||||||
|
|
||||||
}}
|
}}
|
||||||
onChangeCommitted={(_, value) => {
|
onChangeCommitted={(_, value) => {
|
||||||
if (currentQuestion.content.chooseRange) {
|
if (currentQuestion.content.chooseRange) {
|
||||||
const range = value as number[];
|
const range = value as number[];
|
||||||
|
|
||||||
setMinRange(String(range[0]));
|
setMinRange(String(range[0]));
|
||||||
setMaxRange(String(range[1]));
|
setMaxRange(String(range[1]));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
//@ts-ignore
|
sx={{
|
||||||
sx={{
|
color: theme.palette.primary.main,
|
||||||
color: theme.palette.primary.main,
|
"& .MuiSlider-valueLabel": {
|
||||||
"& .MuiSlider-valueLabel": {
|
background: theme.palette.primary.main,
|
||||||
background: theme.palette.primary.main,
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
{!currentQuestion.content.chooseRange && (
|
{!currentQuestion.content.chooseRange && (
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
value={answer}
|
value={answer}
|
||||||
onChange={async ({ target }) => {
|
onChange={async ({ target }) => {
|
||||||
updateMinRangeDebounced(window.Number(target.value.replace (/\D/, '')) > max
|
updateMinRangeDebounced(window.Number(target.value.replace (/\D/, '')) > max
|
||||||
? String(max)
|
? String(max)
|
||||||
: window.Number(target.value) < min
|
: window.Number(target.value) < min
|
||||||
? String(min)
|
? String(min)
|
||||||
: target.value, true);
|
: target.value, true);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "80px",
|
maxWidth: "80px",
|
||||||
borderColor: theme.palette.text.primary,
|
borderColor: theme.palette.text.primary,
|
||||||
"& .MuiInputBase-input": {
|
"& .MuiInputBase-input": {
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
backgroundColor: mode[settings.cfg.theme] ? "white" : theme.palette.background.default,
|
backgroundColor: quizThemes[settings.cfg.theme].isLight ? "white" : theme.palette.background.default,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{currentQuestion.content.chooseRange && (
|
{currentQuestion.content.chooseRange && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
gap: "15px",
|
gap: "15px",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
"& .MuiFormControl-root": { width: "auto" },
|
"& .MuiFormControl-root": { width: "auto" },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
value={minRange}
|
value={minRange}
|
||||||
onChange={({ target }) => {
|
onChange={({ target }) => {
|
||||||
setMinRange(target.value.replace (/\D/, ''));
|
setMinRange(target.value.replace (/\D/, ''));
|
||||||
|
|
||||||
if (window.Number(target.value) >= window.Number(maxRange)) {
|
if (window.Number(target.value) >= window.Number(maxRange)) {
|
||||||
updateMinRangeDebounced(`${maxRange}—${maxRange}`, true);
|
updateMinRangeDebounced(`${maxRange}—${maxRange}`, true);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMinRangeDebounced(`${target.value}—${maxRange}`);
|
updateMinRangeDebounced(`${target.value}—${maxRange}`);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "80px",
|
maxWidth: "80px",
|
||||||
borderColor: theme.palette.text.primary,
|
borderColor: theme.palette.text.primary,
|
||||||
"& .MuiInputBase-input": {
|
"& .MuiInputBase-input": {
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
backgroundColor: mode[settings.cfg.theme] ? "white" : theme.palette.background.default,
|
backgroundColor: quizThemes[settings.cfg.theme].isLight ? "white" : theme.palette.background.default,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography color={theme.palette.text.primary}>до</Typography>
|
<Typography color={theme.palette.text.primary}>до</Typography>
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
value={maxRange}
|
value={maxRange}
|
||||||
onChange={({ target }) => {
|
onChange={({ target }) => {
|
||||||
setMaxRange(target.value.replace (/\D/, ''));
|
setMaxRange(target.value.replace (/\D/, ''));
|
||||||
|
|
||||||
if (window.Number(target.value) <= window.Number(minRange)) {
|
if (window.Number(target.value) <= window.Number(minRange)) {
|
||||||
updateMaxRangeDebounced(`${minRange}—${minRange}`, true);
|
updateMaxRangeDebounced(`${minRange}—${minRange}`, true);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMaxRangeDebounced(`${minRange}—${target.value}`);
|
updateMaxRangeDebounced(`${minRange}—${target.value}`);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "80px",
|
maxWidth: "80px",
|
||||||
borderColor: theme.palette.text.primary,
|
borderColor: theme.palette.text.primary,
|
||||||
"& .MuiInputBase-input": {
|
"& .MuiInputBase-input": {
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
backgroundColor: mode[settings.cfg.theme] ? "white" : theme.palette.background.default,
|
backgroundColor: quizThemes[settings.cfg.theme].isLight ? "white" : theme.palette.background.default,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -69,6 +69,8 @@ export const Rating = ({ currentQuestion }: RatingProps) => {
|
|||||||
({ name }) => name === currentQuestion.content.form
|
({ name }) => name === currentQuestion.content.form
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!settings) throw new Error("settings is null");
|
||||||
|
|
||||||
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>
|
||||||
@ -98,7 +100,6 @@ export const Rating = ({ currentQuestion }: RatingProps) => {
|
|||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: String(value),
|
body: String(value),
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ export const Select = ({ currentQuestion }: SelectProps) => {
|
|||||||
({ questionId }) => questionId === currentQuestion.id
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
) ?? {};
|
) ?? {};
|
||||||
|
|
||||||
|
if (!settings) throw new Error("settings is null");
|
||||||
|
|
||||||
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>
|
||||||
@ -38,10 +40,7 @@ export const Select = ({ currentQuestion }: SelectProps) => {
|
|||||||
placeholder={currentQuestion.content.default}
|
placeholder={currentQuestion.content.default}
|
||||||
activeItemIndex={answer ? Number(answer) : -1}
|
activeItemIndex={answer ? Number(answer) : -1}
|
||||||
items={currentQuestion.content.variants.map(({ answer }) => answer)}
|
items={currentQuestion.content.variants.map(({ answer }) => answer)}
|
||||||
//@ts-ignore
|
|
||||||
colorMain={theme.palette.primary.main}
|
colorMain={theme.palette.primary.main}
|
||||||
//@ts-ignore
|
|
||||||
color={theme.palette.primary.main}
|
|
||||||
onChange={async(_, value) => {
|
onChange={async(_, value) => {
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
deleteAnswer(currentQuestion.id);
|
deleteAnswer(currentQuestion.id);
|
||||||
@ -50,7 +49,6 @@ export const Select = ({ currentQuestion }: SelectProps) => {
|
|||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: "",
|
body: "",
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -65,7 +63,6 @@ export const Select = ({ currentQuestion }: SelectProps) => {
|
|||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: String(currentQuestion.content.variants[Number(value)].answer),
|
body: String(currentQuestion.content.variants[Number(value)].answer),
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -21,12 +21,13 @@ export const Text = ({ currentQuestion }: TextProps) => {
|
|||||||
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,
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,249 +1,245 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Checkbox,
|
||||||
RadioGroup,
|
FormControlLabel,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormControlLabel,
|
Radio,
|
||||||
Radio,
|
RadioGroup,
|
||||||
Checkbox,
|
TextField as MuiTextField,
|
||||||
TextField,
|
Typography,
|
||||||
useTheme,
|
useTheme,
|
||||||
|
TextFieldProps,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
import { FC, useEffect } from "react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useQuizViewStore,
|
deleteAnswer,
|
||||||
updateAnswer,
|
updateAnswer,
|
||||||
deleteAnswer,
|
updateOwnVariant,
|
||||||
updateOwnVariant,
|
useQuizViewStore
|
||||||
deleteOwnVariant,
|
|
||||||
} from "@stores/quizView/store";
|
} from "@stores/quizView/store";
|
||||||
|
|
||||||
|
import { CheckboxIcon } from "@icons/Checkbox";
|
||||||
import RadioCheck from "@ui_kit/RadioCheck";
|
import RadioCheck from "@ui_kit/RadioCheck";
|
||||||
import RadioIcon from "@ui_kit/RadioIcon";
|
import RadioIcon from "@ui_kit/RadioIcon";
|
||||||
import { CheckboxIcon } from "@icons/Checkbox";
|
|
||||||
|
|
||||||
import type { QuizQuestionVariant } from "../../../model/questionTypes/variant";
|
|
||||||
import type { QuestionVariant } from "../../../model/questionTypes/shared";
|
|
||||||
import { enqueueSnackbar } from "notistack";
|
|
||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
import { useQuestionsStore } from "@stores/quizData/store"
|
|
||||||
import { modes } from "../../../utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
|
import { enqueueSnackbar } from "notistack";
|
||||||
|
import type { QuestionVariant } from "../../../model/questionTypes/shared";
|
||||||
|
import type { QuizQuestionVariant } from "../../../model/questionTypes/variant";
|
||||||
|
import { useQuestionsStore } from "@stores/quizData/store";
|
||||||
|
|
||||||
|
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
|
||||||
|
|
||||||
type VariantProps = {
|
type VariantProps = {
|
||||||
stepNumber: number;
|
currentQuestion: QuizQuestionVariant;
|
||||||
currentQuestion: QuizQuestionVariant;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type VariantItemProps = {
|
type VariantItemProps = {
|
||||||
currentQuestion: QuizQuestionVariant;
|
currentQuestion: QuizQuestionVariant;
|
||||||
variant: QuestionVariant;
|
variant: QuestionVariant;
|
||||||
answer: string | string[] | undefined;
|
answer: string | string[] | undefined;
|
||||||
index: number;
|
index: number;
|
||||||
own?: boolean;
|
own?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Variant = ({ currentQuestion }: VariantProps) => {
|
export const Variant = ({ currentQuestion }: VariantProps) => {
|
||||||
const { settings } = useQuestionsStore()
|
const theme = useTheme();
|
||||||
const { answers, ownVariants } = useQuizViewStore();
|
const { answers, ownVariants } = useQuizViewStore();
|
||||||
const mode = modes;
|
const { answer } =
|
||||||
const { answer } =
|
answers.find(
|
||||||
answers.find(
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
({ questionId }) => questionId === currentQuestion.id
|
) ?? {};
|
||||||
) ?? {};
|
const ownVariant = ownVariants.find(
|
||||||
const ownVariant = ownVariants.find(
|
(variant) => variant.id === currentQuestion.id
|
||||||
(variant) => variant.id === currentQuestion.id
|
);
|
||||||
);
|
|
||||||
|
|
||||||
const Group = currentQuestion.content.multi ? FormGroup : RadioGroup;
|
const Group = currentQuestion.content.multi ? FormGroup : RadioGroup;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ownVariant) {
|
if (!ownVariant) {
|
||||||
updateOwnVariant(currentQuestion.id, "");
|
updateOwnVariant(currentQuestion.id, "");
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const theme = useTheme();
|
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 sx={{ display: "flex" }}>
|
||||||
<Box sx={{ display: "flex" }}>
|
<Group
|
||||||
<Group
|
name={currentQuestion.id.toString()}
|
||||||
name={currentQuestion.id}
|
value={currentQuestion.content.variants.findIndex(
|
||||||
value={currentQuestion.content.variants.findIndex(
|
({ id }) => answer === id
|
||||||
({ id }) => answer === id
|
)}
|
||||||
)}
|
sx={{
|
||||||
sx={{
|
display: "flex",
|
||||||
display: "flex",
|
flexWrap: "wrap",
|
||||||
flexWrap: "wrap",
|
flexDirection: "row",
|
||||||
flexDirection: "row",
|
justifyContent: "space-between",
|
||||||
justifyContent: "space-between",
|
flexBasis: "100%",
|
||||||
flexBasis: "100%",
|
marginTop: "20px",
|
||||||
marginTop: "20px",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
sx={{
|
display: "flex",
|
||||||
display: "flex",
|
flexDirection: "row",
|
||||||
flexDirection: "row",
|
flexWrap: "wrap",
|
||||||
flexWrap: "wrap",
|
width: "100%",
|
||||||
width: "100%",
|
gap: "20px",
|
||||||
gap: "20px",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{currentQuestion.content.variants.map((variant, index) => (
|
||||||
{currentQuestion.content.variants.map((variant, index) => (
|
<VariantItem
|
||||||
<VariantItem
|
key={variant.id}
|
||||||
key={variant.id}
|
currentQuestion={currentQuestion}
|
||||||
currentQuestion={currentQuestion}
|
variant={variant}
|
||||||
variant={variant}
|
answer={answer}
|
||||||
answer={answer}
|
index={index}
|
||||||
index={index}
|
/>
|
||||||
/>
|
))}
|
||||||
))}
|
{currentQuestion.content.own && ownVariant && (
|
||||||
{currentQuestion.content.own && ownVariant && (
|
<VariantItem
|
||||||
<VariantItem
|
own
|
||||||
own
|
currentQuestion={currentQuestion}
|
||||||
currentQuestion={currentQuestion}
|
variant={ownVariant.variant}
|
||||||
variant={ownVariant.variant}
|
answer={answer}
|
||||||
answer={answer}
|
index={currentQuestion.content.variants.length + 2}
|
||||||
index={currentQuestion.content.variants.length + 2}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
</Box>
|
||||||
</Box>
|
</Group>
|
||||||
</Group>
|
{currentQuestion.content.back && currentQuestion.content.back !== " " && (
|
||||||
{currentQuestion.content.back && currentQuestion.content.back !== " " && (
|
<Box sx={{ maxWidth: "400px", width: "100%", height: "300px" }}>
|
||||||
<Box sx={{ maxWidth: "400px", width: "100%", height: "300px" }}>
|
<img
|
||||||
<img
|
key={currentQuestion.id}
|
||||||
key={currentQuestion.id}
|
src={currentQuestion.content.back}
|
||||||
src={currentQuestion.content.back}
|
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
alt=""
|
||||||
alt=""
|
/>
|
||||||
/>
|
</Box>
|
||||||
</Box>
|
)}
|
||||||
)}
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
);
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const VariantItem = ({
|
const VariantItem = ({
|
||||||
currentQuestion,
|
currentQuestion,
|
||||||
variant,
|
variant,
|
||||||
answer,
|
answer,
|
||||||
index,
|
index,
|
||||||
own = false,
|
own = false,
|
||||||
}: VariantItemProps) => {
|
}: VariantItemProps) => {
|
||||||
const { settings } = useQuestionsStore()
|
const { settings } = useQuestionsStore()
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const mode = modes;
|
|
||||||
|
|
||||||
return (
|
if (!settings) throw new Error("settings is null");
|
||||||
<FormControlLabel
|
|
||||||
key={variant.id}
|
|
||||||
sx={{
|
|
||||||
margin: "0",
|
|
||||||
borderRadius: "12px",
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
padding: "15px",
|
|
||||||
border: `1px solid`,
|
|
||||||
borderColor:
|
|
||||||
answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
|
||||||
backgroundColor: mode[settings.cfg.theme]
|
|
||||||
? "white"
|
|
||||||
: theme.palette.background.default,
|
|
||||||
display: "flex",
|
|
||||||
maxWidth: "685px",
|
|
||||||
maxHeight: "85px",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
width: "100%",
|
|
||||||
"&.MuiFormControl-root": {
|
|
||||||
width: "100%",
|
|
||||||
},
|
|
||||||
"& .MuiFormControlLabel-label": {
|
|
||||||
wordBreak: "break-word"
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={index}
|
|
||||||
labelPlacement="start"
|
|
||||||
control={
|
|
||||||
currentQuestion.content.multi ? (
|
|
||||||
<Checkbox
|
|
||||||
checked={!!answer?.includes(variant.id)}
|
|
||||||
checkedIcon={<CheckboxIcon checked color={theme.palette.primary.main} />}
|
|
||||||
icon={<CheckboxIcon />}
|
|
||||||
/>
|
|
||||||
) :
|
|
||||||
//@ts-ignore
|
|
||||||
(<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main} />} icon={<RadioIcon />} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
//@ts-ignore
|
|
||||||
label={own ? <TextField label="Другое..." /> : variant.answer}
|
|
||||||
onClick={async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
const variantId = currentQuestion.content.variants[index].id;
|
|
||||||
|
|
||||||
if (currentQuestion.content.multi) {
|
return (
|
||||||
const currentAnswer = typeof answer !== "string" ? answer || [] : [];
|
<FormControlLabel
|
||||||
|
key={variant.id}
|
||||||
|
sx={{
|
||||||
|
margin: "0",
|
||||||
|
borderRadius: "12px",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
padding: "15px",
|
||||||
|
border: `1px solid`,
|
||||||
|
borderColor: answer === variant.id
|
||||||
|
? theme.palette.primary.main
|
||||||
|
: "#9A9AAF",
|
||||||
|
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||||
|
? "white"
|
||||||
|
: theme.palette.background.default,
|
||||||
|
display: "flex",
|
||||||
|
maxWidth: "685px",
|
||||||
|
maxHeight: "85px",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
"&.MuiFormControl-root": {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
"& .MuiFormControlLabel-label": {
|
||||||
|
wordBreak: "break-word"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={index}
|
||||||
|
labelPlacement="start"
|
||||||
|
control={
|
||||||
|
currentQuestion.content.multi ?
|
||||||
|
<Checkbox
|
||||||
|
checked={!!answer?.includes(variant.id)}
|
||||||
|
checkedIcon={<CheckboxIcon checked color={theme.palette.primary.main} />}
|
||||||
|
icon={<CheckboxIcon />}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main} />} icon={<RadioIcon />} />
|
||||||
|
}
|
||||||
|
label={own ? <TextField label="Другое..." /> : variant.answer}
|
||||||
|
onClick={async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const variantId = currentQuestion.content.variants[index].id;
|
||||||
|
|
||||||
try {
|
if (currentQuestion.content.multi) {
|
||||||
|
const currentAnswer = typeof answer !== "string" ? answer || [] : [];
|
||||||
|
|
||||||
await sendAnswer({
|
try {
|
||||||
questionId: currentQuestion.id,
|
|
||||||
body: currentAnswer.includes(variantId)
|
|
||||||
? currentAnswer?.filter((item) => item !== variantId)
|
|
||||||
: [...currentAnswer, variantId],
|
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
|
||||||
})
|
|
||||||
|
|
||||||
updateAnswer(
|
await sendAnswer({
|
||||||
currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
currentAnswer.includes(variantId)
|
body: currentAnswer.includes(variantId)
|
||||||
? currentAnswer?.filter((item) => item !== variantId)
|
? currentAnswer?.filter((item) => item !== variantId)
|
||||||
: [...currentAnswer, variantId]
|
: [...currentAnswer, variantId],
|
||||||
);
|
qid: settings.qid
|
||||||
|
})
|
||||||
|
|
||||||
} catch (e) {
|
updateAnswer(
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
currentQuestion.id,
|
||||||
}
|
currentAnswer.includes(variantId)
|
||||||
|
? currentAnswer?.filter((item) => item !== variantId)
|
||||||
|
: [...currentAnswer, variantId]
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
enqueueSnackbar("ответ не был засчитан")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: currentQuestion.content.variants[index].answer,
|
body: currentQuestion.content.variants[index].answer,
|
||||||
//@ts-ignore
|
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) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: "",
|
body: "",
|
||||||
//@ts-ignore
|
qid: settings.qid
|
||||||
qid: settings.qid
|
})
|
||||||
})
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
enqueueSnackbar("ответ не был засчитан")
|
||||||
}
|
}
|
||||||
deleteAnswer(currentQuestion.id);
|
deleteAnswer(currentQuestion.id);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
useMediaQuery
|
useMediaQuery
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { modes } from "../../../utils/themes/Publication/themePublication";
|
|
||||||
|
|
||||||
import gag from "./gag.png"
|
import gag from "./gag.png"
|
||||||
|
|
||||||
@ -20,6 +19,7 @@ import RadioIcon from "@ui_kit/RadioIcon";
|
|||||||
import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg";
|
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";
|
||||||
|
|
||||||
type VarimgProps = {
|
type VarimgProps = {
|
||||||
currentQuestion: QuizQuestionVarImg;
|
currentQuestion: QuizQuestionVarImg;
|
||||||
@ -30,7 +30,7 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
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 mode = modes;
|
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.id
|
({ questionId }) => questionId === currentQuestion.id
|
||||||
@ -38,6 +38,9 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
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 (
|
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>
|
||||||
@ -70,7 +73,7 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
padding: "15px",
|
padding: "15px",
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
backgroundColor: mode[settings.cfg.theme] ? "white" : theme.palette.background.default,
|
backgroundColor: quizThemes[settings.cfg.theme].isLight ? "white" : theme.palette.background.default,
|
||||||
border: `1px solid`,
|
border: `1px solid`,
|
||||||
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -82,15 +85,14 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
value={index}
|
value={index}
|
||||||
onClick={async(event) => {
|
onClick={async(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: currentQuestion.content.variants[index].answer,
|
body: currentQuestion.content.variants[index].answer,
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -98,11 +100,11 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
currentQuestion.id,
|
currentQuestion.id,
|
||||||
currentQuestion.content.variants[index].id
|
currentQuestion.content.variants[index].id
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
enqueueSnackbar("ответ не был засчитан")
|
enqueueSnackbar("ответ не был засчитан")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (answer === currentQuestion.content.variants[index].id) {
|
if (answer === currentQuestion.content.variants[index].id) {
|
||||||
try {
|
try {
|
||||||
@ -110,7 +112,6 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: "",
|
body: "",
|
||||||
//@ts-ignore
|
|
||||||
qid: settings.qid
|
qid: settings.qid
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -121,7 +122,6 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
control={
|
control={
|
||||||
//@ts-ignore
|
|
||||||
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />} />
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />} />
|
||||||
}
|
}
|
||||||
label={variant.answer}
|
label={variant.answer}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
import { useQuestionsStore } from "./store";
|
import { useQuestionsStore } from "./store";
|
||||||
|
import { QuizSettings } from "@model/settingsData";
|
||||||
|
|
||||||
|
|
||||||
export const getQuestionById = (questionId: string | null):AnyTypedQuizQuestion | null => {
|
export const getQuestionById = (questionId: string | null): AnyTypedQuizQuestion | null => {
|
||||||
if (questionId === null) return null;
|
if (questionId === null) return null;
|
||||||
//@ts-ignore
|
|
||||||
return useQuestionsStore.getState().items.find(q => q.id === questionId || q.content.id === questionId) || null;
|
return useQuestionsStore.getState().items.find(q => q.id === questionId || q.content.id === questionId) || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setQuizData = (quizData: QuizSettings) => useQuestionsStore.setState(quizData);
|
||||||
|
@ -1,25 +1,29 @@
|
|||||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
import { QuizSettings } from "@model/settingsData";
|
||||||
import { Settings, QuestionsStore } from "@model/settingsData";
|
|
||||||
import { QuizConfig } from "@model/quizSettings";
|
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { devtools } from "zustand/middleware";
|
import { devtools } from "zustand/middleware";
|
||||||
|
|
||||||
const initialState: QuestionsStore = {
|
|
||||||
//@ts-ignore
|
type QuizDataStore = {
|
||||||
settings: {},
|
settings: QuizSettings["settings"] | null;
|
||||||
//@ts-ignore
|
items: QuizSettings["items"];
|
||||||
items: [],
|
cnt: QuizSettings["cnt"];
|
||||||
cnt: 0
|
recentlyСompleted: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useQuestionsStore = create<QuestionsStore>()(
|
const initialState: QuizDataStore = {
|
||||||
|
settings: null,
|
||||||
|
items: [],
|
||||||
|
cnt: 0,
|
||||||
|
recentlyСompleted: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useQuestionsStore = create<QuizDataStore>()(
|
||||||
devtools(
|
devtools(
|
||||||
() => initialState,
|
() => initialState,
|
||||||
{
|
{
|
||||||
name: "QuestionsStore",
|
name: "QuizDataStore",
|
||||||
enabled: process.env.NODE_ENV === "development",
|
enabled: import.meta.env.DEV,
|
||||||
trace: process.env.NODE_ENV === "development",
|
trace: import.meta.env.DEV,
|
||||||
actionsBlacklist: "ignored",
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -1,95 +1,92 @@
|
|||||||
|
import { QuestionVariant } from "@model/questionTypes/shared";
|
||||||
|
import { produce } from "immer";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { devtools } from "zustand/middleware";
|
import { devtools } from "zustand/middleware";
|
||||||
|
|
||||||
|
|
||||||
type Answer = {
|
type Answer = {
|
||||||
questionId: string;
|
questionId: string;
|
||||||
answer: string | string[];
|
answer: string | string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type OwnVariant = {
|
type OwnVariant = {
|
||||||
id: string;
|
id: string;
|
||||||
variant: any;
|
variant: QuestionVariant;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface QuizViewStore {
|
interface QuizViewStore {
|
||||||
answers: Answer[];
|
answers: Answer[];
|
||||||
ownVariants: OwnVariant[];
|
ownVariants: OwnVariant[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useQuizViewStore = create<QuizViewStore>()(
|
export const useQuizViewStore = create<QuizViewStore>()(
|
||||||
devtools(
|
devtools(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
answers: [],
|
answers: [],
|
||||||
ownVariants: [],
|
ownVariants: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "quizView",
|
name: "quizView",
|
||||||
}
|
enabled: import.meta.env.DEV,
|
||||||
)
|
trace: import.meta.env.DEV,
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const updateAnswer = (questionId: string, answer: string | string[]) => {
|
function setProducedState<A extends string | { type: string; }>(
|
||||||
const answers = [...useQuizViewStore.getState().answers];
|
recipe: (state: QuizViewStore) => void,
|
||||||
const answerIndex = answers.findIndex(
|
action: A,
|
||||||
(answer) => questionId === answer.questionId
|
) {
|
||||||
);
|
useQuizViewStore.setState(state => produce(state, recipe), false, action);
|
||||||
|
|
||||||
if (answerIndex < 0) {
|
|
||||||
answers.push({ questionId, answer });
|
|
||||||
} else {
|
|
||||||
answers[answerIndex] = { questionId, answer };
|
|
||||||
}
|
|
||||||
|
|
||||||
useQuizViewStore.setState({ answers });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteAnswer = (questionId: string) => {
|
|
||||||
const answers = [...useQuizViewStore.getState().answers];
|
|
||||||
const filteredItems = answers.filter(
|
|
||||||
(answer) => questionId !== answer.questionId
|
|
||||||
);
|
|
||||||
|
|
||||||
useQuizViewStore.setState({ answers: filteredItems });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateOwnVariant = (id: string, answer: string) => {
|
|
||||||
const ownVariants = [...useQuizViewStore.getState().ownVariants];
|
|
||||||
const ownVariantIndex = ownVariants.findIndex(
|
|
||||||
(variant) => variant.id === id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ownVariantIndex < 0) {
|
|
||||||
ownVariants.push({
|
|
||||||
id,
|
|
||||||
variant: {
|
|
||||||
id: getRandom(),
|
|
||||||
answer,
|
|
||||||
extendedText: "",
|
|
||||||
hints: "",
|
|
||||||
originalImageUrl: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ownVariants[ownVariantIndex].variant.answer = answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
useQuizViewStore.setState({ ownVariants });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteOwnVariant = (id: string) => {
|
|
||||||
const ownVariants = [...useQuizViewStore.getState().ownVariants];
|
|
||||||
|
|
||||||
const filteredOwnVariants = ownVariants.filter(
|
|
||||||
(variant) => variant.id !== id
|
|
||||||
);
|
|
||||||
|
|
||||||
useQuizViewStore.setState({ ownVariants: filteredOwnVariants });
|
|
||||||
};
|
|
||||||
|
|
||||||
function getRandom() {
|
|
||||||
const min = Math.ceil(1000000);
|
|
||||||
const max = Math.floor(10000000);
|
|
||||||
|
|
||||||
return String(Math.floor(Math.random() * (max - min)) + min);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateAnswer = (questionId: string, answer: string | string[]) => setProducedState(state => {
|
||||||
|
const index = state.answers.findIndex(answer => questionId === answer.questionId);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
state.answers.push({ questionId, answer });
|
||||||
|
} else {
|
||||||
|
state.answers[index] = { questionId, answer };
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
type: "updateAnswer",
|
||||||
|
questionId,
|
||||||
|
answer
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteAnswer = (questionId: string) => useQuizViewStore.setState(state => ({
|
||||||
|
answers: state.answers.filter(answer => questionId !== answer.questionId)
|
||||||
|
}), false, {
|
||||||
|
type: "deleteAnswer",
|
||||||
|
questionId
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateOwnVariant = (id: string, answer: string) => setProducedState(state => {
|
||||||
|
const index = state.ownVariants.findIndex((variant) => variant.id === id);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
state.ownVariants.push({
|
||||||
|
id,
|
||||||
|
variant: {
|
||||||
|
id: nanoid(),
|
||||||
|
answer,
|
||||||
|
extendedText: "",
|
||||||
|
hints: "",
|
||||||
|
originalImageUrl: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
state.ownVariants[index].variant.answer = answer;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
type: "updateOwnVariant",
|
||||||
|
id,
|
||||||
|
answer
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteOwnVariant = (id: string) => useQuizViewStore.setState(state => ({
|
||||||
|
ownVariants: state.ownVariants.filter((variant) => variant.id !== id)
|
||||||
|
}), false, {
|
||||||
|
type: "deleteOwnVariant",
|
||||||
|
id
|
||||||
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { FormControlLabel, Checkbox, useTheme, Box, useMediaQuery } from "@mui/material";
|
import { Checkbox, FormControlLabel } from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { CheckboxIcon } from "@icons/Checkbox";
|
import { CheckboxIcon } from "@icons/Checkbox";
|
||||||
@ -15,8 +15,6 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function CustomCheckbox({ label, handleChange, checked, sx, dataCy, colorIcon }: Props) {
|
export default function CustomCheckbox({ label, handleChange, checked, sx, dataCy, colorIcon }: Props) {
|
||||||
const theme = useTheme();
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
@ -25,7 +23,6 @@ export default function CustomCheckbox({ label, handleChange, checked, sx, dataC
|
|||||||
sx={{ padding: "0px 13px 1px 11px" }}
|
sx={{ padding: "0px 13px 1px 11px" }}
|
||||||
disableRipple
|
disableRipple
|
||||||
icon={<CheckboxIcon />}
|
icon={<CheckboxIcon />}
|
||||||
//@ts-ignore
|
|
||||||
checkedIcon={<CheckboxIcon checked color={colorIcon} />}
|
checkedIcon={<CheckboxIcon checked color={colorIcon} />}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
|
17
src/ui_kit/LoadingSkeleton.tsx
Normal file
17
src/ui_kit/LoadingSkeleton.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Skeleton } from "@mui/material";
|
||||||
|
|
||||||
|
|
||||||
|
export default function LoadingSkeleton() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Skeleton
|
||||||
|
component="div"
|
||||||
|
variant="rectangular"
|
||||||
|
sx={{
|
||||||
|
bgcolor: "grey",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
import { createTheme } from "@mui/material";
|
import { QuizTheme } from "@model/settingsData";
|
||||||
|
import { Theme, createTheme } from "@mui/material";
|
||||||
import themePublic from "./genericPublication";
|
import themePublic from "./genericPublication";
|
||||||
import theme from "../generic";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const StandardTheme = createTheme({
|
const StandardTheme = createTheme({
|
||||||
@ -224,30 +223,16 @@ const BlueDarkTheme = createTheme({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const modes = {
|
export const quizThemes: Record<QuizTheme, { theme: Theme; isLight: boolean; }> = {
|
||||||
StandardTheme: true,
|
StandardTheme: { theme: StandardTheme, isLight: true },
|
||||||
StandardDarkTheme: false,
|
StandardDarkTheme: { theme: StandardDarkTheme, isLight: false },
|
||||||
PinkTheme: true,
|
PinkTheme: { theme: PinkTheme, isLight: true },
|
||||||
PinkDarkTheme: false,
|
PinkDarkTheme: { theme: PinkDarkTheme, isLight: false },
|
||||||
BlackWhiteTheme: true,
|
BlackWhiteTheme: { theme: BlackWhiteTheme, isLight: true },
|
||||||
OliveTheme: true,
|
OliveTheme: { theme: OliveTheme, isLight: true },
|
||||||
YellowTheme: true,
|
YellowTheme: { theme: YellowTheme, isLight: true },
|
||||||
GoldDarkTheme: false,
|
GoldDarkTheme: { theme: GoldDarkTheme, isLight: false },
|
||||||
PurpleTheme: true,
|
PurpleTheme: { theme: PurpleTheme, isLight: true },
|
||||||
BlueTheme: true,
|
BlueTheme: { theme: BlueTheme, isLight: true },
|
||||||
BlueDarkTheme: false
|
BlueDarkTheme: { theme: BlueDarkTheme, isLight: false },
|
||||||
}
|
};
|
||||||
|
|
||||||
export const themesPublication = {
|
|
||||||
StandardTheme,
|
|
||||||
StandardDarkTheme,
|
|
||||||
PinkTheme,
|
|
||||||
PinkDarkTheme,
|
|
||||||
BlackWhiteTheme,
|
|
||||||
OliveTheme,
|
|
||||||
YellowTheme,
|
|
||||||
GoldDarkTheme,
|
|
||||||
PurpleTheme,
|
|
||||||
BlueTheme,
|
|
||||||
BlueDarkTheme,
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user