feat: design logic
This commit is contained in:
parent
284472c5bb
commit
69ea201ddc
@ -1,6 +1,9 @@
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { ReactNode } from "react";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
|
||||
import Stepper from "@ui_kit/Stepper";
|
||||
|
||||
type FooterProps = {
|
||||
stepNumber: number | null;
|
||||
@ -10,8 +13,10 @@ type FooterProps = {
|
||||
|
||||
export const Footer = ({ stepNumber, nextButton, prevButton }: FooterProps) => {
|
||||
const theme = useTheme();
|
||||
const { questions } = useQuizData();
|
||||
console.log(questions);
|
||||
const { questions, settings } = useQuizData();
|
||||
const questionsAmount = questions.filter(
|
||||
({ type }) => type !== "result"
|
||||
).length;
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -21,13 +26,16 @@ export const Footer = ({ stepNumber, nextButton, prevButton }: FooterProps) => {
|
||||
borderTop: `1px solid ${theme.palette.grey[400]}`,
|
||||
height: "75px",
|
||||
display: "flex",
|
||||
background: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: "transparent",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
maxWidth: "1000px",
|
||||
padding: "0 10px",
|
||||
maxWidth: "1410px",
|
||||
padding: "10px",
|
||||
margin: "0 auto",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
@ -40,35 +48,11 @@ export const Footer = ({ stepNumber, nextButton, prevButton }: FooterProps) => {
|
||||
{/* <NameplateLogoFQDark style={{ fontSize: "34px", width:"200px", height:"auto" }} />*/}
|
||||
{/*)}*/}
|
||||
{stepNumber !== null && (
|
||||
<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" }}>
|
||||
{questions.filter((q) => q.type !== "result").length}
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Typography sx={{ color: theme.palette.text.primary }}>
|
||||
Вопрос {stepNumber} из {questionsAmount}
|
||||
</Typography>
|
||||
<Stepper activeStep={stepNumber} steps={questionsAmount} />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
@ -33,7 +33,7 @@ import Desgin9 from "@icons/designs/design9.jpg";
|
||||
import Desgin10 from "@icons/designs/design10.jpg";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import type { Design } from "@model/settingsData";
|
||||
import type { QuizTheme } from "@model/settingsData";
|
||||
|
||||
type Props = {
|
||||
currentQuestion: RealTypedQuizQuestion;
|
||||
@ -42,18 +42,28 @@ type Props = {
|
||||
prevButton: ReactNode;
|
||||
};
|
||||
|
||||
export const DESIGN_LIST: Record<Design, string> = {
|
||||
"": "",
|
||||
design1: Desgin1,
|
||||
design2: Desgin2,
|
||||
design3: Desgin3,
|
||||
design4: Desgin4,
|
||||
design5: Desgin5,
|
||||
design6: Desgin6,
|
||||
design7: Desgin7,
|
||||
design8: Desgin8,
|
||||
design9: Desgin9,
|
||||
design10: Desgin10,
|
||||
export const DESIGN_LIST: Record<QuizTheme, string> = {
|
||||
Design1: Desgin1,
|
||||
Design2: Desgin2,
|
||||
Design3: Desgin3,
|
||||
Design4: Desgin4,
|
||||
Design5: Desgin5,
|
||||
Design6: Desgin6,
|
||||
Design7: Desgin7,
|
||||
Design8: Desgin8,
|
||||
Design9: Desgin9,
|
||||
Design10: Desgin10,
|
||||
StandardTheme: "",
|
||||
StandardDarkTheme: "",
|
||||
PinkTheme: "",
|
||||
PinkDarkTheme: "",
|
||||
BlackWhiteTheme: "",
|
||||
OliveTheme: "",
|
||||
YellowTheme: "",
|
||||
GoldDarkTheme: "",
|
||||
PurpleTheme: "",
|
||||
BlueTheme: "",
|
||||
BlueDarkTheme: "",
|
||||
};
|
||||
|
||||
export const Question = ({
|
||||
@ -64,19 +74,20 @@ export const Question = ({
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const { settings } = useQuizData();
|
||||
const design = DESIGN_LIST[settings.cfg.design];
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
backgroundPosition: "center",
|
||||
backgroundSize: "cover",
|
||||
backgroundImage: `url(${design})`,
|
||||
backgroundImage: settings.cfg.design
|
||||
? `url(${DESIGN_LIST[settings.cfg.theme]})`
|
||||
: null,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
background: design
|
||||
background: settings.cfg.design
|
||||
? "linear-gradient(90deg,#272626, transparent)"
|
||||
: theme.palette.background.default,
|
||||
}}
|
||||
@ -100,19 +111,21 @@ export const Question = ({
|
||||
question={currentQuestion}
|
||||
stepNumber={currentQuestionStepNumber}
|
||||
/>
|
||||
{quizThemes[settings.cfg.theme].isLight ? (
|
||||
<Link target={"_blank"} href={"https://quiz.pena.digital"}>
|
||||
<NameplateLogoFQ
|
||||
style={{ fontSize: "34px", width: "200px", height: "auto" }}
|
||||
/>
|
||||
</Link>
|
||||
) : (
|
||||
<Link target={"_blank"} href={"https://quiz.pena.digital"}>
|
||||
<NameplateLogoFQDark
|
||||
style={{ fontSize: "34px", width: "200px", height: "auto" }}
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
<Box sx={{ marginLeft: "auto" }}>
|
||||
{quizThemes[settings.cfg.theme].isLight ? (
|
||||
<Link target={"_blank"} href={"https://quiz.pena.digital"}>
|
||||
<NameplateLogoFQ
|
||||
style={{ fontSize: "34px", width: "200px", height: "auto" }}
|
||||
/>
|
||||
</Link>
|
||||
) : (
|
||||
<Link target={"_blank"} href={"https://quiz.pena.digital"}>
|
||||
<NameplateLogoFQDark
|
||||
style={{ fontSize: "34px", width: "200px", height: "auto" }}
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<Footer
|
||||
stepNumber={currentQuestionStepNumber}
|
||||
|
@ -44,11 +44,13 @@ export const StartPageViewPublication = () => {
|
||||
}
|
||||
alt=""
|
||||
style={{
|
||||
display: "block",
|
||||
width:
|
||||
isMobile || settings.cfg.startpageType === "expanded"
|
||||
? "100%"
|
||||
: undefined,
|
||||
height: "100%",
|
||||
maxHeight: "100vh",
|
||||
objectFit: "cover",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
@ -214,7 +216,7 @@ export const StartPageViewPublication = () => {
|
||||
fontSize: "18px",
|
||||
padding: "10px 30px",
|
||||
width: "auto",
|
||||
background: "#7E2AEA",
|
||||
background: theme.palette.primary.main,
|
||||
}}
|
||||
onClick={() => setCurrentQuizStep("question")}
|
||||
>
|
||||
@ -248,7 +250,7 @@ export const StartPageViewPublication = () => {
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
color: "#7E2AEA",
|
||||
color: theme.palette.primary.main,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
@ -262,19 +264,35 @@ export const StartPageViewPublication = () => {
|
||||
{settings.cfg.info.clickable ? (
|
||||
isMobileDevice ? (
|
||||
<Link href={`tel:${settings.cfg.info.phonenumber}`}>
|
||||
<Typography sx={{ fontSize: "16px" }}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
{settings.cfg.info.phonenumber}
|
||||
</Typography>
|
||||
</Link>
|
||||
) : (
|
||||
<ButtonBase onClick={handleCopyNumber}>
|
||||
<Typography sx={{ fontSize: "16px" }}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
{settings.cfg.info.phonenumber}
|
||||
</Typography>
|
||||
</ButtonBase>
|
||||
)
|
||||
) : (
|
||||
<Typography sx={{ fontSize: "16px", marginTop: "5px" }}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
marginTop: "5px",
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
{settings.cfg.info.phonenumber}
|
||||
</Typography>
|
||||
)}
|
||||
|
@ -61,7 +61,16 @@ export default function ViewPublicationPage() {
|
||||
<Button
|
||||
disabled={!isPreviousButtonEnabled}
|
||||
variant="contained"
|
||||
sx={{ fontSize: "16px", padding: "10px 15px" }}
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
padding: "10px 15px",
|
||||
color: "#FFFFFF",
|
||||
border: "1px solid #9A9AAF",
|
||||
background: "rgba(154,154,175, 0.2)",
|
||||
"&:disabled": {
|
||||
color: "#9A9AAF",
|
||||
},
|
||||
}}
|
||||
onClick={moveToPrevQuestion}
|
||||
>
|
||||
{isMobileMini ? "←" : "← Назад"}
|
||||
|
@ -14,97 +14,99 @@ import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useState } from "react";
|
||||
|
||||
type DateProps = {
|
||||
currentQuestion: QuizQuestionDate;
|
||||
currentQuestion: QuizQuestionDate;
|
||||
};
|
||||
|
||||
export const Date = ({ currentQuestion }: DateProps) => {
|
||||
const theme = useTheme();
|
||||
const { settings, quizId } = useQuizData();
|
||||
const { answers } = useQuizViewStore();
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
const currentAnswer = moment(answer) || moment();
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const theme = useTheme();
|
||||
const { settings, quizId } = useQuizData();
|
||||
const { answers } = useQuizViewStore();
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
const currentAnswer = moment(answer) || moment();
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h5" color={theme.palette.text.primary} sx={{ wordBreak: "break-word" }}>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<DatePicker
|
||||
slots={{
|
||||
openPickerIcon: () => (
|
||||
<CalendarIcon
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
"& path": { stroke: theme.palette.primary.main },
|
||||
"& rect": { stroke: theme.palette.primary.main },
|
||||
}}
|
||||
>
|
||||
<DatePicker
|
||||
slots={{
|
||||
openPickerIcon: () => (
|
||||
<CalendarIcon
|
||||
sx={{
|
||||
"& path": { stroke: theme.palette.primary.main },
|
||||
"& rect": { stroke: theme.palette.primary.main },
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
value={currentAnswer}
|
||||
onChange={async (date) => {
|
||||
if (isSending || !date) return;
|
||||
/>
|
||||
),
|
||||
}}
|
||||
value={currentAnswer}
|
||||
onChange={async (date) => {
|
||||
if (isSending || !date) return;
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: moment(date).format("YYYY.MM.DD"),
|
||||
qid: quizId,
|
||||
});
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: moment(date).format("YYYY.MM.DD"),
|
||||
qid: quizId,
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
date,
|
||||
0
|
||||
);
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
updateAnswer(currentQuestion.id, date, 0);
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}}
|
||||
slotProps={{
|
||||
openPickerButton: {
|
||||
sx: {
|
||||
p: 0,
|
||||
},
|
||||
"data-cy": "open-datepicker",
|
||||
},
|
||||
layout: {
|
||||
sx: { backgroundColor: theme.palette.background.default },
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
"& .MuiInputBase-root": {
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
borderRadius: "10px",
|
||||
maxWidth: "250px",
|
||||
pr: "22px",
|
||||
"& input": {
|
||||
py: "11px",
|
||||
pl: "20px",
|
||||
lineHeight: "19px",
|
||||
},
|
||||
"& fieldset": {
|
||||
borderColor: "#9A9AAF",
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
setIsSending(false);
|
||||
}}
|
||||
slotProps={{
|
||||
openPickerButton: {
|
||||
sx: {
|
||||
p: 0,
|
||||
},
|
||||
"data-cy": "open-datepicker",
|
||||
},
|
||||
layout: {
|
||||
sx: { backgroundColor: theme.palette.background.default },
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
"& .MuiInputBase-root": {
|
||||
backgroundColor: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
borderRadius: "10px",
|
||||
maxWidth: "250px",
|
||||
pr: "22px",
|
||||
"& input": {
|
||||
py: "11px",
|
||||
pl: "20px",
|
||||
lineHeight: "19px",
|
||||
},
|
||||
"& fieldset": {
|
||||
borderColor: "#9A9AAF",
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -189,7 +189,7 @@ export const File = ({ currentQuestion }: FileProps) => {
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
padding: "33px 44px 33px 55px",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
backgroundColor: "#F2F3F7",
|
||||
border: `1px solid ${isDropzoneHighlighted ? "red" : "#9A9AAF"}`,
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
|
@ -18,445 +18,458 @@ import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import type { ChangeEvent, SyntheticEvent } from "react";
|
||||
|
||||
type NumberProps = {
|
||||
currentQuestion: QuizQuestionNumber;
|
||||
currentQuestion: QuizQuestionNumber;
|
||||
};
|
||||
|
||||
export const Number = ({ currentQuestion }: NumberProps) => {
|
||||
const { settings, quizId } = useQuizData();
|
||||
const [inputValue, setInputValue] = useState<string>("0");
|
||||
const [minRange, setMinRange] = useState<string>("0");
|
||||
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
||||
const [reversedInputValue, setReversedInputValue] = useState<string>("0");
|
||||
const [reversedMinRange, setReversedMinRange] = useState<string>("0");
|
||||
const [reversedMaxRange, setReversedMaxRange] =
|
||||
useState<string>("100000000000");
|
||||
const theme = useTheme();
|
||||
const { answers } = useQuizViewStore();
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const { settings, quizId } = useQuizData();
|
||||
const [inputValue, setInputValue] = useState<string>("0");
|
||||
const [minRange, setMinRange] = useState<string>("0");
|
||||
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
||||
const [reversedInputValue, setReversedInputValue] = useState<string>("0");
|
||||
const [reversedMinRange, setReversedMinRange] = useState<string>("0");
|
||||
const [reversedMaxRange, setReversedMaxRange] =
|
||||
useState<string>("100000000000");
|
||||
const theme = useTheme();
|
||||
const { answers } = useQuizViewStore();
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const [minBorder, maxBorder] = currentQuestion.content.range
|
||||
.split("—")
|
||||
.map(window.Number);
|
||||
const min = minBorder < maxBorder ? minBorder : maxBorder;
|
||||
const max = minBorder < maxBorder ? maxBorder : minBorder;
|
||||
const reversed = minBorder > maxBorder;
|
||||
|
||||
useEffect(() => {
|
||||
console.log("reversed:", reversed)
|
||||
}, [reversed])
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const [minBorder, maxBorder] = currentQuestion.content.range
|
||||
.split("—")
|
||||
.map(window.Number);
|
||||
const min = minBorder < maxBorder ? minBorder : maxBorder;
|
||||
const max = minBorder < maxBorder ? maxBorder : minBorder;
|
||||
const reversed = minBorder > maxBorder;
|
||||
|
||||
const sendAnswerToBackend = async (value: string, noUpdate = false) => {
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: value,
|
||||
qid: quizId,
|
||||
});
|
||||
useEffect(() => {
|
||||
console.log("reversed:", reversed);
|
||||
}, [reversed]);
|
||||
|
||||
if (!noUpdate) {
|
||||
updateAnswer(currentQuestion.id, value, 0);
|
||||
}
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
const sendAnswerToBackend = async (value: string, noUpdate = false) => {
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: value,
|
||||
qid: quizId,
|
||||
});
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
if (!noUpdate) {
|
||||
updateAnswer(currentQuestion.id, value, 0);
|
||||
}
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
const updateValueDebounced = useDebouncedCallback(async (value: string) => {
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
const updateValueDebounced = useDebouncedCallback(async (value: string) => {
|
||||
if (reversed) {
|
||||
const newValue =
|
||||
window.Number(value) < window.Number(min)
|
||||
? String(min)
|
||||
: window.Number(value) > window.Number(max)
|
||||
? String(max)
|
||||
: value;
|
||||
|
||||
setReversedInputValue(newValue);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
String(max + min - window.Number(newValue)),
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(String(window.Number(newValue)), true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newValue =
|
||||
window.Number(value) < window.Number(minRange)
|
||||
? minRange
|
||||
: window.Number(value) > window.Number(maxRange)
|
||||
? maxRange
|
||||
: value;
|
||||
|
||||
setInputValue(newValue);
|
||||
await sendAnswerToBackend(newValue);
|
||||
}, 1000);
|
||||
const updateMinRangeDebounced = useDebouncedCallback(
|
||||
async (value: string, crowded = false) => {
|
||||
if (reversed) {
|
||||
const newMinRange = crowded
|
||||
? window.Number(value.split("—")[1])
|
||||
: max + min - window.Number(value.split("—")[0]) < min
|
||||
? min
|
||||
: max + min - window.Number(value.split("—")[0]);
|
||||
|
||||
const newMinValue =
|
||||
window.Number(value.split("—")[0]) > max
|
||||
? String(max)
|
||||
: value.split("—")[0];
|
||||
|
||||
setReversedMinRange(
|
||||
crowded ? String(max + min - window.Number(newMinValue)) : newMinValue
|
||||
);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
`${newMinRange}—${value.split("—")[1]}`,
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(
|
||||
`${newMinValue}—${value.split("—")[1]}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newMinValue = crowded
|
||||
? maxRange
|
||||
: window.Number(value.split("—")[0]) < min
|
||||
? String(min)
|
||||
: value.split("—")[0];
|
||||
|
||||
setMinRange(newMinValue);
|
||||
await sendAnswerToBackend(`${newMinValue}—${value.split("—")[1]}`);
|
||||
},
|
||||
1000
|
||||
);
|
||||
const updateMaxRangeDebounced = useDebouncedCallback(
|
||||
async (value: string, crowded = false) => {
|
||||
if (reversed) {
|
||||
const newMaxRange = crowded
|
||||
? window.Number(value.split("—")[1])
|
||||
: max + min - window.Number(value.split("—")[1]) > max
|
||||
? max
|
||||
: max + min - window.Number(value.split("—")[1]);
|
||||
|
||||
const newMaxValue =
|
||||
window.Number(value.split("—")[1]) < min
|
||||
? String(min)
|
||||
: value.split("—")[1];
|
||||
|
||||
setReversedMaxRange(
|
||||
crowded ? String(max + min - window.Number(newMaxValue)) : newMaxValue
|
||||
);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
`${value.split("—")[0]}—${newMaxRange}`,
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(
|
||||
`${value.split("—")[0]}—${newMaxValue}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newMaxValue = crowded
|
||||
? minRange
|
||||
: window.Number(value.split("—")[1]) > max
|
||||
? String(max)
|
||||
: value.split("—")[1];
|
||||
|
||||
setMaxRange(newMaxValue);
|
||||
await sendAnswerToBackend(`${value.split("—")[0]}—${newMaxValue}`);
|
||||
},
|
||||
1000
|
||||
);
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
|
||||
const sliderValue =
|
||||
answer ||
|
||||
(reversed
|
||||
? max + min - currentQuestion.content.start + "—" + max
|
||||
: currentQuestion.content.start + "—" + max);
|
||||
|
||||
useEffect(() => {
|
||||
if (answer) {
|
||||
if (answer.includes("—")) {
|
||||
if (reversed) {
|
||||
const newValue =
|
||||
window.Number(value) < window.Number(min)
|
||||
? String(min)
|
||||
: window.Number(value) > window.Number(max)
|
||||
? String(max)
|
||||
: value;
|
||||
|
||||
setReversedInputValue(newValue);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
String(max + min - window.Number(newValue)),
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(String(window.Number(newValue)), true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newValue =
|
||||
window.Number(value) < window.Number(minRange)
|
||||
? minRange
|
||||
: window.Number(value) > window.Number(maxRange)
|
||||
? maxRange
|
||||
: value;
|
||||
|
||||
setInputValue(newValue);
|
||||
await sendAnswerToBackend(newValue);
|
||||
}, 1000);
|
||||
const updateMinRangeDebounced = useDebouncedCallback(
|
||||
async (value: string, crowded = false) => {
|
||||
if (reversed) {
|
||||
const newMinRange = crowded
|
||||
? window.Number(value.split("—")[1])
|
||||
: max + min - window.Number(value.split("—")[0]) < min
|
||||
? min
|
||||
: max + min - window.Number(value.split("—")[0]);
|
||||
|
||||
const newMinValue =
|
||||
window.Number(value.split("—")[0]) > max
|
||||
? String(max)
|
||||
: value.split("—")[0];
|
||||
|
||||
setReversedMinRange(
|
||||
crowded ? String(max + min - window.Number(newMinValue)) : newMinValue
|
||||
);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
`${newMinRange}—${value.split("—")[1]}`,
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(
|
||||
`${newMinValue}—${value.split("—")[1]}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newMinValue = crowded
|
||||
? maxRange
|
||||
: window.Number(value.split("—")[0]) < min
|
||||
? String(min)
|
||||
: value.split("—")[0];
|
||||
|
||||
setMinRange(newMinValue);
|
||||
await sendAnswerToBackend(`${newMinValue}—${value.split("—")[1]}`);
|
||||
},
|
||||
1000
|
||||
);
|
||||
const updateMaxRangeDebounced = useDebouncedCallback(
|
||||
async (value: string, crowded = false) => {
|
||||
if (reversed) {
|
||||
const newMaxRange = crowded
|
||||
? window.Number(value.split("—")[1])
|
||||
: max + min - window.Number(value.split("—")[1]) > max
|
||||
? max
|
||||
: max + min - window.Number(value.split("—")[1]);
|
||||
|
||||
const newMaxValue =
|
||||
window.Number(value.split("—")[1]) < min
|
||||
? String(min)
|
||||
: value.split("—")[1];
|
||||
|
||||
setReversedMaxRange(
|
||||
crowded ? String(max + min - window.Number(newMaxValue)) : newMaxValue
|
||||
);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
`${value.split("—")[0]}—${newMaxRange}`,
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(
|
||||
`${value.split("—")[0]}—${newMaxValue}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newMaxValue = crowded
|
||||
? minRange
|
||||
: window.Number(value.split("—")[1]) > max
|
||||
? String(max)
|
||||
: value.split("—")[1];
|
||||
|
||||
setMaxRange(newMaxValue);
|
||||
await sendAnswerToBackend(`${value.split("—")[0]}—${newMaxValue}`);
|
||||
},
|
||||
1000
|
||||
);
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
|
||||
const sliderValue =
|
||||
answer ||
|
||||
(reversed
|
||||
? max + min - currentQuestion.content.start + "—" + max
|
||||
: currentQuestion.content.start + "—" + max);
|
||||
|
||||
useEffect(() => {
|
||||
if (answer) {
|
||||
if (answer.includes("—")) {
|
||||
if (reversed) {
|
||||
setReversedMinRange(
|
||||
String(max + min - window.Number(answer.split("—")[0]))
|
||||
);
|
||||
setReversedMaxRange(
|
||||
String(max + min - window.Number(answer.split("—")[1]))
|
||||
);
|
||||
} else {
|
||||
setMinRange(answer.split("—")[0]);
|
||||
setMaxRange(answer.split("—")[1]);
|
||||
}
|
||||
} else {
|
||||
if (reversed) {
|
||||
setReversedInputValue(String(max + min - window.Number(answer)));
|
||||
} else {
|
||||
setInputValue(answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!answer) {
|
||||
setMinRange(String(currentQuestion.content.start));
|
||||
setMaxRange(String(max));
|
||||
|
||||
if (currentQuestion.content.chooseRange) {
|
||||
setReversedMinRange(String(currentQuestion.content.start));
|
||||
setReversedMaxRange(String(min));
|
||||
}
|
||||
|
||||
setReversedInputValue(String(currentQuestion.content.start));
|
||||
setInputValue(String(currentQuestion.content.start));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onSliderChange = (_: Event, value: number | number[]) => {
|
||||
const range = Array.isArray(value)
|
||||
? `${value[0]}—${value[1]}`
|
||||
: String(value);
|
||||
|
||||
updateAnswer(currentQuestion.id, range, 0);
|
||||
};
|
||||
|
||||
const onChangeCommitted = async (
|
||||
_: Event | SyntheticEvent<Element, Event>,
|
||||
value: number | number[]
|
||||
) => {
|
||||
if (currentQuestion.content.chooseRange && Array.isArray(value)) {
|
||||
if (reversed) {
|
||||
const newMinReversedValue = String(max + min - value[0]);
|
||||
const newMaxReversedValue = String(max + min - value[1]);
|
||||
|
||||
setMinRange(String(value[0]));
|
||||
setMaxRange(String(value[1]));
|
||||
setReversedMinRange(newMinReversedValue);
|
||||
setReversedMaxRange(newMaxReversedValue);
|
||||
await sendAnswerToBackend(
|
||||
`${newMinReversedValue}—${newMaxReversedValue}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setMinRange(String(value[0]));
|
||||
setMaxRange(String(value[1]));
|
||||
await sendAnswerToBackend(`${value[0]}—${value[1]}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
setReversedInputValue(String(max + min - window.Number(value)));
|
||||
setReversedMinRange(
|
||||
String(max + min - window.Number(answer.split("—")[0]))
|
||||
);
|
||||
setReversedMaxRange(
|
||||
String(max + min - window.Number(answer.split("—")[1]))
|
||||
);
|
||||
} else {
|
||||
setInputValue(String(value));
|
||||
setMinRange(answer.split("—")[0]);
|
||||
setMaxRange(answer.split("—")[1]);
|
||||
}
|
||||
|
||||
await sendAnswerToBackend(String(value));
|
||||
};
|
||||
|
||||
const changeValueLabelFormat = (value: number) => {
|
||||
if (!reversed) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const [minSliderBorder, maxSliderBorder] = sliderValue
|
||||
.split("—")
|
||||
.map(window.Number);
|
||||
|
||||
if (value === minSliderBorder) {
|
||||
return max + min - minSliderBorder;
|
||||
}
|
||||
|
||||
return max + min - maxSliderBorder;
|
||||
};
|
||||
|
||||
const onInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = target.value.replace(/\D/g, "");
|
||||
|
||||
} else {
|
||||
if (reversed) {
|
||||
setReversedInputValue(value);
|
||||
setReversedInputValue(String(max + min - window.Number(answer)));
|
||||
} else {
|
||||
setInputValue(value);
|
||||
setInputValue(answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateValueDebounced(value);
|
||||
};
|
||||
if (!answer) {
|
||||
setMinRange(String(currentQuestion.content.start));
|
||||
setMaxRange(String(max));
|
||||
|
||||
const onMinInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = target.value.replace(/\D/g, "");
|
||||
if (currentQuestion.content.chooseRange) {
|
||||
setReversedMinRange(String(currentQuestion.content.start));
|
||||
setReversedMaxRange(String(min));
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
setReversedMinRange(newValue);
|
||||
setReversedInputValue(String(currentQuestion.content.start));
|
||||
setInputValue(String(currentQuestion.content.start));
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (window.Number(newValue) <= window.Number(reversedMaxRange)) {
|
||||
const value = max + min - window.Number(reversedMaxRange);
|
||||
updateMinRangeDebounced(`${value}—${value}`, true);
|
||||
const onSliderChange = (_: Event, value: number | number[]) => {
|
||||
const range = Array.isArray(value)
|
||||
? `${value[0]}—${value[1]}`
|
||||
: String(value);
|
||||
|
||||
return;
|
||||
}
|
||||
updateAnswer(currentQuestion.id, range, 0);
|
||||
};
|
||||
|
||||
updateMinRangeDebounced(
|
||||
`${newValue}—${max + min - window.Number(reversedMaxRange)}`
|
||||
);
|
||||
const onChangeCommitted = async (
|
||||
_: Event | SyntheticEvent<Element, Event>,
|
||||
value: number | number[]
|
||||
) => {
|
||||
if (currentQuestion.content.chooseRange && Array.isArray(value)) {
|
||||
if (reversed) {
|
||||
const newMinReversedValue = String(max + min - value[0]);
|
||||
const newMaxReversedValue = String(max + min - value[1]);
|
||||
|
||||
return;
|
||||
}
|
||||
setMinRange(String(value[0]));
|
||||
setMaxRange(String(value[1]));
|
||||
setReversedMinRange(newMinReversedValue);
|
||||
setReversedMaxRange(newMaxReversedValue);
|
||||
await sendAnswerToBackend(
|
||||
`${newMinReversedValue}—${newMaxReversedValue}`,
|
||||
true
|
||||
);
|
||||
|
||||
setMinRange(newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.Number(newValue) >= window.Number(maxRange)) {
|
||||
updateMinRangeDebounced(`${maxRange}—${maxRange}`, true);
|
||||
setMinRange(String(value[0]));
|
||||
setMaxRange(String(value[1]));
|
||||
await sendAnswerToBackend(`${value[0]}—${value[1]}`);
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
updateMinRangeDebounced(`${newValue}—${maxRange}`);
|
||||
};
|
||||
if (reversed) {
|
||||
setReversedInputValue(String(max + min - window.Number(value)));
|
||||
} else {
|
||||
setInputValue(String(value));
|
||||
}
|
||||
|
||||
const onMaxInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = target.value.replace(/\D/g, "");
|
||||
await sendAnswerToBackend(String(value));
|
||||
};
|
||||
|
||||
if (reversed) {
|
||||
setReversedMaxRange(newValue);
|
||||
const changeValueLabelFormat = (value: number) => {
|
||||
if (!reversed) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (window.Number(newValue) >= window.Number(reversedMinRange)) {
|
||||
const value = max + min - window.Number(reversedMinRange);
|
||||
updateMaxRangeDebounced(`${value}—${value}`, true);
|
||||
const [minSliderBorder, maxSliderBorder] = sliderValue
|
||||
.split("—")
|
||||
.map(window.Number);
|
||||
|
||||
return;
|
||||
}
|
||||
if (value === minSliderBorder) {
|
||||
return max + min - minSliderBorder;
|
||||
}
|
||||
|
||||
updateMaxRangeDebounced(
|
||||
`${max + min - window.Number(reversedMinRange)}—${newValue}`
|
||||
);
|
||||
return max + min - maxSliderBorder;
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
const onInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = target.value.replace(/\D/g, "");
|
||||
|
||||
setMaxRange(newValue);
|
||||
if (reversed) {
|
||||
setReversedInputValue(value);
|
||||
} else {
|
||||
setInputValue(value);
|
||||
}
|
||||
|
||||
if (window.Number(newValue) <= window.Number(minRange)) {
|
||||
updateMaxRangeDebounced(`${minRange}—${minRange}`, true);
|
||||
updateValueDebounced(value);
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
const onMinInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = target.value.replace(/\D/g, "");
|
||||
|
||||
updateMaxRangeDebounced(`${minRange}—${newValue}`);
|
||||
};
|
||||
if (reversed) {
|
||||
setReversedMinRange(newValue);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h5" color={theme.palette.text.primary} sx={{ wordBreak: "break-word" }}>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
gap: "30px",
|
||||
paddingRight: isMobile ? "10px" : undefined,
|
||||
}}
|
||||
>
|
||||
<CustomSlider
|
||||
value={
|
||||
currentQuestion.content.chooseRange
|
||||
? sliderValue.split("—").length || 0 > 1
|
||||
? sliderValue.split("—").map((item) => window.Number(item))
|
||||
: [min, min + 1]
|
||||
: window.Number(sliderValue.split("—")[0])
|
||||
}
|
||||
min={min}
|
||||
max={max}
|
||||
step={currentQuestion.content.step || 1}
|
||||
onChange={onSliderChange}
|
||||
onChangeCommitted={onChangeCommitted}
|
||||
valueLabelFormat={changeValueLabelFormat}
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
"& .MuiSlider-valueLabel": {
|
||||
background: theme.palette.primary.main,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
if (window.Number(newValue) <= window.Number(reversedMaxRange)) {
|
||||
const value = max + min - window.Number(reversedMaxRange);
|
||||
updateMinRangeDebounced(`${value}—${value}`, true);
|
||||
|
||||
{!currentQuestion.content.chooseRange && (
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? reversedInputValue : inputValue}
|
||||
onChange={onInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
borderColor: theme.palette.text.primary,
|
||||
"& .MuiInputBase-input": {
|
||||
textAlign: "center",
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
return;
|
||||
}
|
||||
|
||||
{currentQuestion.content.chooseRange && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
alignItems: "center",
|
||||
"& .MuiFormControl-root": { width: "auto" },
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? String(reversedMinRange) : minRange}
|
||||
onChange={onMinInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
borderColor: theme.palette.text.primary,
|
||||
"& .MuiInputBase-input": {
|
||||
textAlign: "center",
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Typography color={theme.palette.text.primary}>до</Typography>
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? String(reversedMaxRange) : maxRange}
|
||||
onChange={onMaxInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
borderColor: theme.palette.text.primary,
|
||||
"& .MuiInputBase-input": {
|
||||
textAlign: "center",
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
updateMinRangeDebounced(
|
||||
`${newValue}—${max + min - window.Number(reversedMaxRange)}`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setMinRange(newValue);
|
||||
|
||||
if (window.Number(newValue) >= window.Number(maxRange)) {
|
||||
updateMinRangeDebounced(`${maxRange}—${maxRange}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMinRangeDebounced(`${newValue}—${maxRange}`);
|
||||
};
|
||||
|
||||
const onMaxInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = target.value.replace(/\D/g, "");
|
||||
|
||||
if (reversed) {
|
||||
setReversedMaxRange(newValue);
|
||||
|
||||
if (window.Number(newValue) >= window.Number(reversedMinRange)) {
|
||||
const value = max + min - window.Number(reversedMinRange);
|
||||
updateMaxRangeDebounced(`${value}—${value}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMaxRangeDebounced(
|
||||
`${max + min - window.Number(reversedMinRange)}—${newValue}`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setMaxRange(newValue);
|
||||
|
||||
if (window.Number(newValue) <= window.Number(minRange)) {
|
||||
updateMaxRangeDebounced(`${minRange}—${minRange}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMaxRangeDebounced(`${minRange}—${newValue}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
gap: "30px",
|
||||
paddingRight: isMobile ? "10px" : undefined,
|
||||
}}
|
||||
>
|
||||
<CustomSlider
|
||||
value={
|
||||
currentQuestion.content.chooseRange
|
||||
? sliderValue.split("—").length || 0 > 1
|
||||
? sliderValue.split("—").map((item) => window.Number(item))
|
||||
: [min, min + 1]
|
||||
: window.Number(sliderValue.split("—")[0])
|
||||
}
|
||||
min={min}
|
||||
max={max}
|
||||
step={currentQuestion.content.step || 1}
|
||||
onChange={onSliderChange}
|
||||
onChangeCommitted={onChangeCommitted}
|
||||
valueLabelFormat={changeValueLabelFormat}
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
"& .MuiSlider-valueLabel": {
|
||||
background: theme.palette.primary.main,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
{!currentQuestion.content.chooseRange && (
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? reversedInputValue : inputValue}
|
||||
onChange={onInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
borderColor: theme.palette.text.primary,
|
||||
"& .MuiOutlinedInput-root": { background: "transparent" },
|
||||
"& .MuiInputBase-input": { textAlign: "center", zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
backgroundColor: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentQuestion.content.chooseRange && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
alignItems: "center",
|
||||
"& .MuiFormControl-root": { width: "auto" },
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? String(reversedMinRange) : minRange}
|
||||
onChange={onMinInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
borderColor: theme.palette.text.primary,
|
||||
"& .MuiOutlinedInput-root": { background: "transparent" },
|
||||
"& .MuiInputBase-input": { textAlign: "center", zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
backgroundColor: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Typography color={theme.palette.text.primary}>до</Typography>
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? String(reversedMaxRange) : maxRange}
|
||||
onChange={onMaxInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
borderColor: theme.palette.text.primary,
|
||||
"& .MuiOutlinedInput-root": { background: "transparent" },
|
||||
"& .MuiInputBase-input": { textAlign: "center", zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
backgroundColor: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,10 @@
|
||||
import {Box, TextField as MuiTextField, TextFieldProps, Typography, useTheme} from "@mui/material";
|
||||
import {
|
||||
Box,
|
||||
TextField as MuiTextField,
|
||||
TextFieldProps,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
|
||||
@ -8,194 +14,259 @@ import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import {ChangeEvent, FC, useEffect, useState} from "react";
|
||||
import { ChangeEvent, FC, useEffect, useState } from "react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
import type { QuizQuestionText } from "../../../model/questionTypes/text";
|
||||
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>; // temporary fix ts(2590)
|
||||
|
||||
type TextProps = {
|
||||
currentQuestion: QuizQuestionText;
|
||||
stepNumber: number | null;
|
||||
currentQuestion: QuizQuestionText;
|
||||
stepNumber: number | null;
|
||||
};
|
||||
|
||||
const Orientation = [
|
||||
{horizontal: true},
|
||||
{horizontal: false},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: false},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: false},
|
||||
{horizontal: true},
|
||||
{horizontal: false},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: false},
|
||||
{horizontal: false},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
{horizontal: true},
|
||||
]
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
];
|
||||
|
||||
export const Text = ({ currentQuestion, stepNumber }: TextProps) => {
|
||||
const theme = useTheme();
|
||||
const { settings } = useQuizData();
|
||||
const spec = settings.cfg.spec
|
||||
const { quizId } = useQuizData();
|
||||
const { answers } = useQuizViewStore();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const theme = useTheme();
|
||||
const { settings } = useQuizData();
|
||||
const spec = settings.cfg.spec;
|
||||
const { quizId } = useQuizData();
|
||||
const { answers } = useQuizViewStore();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
|
||||
const inputHC = useDebouncedCallback(async (text) => {
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: text,
|
||||
qid: quizId,
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}, 400);
|
||||
useEffect(
|
||||
() => () => {
|
||||
inputHC.flush();
|
||||
},
|
||||
[inputHC]
|
||||
);
|
||||
switch (spec) {
|
||||
case true:
|
||||
return <TextSpecial currentQuestion={currentQuestion} answer={answer} inputHC={inputHC} stepNumber={stepNumber}/>;
|
||||
case undefined:
|
||||
return <TextNormal currentQuestion={currentQuestion} answer={answer} inputHC={inputHC} />;
|
||||
default:
|
||||
return <TextNormal currentQuestion={currentQuestion} answer={answer} inputHC={inputHC} />;
|
||||
const inputHC = useDebouncedCallback(async (text) => {
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: text,
|
||||
qid: quizId,
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}, 400);
|
||||
useEffect(
|
||||
() => () => {
|
||||
inputHC.flush();
|
||||
},
|
||||
[inputHC]
|
||||
);
|
||||
switch (spec) {
|
||||
case true:
|
||||
return (
|
||||
<TextSpecial
|
||||
currentQuestion={currentQuestion}
|
||||
answer={answer}
|
||||
inputHC={inputHC}
|
||||
stepNumber={stepNumber}
|
||||
/>
|
||||
);
|
||||
case undefined:
|
||||
return (
|
||||
<TextNormal
|
||||
currentQuestion={currentQuestion}
|
||||
answer={answer}
|
||||
inputHC={inputHC}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<TextNormal
|
||||
currentQuestion={currentQuestion}
|
||||
answer={answer}
|
||||
inputHC={inputHC}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface Props {
|
||||
currentQuestion: QuizQuestionText;
|
||||
answer: any,
|
||||
inputHC: (a: string) => void;
|
||||
stepNumber?: number | null;
|
||||
currentQuestion: QuizQuestionText;
|
||||
answer: any;
|
||||
inputHC: (a: string) => void;
|
||||
stepNumber?: number | null;
|
||||
}
|
||||
|
||||
const TextNormal = ({currentQuestion, answer, inputHC}: Props) => {
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const theme = useTheme();
|
||||
return(
|
||||
<Box>
|
||||
<Typography variant="h5" color={theme.palette.text.primary} sx={{ wordBreak: "break-word" }}>{currentQuestion.title}</Typography>
|
||||
const TextNormal = ({ currentQuestion, answer, inputHC }: Props) => {
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const theme = useTheme();
|
||||
const { settings } = useQuizData();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
placeholder={currentQuestion.content.placeholder}
|
||||
value={answer || ""}
|
||||
onChange={async ({ target }) => {
|
||||
updateAnswer(currentQuestion.id, target.value, 0);
|
||||
inputHC(target.value);
|
||||
}}
|
||||
sx={{
|
||||
"& .MuiOutlinedInput-root": {
|
||||
backgroundColor: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: "#FFFFFF",
|
||||
},
|
||||
"&:focus-visible": { borderColor: theme.palette.primary.main },
|
||||
}}
|
||||
/>
|
||||
{currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
alignItems: "center"
|
||||
}}
|
||||
sx={{
|
||||
maxWidth: "400px",
|
||||
width: "100%",
|
||||
height: "300px",
|
||||
margin: "15px",
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
placeholder={currentQuestion.content.placeholder}
|
||||
value={answer || ""}
|
||||
onChange={async ({ target }) => {
|
||||
updateAnswer(currentQuestion.id, target.value, 0);
|
||||
inputHC(target.value);
|
||||
}}
|
||||
sx={{
|
||||
"&:focus-visible": {
|
||||
borderColor: theme.palette.primary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{currentQuestion.content.back && currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ maxWidth: "400px", width: "100%", height: "300px", margin: "15px" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const TextSpecial = ({currentQuestion, answer, inputHC, stepNumber}: Props) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const isHorizontal = Orientation[Number(stepNumber) -1].horizontal
|
||||
return(
|
||||
<Box sx={{display: "flex", flexDirection: isMobile? "column" : undefined, alignItems: isMobile ? "center" : undefined,}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "20px"
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" color={theme.palette.text.primary} sx={{ wordBreak: "break-word" }}>{currentQuestion.title}</Typography>
|
||||
{isHorizontal && currentQuestion.content.back && currentQuestion.content.back !== " " && (
|
||||
<Box sx={{margin: "30px", width: "50vw", maxHeight: "550px" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{
|
||||
(<TextField
|
||||
autoFocus={true}
|
||||
multiline
|
||||
maxRows={4}
|
||||
placeholder={currentQuestion.content.placeholder}
|
||||
value={answer || ""}
|
||||
onChange={async ({ target }:ChangeEvent<HTMLInputElement>) => {
|
||||
updateAnswer(currentQuestion.id, target.value, 0);
|
||||
inputHC(target.value);
|
||||
}
|
||||
}
|
||||
inputProps={{maxLength:400}}
|
||||
sx={{
|
||||
width: "100%",
|
||||
"&:focus-visible": {
|
||||
borderColor: theme.palette.primary.main
|
||||
}
|
||||
}}
|
||||
/>)
|
||||
}
|
||||
|
||||
const TextSpecial = ({
|
||||
currentQuestion,
|
||||
answer,
|
||||
inputHC,
|
||||
stepNumber,
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const isHorizontal = Orientation[Number(stepNumber) - 1].horizontal;
|
||||
const { settings } = useQuizData();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: isMobile ? "column" : undefined,
|
||||
alignItems: isMobile ? "center" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
{isHorizontal &&
|
||||
currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ margin: "30px", width: "50vw", maxHeight: "550px" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
{!isHorizontal && currentQuestion.content.back && currentQuestion.content.back !== " " && (
|
||||
<Box sx={{margin: "15px", width: "40vw" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
)}
|
||||
{
|
||||
<TextField
|
||||
autoFocus={true}
|
||||
multiline
|
||||
maxRows={4}
|
||||
placeholder={currentQuestion.content.placeholder}
|
||||
value={answer || ""}
|
||||
onChange={async ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
updateAnswer(currentQuestion.id, target.value, 0);
|
||||
inputHC(target.value);
|
||||
}}
|
||||
inputProps={{ maxLength: 400 }}
|
||||
sx={{
|
||||
width: "100%",
|
||||
"& .MuiOutlinedInput-root": {
|
||||
backgroundColor: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: "#FFFFFF",
|
||||
},
|
||||
"&:focus-visible": {
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</Box>
|
||||
{!isHorizontal &&
|
||||
currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ margin: "15px", width: "40vw" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -1,22 +1,22 @@
|
||||
import {
|
||||
Box,
|
||||
Checkbox,
|
||||
FormControlLabel,
|
||||
FormGroup,
|
||||
TextField as MuiTextField,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
TextFieldProps,
|
||||
Typography,
|
||||
useTheme
|
||||
Box,
|
||||
Checkbox,
|
||||
FormControlLabel,
|
||||
FormGroup,
|
||||
TextField as MuiTextField,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
TextFieldProps,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
deleteAnswer,
|
||||
updateAnswer,
|
||||
updateOwnVariant,
|
||||
useQuizViewStore
|
||||
deleteAnswer,
|
||||
updateAnswer,
|
||||
updateOwnVariant,
|
||||
useQuizViewStore,
|
||||
} from "@stores/quizView";
|
||||
|
||||
import { CheckboxIcon } from "@icons/Checkbox";
|
||||
@ -36,245 +36,261 @@ import moment from "moment";
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
|
||||
|
||||
type VariantProps = {
|
||||
currentQuestion: QuizQuestionVariant;
|
||||
currentQuestion: QuizQuestionVariant;
|
||||
};
|
||||
|
||||
export const Variant = ({ currentQuestion }: VariantProps) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const { answers, ownVariants } = useQuizViewStore();
|
||||
const { answer } =
|
||||
answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
) ?? {};
|
||||
const ownVariant = ownVariants.find(
|
||||
(variant) => variant.id === currentQuestion.id
|
||||
);
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const { answers, ownVariants } = useQuizViewStore();
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const ownVariant = ownVariants.find(
|
||||
(variant) => variant.id === currentQuestion.id
|
||||
);
|
||||
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
|
||||
const Group = currentQuestion.content.multi ? FormGroup : RadioGroup;
|
||||
const Group = currentQuestion.content.multi ? FormGroup : RadioGroup;
|
||||
|
||||
useEffect(() => {
|
||||
if (!ownVariant) {
|
||||
updateOwnVariant(currentQuestion.id, "");
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (!ownVariant) {
|
||||
updateOwnVariant(currentQuestion.id, "");
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (moment.isMoment(answer)) throw new Error("Answer is Moment in Variant question");
|
||||
if (moment.isMoment(answer))
|
||||
throw new Error("Answer is Moment in Variant question");
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h5" color={theme.palette.text.primary} sx={{ wordBreak: "break-word" }}>{currentQuestion.title}</Typography>
|
||||
<Box sx={{
|
||||
display: "flex", gap: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined, alignItems: isMobile ? "center" : undefined
|
||||
}}>
|
||||
<Group
|
||||
name={currentQuestion.id.toString()}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
flexBasis: "100%",
|
||||
marginTop: "20px",
|
||||
width: isMobile ? "100%" : undefined
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<VariantItem
|
||||
key={variant.id}
|
||||
currentQuestion={currentQuestion}
|
||||
variant={variant}
|
||||
answer={answer}
|
||||
index={index}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
/>
|
||||
))}
|
||||
{currentQuestion.content.own && ownVariant && (
|
||||
<VariantItem
|
||||
own
|
||||
currentQuestion={currentQuestion}
|
||||
variant={ownVariant.variant}
|
||||
answer={answer}
|
||||
index={currentQuestion.content.variants.length + 2}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Group>
|
||||
{currentQuestion.content.back && currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ maxWidth: "400px", width: "100%", height: "300px" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
alignItems: isMobile ? "center" : undefined,
|
||||
}}
|
||||
>
|
||||
<Group
|
||||
name={currentQuestion.id.toString()}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
flexBasis: "100%",
|
||||
marginTop: "20px",
|
||||
width: isMobile ? "100%" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<VariantItem
|
||||
key={variant.id}
|
||||
currentQuestion={currentQuestion}
|
||||
variant={variant}
|
||||
answer={answer}
|
||||
index={index}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
/>
|
||||
))}
|
||||
{currentQuestion.content.own && ownVariant && (
|
||||
<VariantItem
|
||||
own
|
||||
currentQuestion={currentQuestion}
|
||||
variant={ownVariant.variant}
|
||||
answer={answer}
|
||||
index={currentQuestion.content.variants.length + 2}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Group>
|
||||
{currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ maxWidth: "400px", width: "100%", height: "300px" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const VariantItem = ({
|
||||
currentQuestion,
|
||||
variant,
|
||||
answer,
|
||||
index,
|
||||
own = false,
|
||||
isSending,
|
||||
setIsSending,
|
||||
currentQuestion,
|
||||
variant,
|
||||
answer,
|
||||
index,
|
||||
own = false,
|
||||
isSending,
|
||||
setIsSending,
|
||||
}: {
|
||||
currentQuestion: QuizQuestionVariant;
|
||||
variant: QuestionVariant;
|
||||
answer: string | string[] | undefined;
|
||||
index: number;
|
||||
own?: boolean;
|
||||
isSending: boolean;
|
||||
setIsSending: (a: boolean) => void;
|
||||
currentQuestion: QuizQuestionVariant;
|
||||
variant: QuestionVariant;
|
||||
answer: string | string[] | undefined;
|
||||
index: number;
|
||||
own?: boolean;
|
||||
isSending: boolean;
|
||||
setIsSending: (a: boolean) => void;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const { settings, quizId } = useQuizData();
|
||||
const theme = useTheme();
|
||||
const { settings, quizId } = useQuizData();
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
disabled={isSending}
|
||||
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",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
lineHeight: "normal",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "4px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "#b8babf",
|
||||
}
|
||||
}
|
||||
}}
|
||||
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 />} />
|
||||
return (
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
disabled={isSending}
|
||||
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: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: 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",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
lineHeight: "normal",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "4px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "#b8babf",
|
||||
},
|
||||
},
|
||||
}}
|
||||
value={index}
|
||||
labelPlacement="start"
|
||||
control={
|
||||
currentQuestion.content.multi ? (
|
||||
<Checkbox
|
||||
checked={!!answer?.includes(variant.id)}
|
||||
checkedIcon={
|
||||
<CheckboxIcon checked color={theme.palette.primary.main} />
|
||||
}
|
||||
label={own ? <TextField label="Другое..." /> : variant.answer}
|
||||
onClick={async (event) => {
|
||||
event.preventDefault();
|
||||
if (isSending) return;
|
||||
icon={<CheckboxIcon />}
|
||||
/>
|
||||
) : (
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck color={theme.palette.primary.main} />}
|
||||
icon={<RadioIcon />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
label={own ? <TextField label="Другое..." /> : variant.answer}
|
||||
onClick={async (event) => {
|
||||
event.preventDefault();
|
||||
if (isSending) return;
|
||||
|
||||
setIsSending(true);
|
||||
const variantId = currentQuestion.content.variants[index].id;
|
||||
console.log(answer);
|
||||
setIsSending(true);
|
||||
const variantId = currentQuestion.content.variants[index].id;
|
||||
console.log(answer);
|
||||
|
||||
if (currentQuestion.content.multi) {
|
||||
const currentAnswer = typeof answer !== "string" ? answer || [] : [];
|
||||
if (currentQuestion.content.multi) {
|
||||
const currentAnswer = typeof answer !== "string" ? answer || [] : [];
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: currentAnswer.includes(variantId)
|
||||
? currentAnswer?.filter((item) => item !== variantId)
|
||||
: [...currentAnswer, variantId],
|
||||
qid: quizId,
|
||||
});
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: currentAnswer.includes(variantId)
|
||||
? currentAnswer?.filter((item) => item !== variantId)
|
||||
: [...currentAnswer, variantId],
|
||||
qid: quizId,
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentAnswer.includes(variantId)
|
||||
? currentAnswer?.filter((item) => item !== variantId)
|
||||
: [...currentAnswer, variantId],
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentAnswer.includes(variantId)
|
||||
? currentAnswer?.filter((item) => item !== variantId)
|
||||
: [...currentAnswer, variantId],
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
return;
|
||||
}
|
||||
setIsSending(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: currentQuestion.content.variants[index].answer,
|
||||
qid: quizId,
|
||||
});
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: currentQuestion.content.variants[index].answer,
|
||||
qid: quizId,
|
||||
});
|
||||
|
||||
updateAnswer(currentQuestion.id, variantId,
|
||||
answer === variantId ? 0
|
||||
:
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
variantId,
|
||||
answer === variantId
|
||||
? 0
|
||||
: currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
if (answer === variantId) {
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
deleteAnswer(currentQuestion.id);
|
||||
}
|
||||
|
||||
if (answer === variantId) {
|
||||
try {
|
||||
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
deleteAnswer(currentQuestion.id);
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
setIsSending(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,10 +1,10 @@
|
||||
import {
|
||||
Box,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Typography,
|
||||
useTheme
|
||||
Box,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { deleteAnswer, updateAnswer, useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
@ -21,173 +21,192 @@ import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg";
|
||||
import { useState } from "react";
|
||||
|
||||
type VarimgProps = {
|
||||
currentQuestion: QuizQuestionVarImg;
|
||||
currentQuestion: QuizQuestionVarImg;
|
||||
};
|
||||
|
||||
export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
||||
const { settings, quizId } = useQuizData();
|
||||
const { answers } = useQuizViewStore();
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const { settings, quizId } = useQuizData();
|
||||
const { answers } = useQuizViewStore();
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
|
||||
const { answer } =
|
||||
answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
) ?? {};
|
||||
const variant = currentQuestion.content.variants.find(
|
||||
({ id }) => answer === id
|
||||
);
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const variant = currentQuestion.content.variants.find(
|
||||
({ id }) => answer === id
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h5" color={theme.palette.text.primary} sx={{ wordBreak: "break-word" }}>{currentQuestion.title}</Typography>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
marginTop: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
gap: isMobile ? "30px" : undefined,
|
||||
alignItems: isMobile ? "center" : undefined
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
marginTop: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
gap: isMobile ? "30px" : undefined,
|
||||
alignItems: isMobile ? "center" : undefined,
|
||||
}}
|
||||
>
|
||||
<RadioGroup
|
||||
name={currentQuestion.id}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
flexBasis: "100%",
|
||||
width: isMobile ? "100%" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
gap: isMobile ? "20px" : undefined,
|
||||
}}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
disabled={isSending}
|
||||
sx={{
|
||||
marginBottom: "15px",
|
||||
borderRadius: "5px",
|
||||
padding: "15px",
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
border: `1px solid`,
|
||||
borderColor:
|
||||
answer === variant.id
|
||||
? theme.palette.primary.main
|
||||
: "#9A9AAF",
|
||||
display: "flex",
|
||||
margin: isMobile ? 0 : undefined,
|
||||
"& .MuiFormControlLabel-label": {
|
||||
wordBreak: "break-word",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
lineHeight: "normal",
|
||||
paddingLeft: "45px",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "4px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "#b8babf",
|
||||
},
|
||||
},
|
||||
}}
|
||||
value={index}
|
||||
onClick={async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
}}>
|
||||
<RadioGroup
|
||||
name={currentQuestion.id}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
flexBasis: "100%",
|
||||
width: isMobile ? "100%" : undefined
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%", gap: isMobile ? "20px" : undefined }}>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
disabled={isSending}
|
||||
sx={{
|
||||
marginBottom: "15px",
|
||||
borderRadius: "5px",
|
||||
padding: "15px",
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight ? "white" : theme.palette.background.default,
|
||||
border: `1px solid`,
|
||||
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||
display: "flex",
|
||||
margin: isMobile ? 0 : undefined,
|
||||
"& .MuiFormControlLabel-label": {
|
||||
wordBreak: "break-word",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
lineHeight: "normal",
|
||||
paddingLeft: "45px",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "4px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "#b8babf",
|
||||
}
|
||||
},
|
||||
}}
|
||||
value={index}
|
||||
onClick={async (event) => {
|
||||
event.preventDefault();
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: `${currentQuestion.content.variants[index].answer} <img style="width:100%; max-width:250px; max-height:250px" src="${currentQuestion.content.variants[index].extendedText}"/>`,
|
||||
qid: quizId,
|
||||
});
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentQuestion.content.variants[index].id,
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: `${currentQuestion.content.variants[index].answer} <img style="width:100%; max-width:250px; max-height:250px" src="${currentQuestion.content.variants[index].extendedText}"/>`,
|
||||
qid: quizId,
|
||||
});
|
||||
if (answer === currentQuestion.content.variants[index].id) {
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
deleteAnswer(currentQuestion.id);
|
||||
}
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentQuestion.content.variants[index].id,
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
|
||||
if (answer === currentQuestion.content.variants[index].id) {
|
||||
try {
|
||||
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
deleteAnswer(currentQuestion.id);
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}}
|
||||
control={
|
||||
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main} />} icon={<RadioIcon />} />
|
||||
}
|
||||
label={variant.answer}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</RadioGroup>
|
||||
{/* {(variant?.extendedText || currentQuestion.content.back) && ( */}
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: "450px",
|
||||
width: "100%",
|
||||
height: "450px",
|
||||
border: "1px solid #9A9AAF",
|
||||
borderRadius: "12px",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "#9A9AAF12",
|
||||
color: "#9A9AAF",
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
{answer ? (
|
||||
variant?.extendedText ? (
|
||||
<img
|
||||
src={variant?.extendedText}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
<BlankImage />
|
||||
)
|
||||
) : currentQuestion.content.back !== " "
|
||||
&& currentQuestion.content.back !== null
|
||||
&& currentQuestion.content.back.length > 0
|
||||
? (
|
||||
<img
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
) : (currentQuestion.content.replText !== " " && currentQuestion.content.replText.length > 0) ? currentQuestion.content.replText : variant?.extendedText || isMobile ? (
|
||||
"Выберите вариант ответа ниже"
|
||||
) : (
|
||||
"Выберите вариант ответа слева"
|
||||
)}
|
||||
|
||||
</Box>
|
||||
{/* )} */}
|
||||
</Box>
|
||||
setIsSending(false);
|
||||
}}
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={
|
||||
<RadioCheck color={theme.palette.primary.main} />
|
||||
}
|
||||
icon={<RadioIcon />}
|
||||
/>
|
||||
}
|
||||
label={variant.answer}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</RadioGroup>
|
||||
{/* {(variant?.extendedText || currentQuestion.content.back) && ( */}
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: "450px",
|
||||
width: "100%",
|
||||
height: "450px",
|
||||
border: "1px solid #9A9AAF",
|
||||
borderRadius: "12px",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "#9A9AAF12",
|
||||
color: "#9A9AAF",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{answer ? (
|
||||
variant?.extendedText ? (
|
||||
<img
|
||||
src={variant?.extendedText}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
<BlankImage />
|
||||
)
|
||||
) : currentQuestion.content.back !== " " &&
|
||||
currentQuestion.content.back !== null &&
|
||||
currentQuestion.content.back.length > 0 ? (
|
||||
<img
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
) : currentQuestion.content.replText !== " " &&
|
||||
currentQuestion.content.replText.length > 0 ? (
|
||||
currentQuestion.content.replText
|
||||
) : variant?.extendedText || isMobile ? (
|
||||
"Выберите вариант ответа ниже"
|
||||
) : (
|
||||
"Выберите вариант ответа слева"
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
{/* )} */}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -121,8 +121,7 @@ export const Select = ({
|
||||
"& .MuiTypography-root": {
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
}}
|
||||
IconComponent={(props) => <ArrowDown {...props} />}
|
||||
@ -139,7 +138,7 @@ export const Select = ({
|
||||
borderRadius: "5px",
|
||||
color: colorPlaceholder,
|
||||
whiteSpace: "normal",
|
||||
wordBreak: "break-word"
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
|
@ -21,7 +21,17 @@ export type QuizTheme =
|
||||
| "GoldDarkTheme"
|
||||
| "PurpleTheme"
|
||||
| "BlueTheme"
|
||||
| "BlueDarkTheme";
|
||||
| "BlueDarkTheme"
|
||||
| "Design1"
|
||||
| "Design2"
|
||||
| "Design3"
|
||||
| "Design4"
|
||||
| "Design5"
|
||||
| "Design6"
|
||||
| "Design7"
|
||||
| "Design8"
|
||||
| "Design9"
|
||||
| "Design10";
|
||||
|
||||
export type FCField = {
|
||||
text: string;
|
||||
@ -47,18 +57,6 @@ export type QuizSettings = {
|
||||
recentlyCompleted: boolean;
|
||||
};
|
||||
|
||||
export type Design =
|
||||
| ""
|
||||
| "design1"
|
||||
| "design2"
|
||||
| "design3"
|
||||
| "design4"
|
||||
| "design5"
|
||||
| "design6"
|
||||
| "design7"
|
||||
| "design8"
|
||||
| "design9"
|
||||
| "design10";
|
||||
|
||||
export interface QuizConfig {
|
||||
spec: undefined | true;
|
||||
@ -69,7 +67,7 @@ export interface QuizConfig {
|
||||
results: QuizResultsType;
|
||||
haveRoot: string | null;
|
||||
theme: QuizTheme;
|
||||
design: Design;
|
||||
design: boolean;
|
||||
resultInfo: {
|
||||
when: "email" | "";
|
||||
share: boolean;
|
||||
|
37
lib/ui_kit/Stepper.tsx
Normal file
37
lib/ui_kit/Stepper.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { useTheme } from "@mui/material";
|
||||
import MobileStepper from "@mui/material/MobileStepper";
|
||||
|
||||
interface Props {
|
||||
activeStep: number;
|
||||
steps: number;
|
||||
}
|
||||
|
||||
export default function ProgressMobileStepper({ activeStep, steps }: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<MobileStepper
|
||||
variant="progress"
|
||||
steps={steps + 1}
|
||||
position="static"
|
||||
activeStep={activeStep}
|
||||
sx={{
|
||||
width: "100%",
|
||||
padding: "10px 0 0",
|
||||
background: "transparent",
|
||||
"& .MuiLinearProgress-root": {
|
||||
height: "4px",
|
||||
background: theme.palette.primary.light,
|
||||
width: "100%",
|
||||
opacity: 0.5,
|
||||
},
|
||||
"& .MuiLinearProgress-bar": {
|
||||
background: theme.palette.primary.main,
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
nextButton={<></>}
|
||||
backButton={<></>}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,229 +1,431 @@
|
||||
import { QuizTheme } from "@model/settingsData";
|
||||
import { Theme, createTheme } from "@mui/material";
|
||||
import { createTheme } from "@mui/material";
|
||||
import themePublic from "./genericPublication";
|
||||
|
||||
import type { Theme } from "@mui/material";
|
||||
import type { QuizTheme } from "@model/settingsData";
|
||||
|
||||
const StandardTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#7E2AEA",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734"
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#7E2AEA",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#7E2AEA",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#7E2AEA",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#FFFFFF",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#FFFFFF",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const StandardDarkTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#7E2AEA",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734"
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#7E2AEA",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#7E2AEA",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#7E2AEA",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const PinkTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#D34085",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734"
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#D34085",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#D34085",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#D34085",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#FFF9FC",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#FFF9FC",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const PinkDarkTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#D34085",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734"
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#D34085",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#D34085",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#D34085",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const BlackWhiteTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#4E4D51",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734"
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#FFF9FC",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#4E4D51",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#FFF9FC",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#FFFFFF",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#FFFFFF",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const OliveTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#758E4F",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734"
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#758E4F",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#758E4F",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#758E4F",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#F9FBF1",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#F9FBF1",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const PurpleTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#7E2AEA",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734"
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#7E2AEA",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#7E2AEA",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#7E2AEA",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#FBF8FF",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#FBF8FF",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const YellowTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#F2B133",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734"
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#F2B133",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#F2B133",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#F2B133",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#FFFCF6",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#FFFCF6",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const GoldDarkTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#E6AA37",
|
||||
},
|
||||
secondary: {
|
||||
main: "#FFFCF6",
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#F2B133",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#E6AA37",
|
||||
},
|
||||
secondary: {
|
||||
main: "#FFFCF6",
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#F2B133",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const BlueTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#4964ED",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734"
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#4964ED",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#4964ED",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#4964ED",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#F5F7FF",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#F5F7FF",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const BlueDarkTheme = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#07A0C3",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734"
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#07A0C3",
|
||||
},
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#07A0C3",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#07A0C3",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
}
|
||||
})
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const quizThemes: Record<QuizTheme, { theme: Theme; isLight: boolean; }> = {
|
||||
const Design1 = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#F2B133",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#F2B133",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#FFFCF6",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Design2 = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#758E4F",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#758E4F",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#F9FBF1",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Design3 = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#07A0C3",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#07A0C3",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Design4 = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#F2B133",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#F2B133",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#FFFCF6",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Design5 = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#07A0C3",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#07A0C3",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Design6 = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#D34085",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#D34085",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Design8 = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#D34085",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#D34085",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Design7 = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#F2B133",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#F2B133",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#FFFCF6",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Design9 = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#758E4F",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#333647",
|
||||
secondary: "#758E4F",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#F9FBF1",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Design10 = createTheme({
|
||||
...themePublic,
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#07A0C3",
|
||||
},
|
||||
secondary: {
|
||||
main: "#252734",
|
||||
},
|
||||
text: {
|
||||
primary: "#FFFFFF",
|
||||
secondary: "#07A0C3",
|
||||
},
|
||||
|
||||
background: {
|
||||
default: "#333647",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const quizThemes: Record<QuizTheme, { theme: Theme; isLight: boolean }> =
|
||||
{
|
||||
StandardTheme: { theme: StandardTheme, isLight: true },
|
||||
StandardDarkTheme: { theme: StandardDarkTheme, isLight: false },
|
||||
PinkTheme: { theme: PinkTheme, isLight: true },
|
||||
@ -235,4 +437,14 @@ export const quizThemes: Record<QuizTheme, { theme: Theme; isLight: boolean; }>
|
||||
PurpleTheme: { theme: PurpleTheme, isLight: true },
|
||||
BlueTheme: { theme: BlueTheme, isLight: true },
|
||||
BlueDarkTheme: { theme: BlueDarkTheme, isLight: false },
|
||||
};
|
||||
Design1: { theme: Design1, isLight: false },
|
||||
Design2: { theme: Design2, isLight: false },
|
||||
Design3: { theme: Design3, isLight: true },
|
||||
Design4: { theme: Design4, isLight: false },
|
||||
Design5: { theme: Design5, isLight: false },
|
||||
Design6: { theme: Design6, isLight: false },
|
||||
Design7: { theme: Design7, isLight: false },
|
||||
Design8: { theme: Design8, isLight: false },
|
||||
Design9: { theme: Design9, isLight: false },
|
||||
Design10: { theme: Design10, isLight: false },
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user