241 lines
10 KiB
TypeScript
241 lines
10 KiB
TypeScript
import { Box, Button, LinearProgress, Paper, Typography, FormControl, Select as MuiSelect, MenuItem, useTheme } from "@mui/material";
|
||
import { useQuestionsStore } from "@root/questions/store";
|
||
import {
|
||
decrementCurrentQuestionIndex,
|
||
incrementCurrentQuestionIndex,
|
||
useQuizPreviewStore,
|
||
setCurrentQuestionIndex
|
||
} from "@root/quizPreview";
|
||
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "model/questionTypes/shared";
|
||
import { useEffect } from "react";
|
||
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
||
import Date from "./QuizPreviewQuestionTypes/Date";
|
||
import Emoji from "./QuizPreviewQuestionTypes/Emoji";
|
||
import File from "./QuizPreviewQuestionTypes/File";
|
||
import Images from "./QuizPreviewQuestionTypes/Images";
|
||
import Number from "./QuizPreviewQuestionTypes/Number";
|
||
import Page from "./QuizPreviewQuestionTypes/Page";
|
||
import Rating from "./QuizPreviewQuestionTypes/Rating";
|
||
import Select from "./QuizPreviewQuestionTypes/Select";
|
||
import Text from "./QuizPreviewQuestionTypes/Text";
|
||
import Variant from "./QuizPreviewQuestionTypes/Variant";
|
||
import Varimg from "./QuizPreviewQuestionTypes/Varimg";
|
||
import { notReachable } from "../../utils/notReachable";
|
||
import ArrowDownIcon from "@icons/ArrowDownIcon";
|
||
|
||
export default function QuizPreviewLayout() {
|
||
const theme = useTheme();
|
||
const questions = useQuestionsStore(state => state.questions);
|
||
const currentQuizStep = useQuizPreviewStore(
|
||
(state) => state.currentQuestionIndex
|
||
);
|
||
|
||
const nonDeletedQuizQuestions = questions.filter(
|
||
(question) => !question.deleted
|
||
);
|
||
const maxCurrentQuizStep =
|
||
nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0;
|
||
const currentProgress = Math.floor(
|
||
(currentQuizStep / maxCurrentQuizStep) * 100
|
||
);
|
||
|
||
const currentQuestion = nonDeletedQuizQuestions[currentQuizStep];
|
||
|
||
useEffect(
|
||
function resetCurrentQuizStep() {
|
||
if (currentQuizStep > maxCurrentQuizStep) {
|
||
decrementCurrentQuestionIndex();
|
||
}
|
||
},
|
||
[currentQuizStep, maxCurrentQuizStep]
|
||
);
|
||
|
||
return (
|
||
<Paper
|
||
className="quiz-preview-draghandle"
|
||
data-cy="quiz-preview-layout"
|
||
sx={{
|
||
height: "100%",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
flexGrow: 1,
|
||
borderRadius: "12px",
|
||
pointerEvents: "auto",
|
||
}}
|
||
>
|
||
<Box
|
||
sx={{
|
||
p: "16px",
|
||
whiteSpace: "break-spaces",
|
||
overflowY: "auto",
|
||
flexGrow: 1,
|
||
"&::-webkit-scrollbar": { width: 0, display: "none" },
|
||
msOverflowStyle: "none",
|
||
scrollbarWidth: "none",
|
||
}}
|
||
>
|
||
<QuestionPreviewComponent question={currentQuestion} />
|
||
</Box>
|
||
<Box
|
||
sx={{
|
||
mt: "auto",
|
||
p: "16px",
|
||
borderTop: "1px solid #E3E3E3",
|
||
}}
|
||
>
|
||
<Box sx={{ marginBottom: "10px" }}>
|
||
<FormControl
|
||
fullWidth
|
||
size="small"
|
||
sx={{ width: "100%", minWidth: "200px", height: "48px" }}
|
||
>
|
||
<MuiSelect
|
||
id="category-select"
|
||
variant="outlined"
|
||
value={currentQuizStep}
|
||
placeholder="Заголовок вопроса"
|
||
onChange={({ target }) =>
|
||
setCurrentQuestionIndex(window.Number(target.value))
|
||
}
|
||
sx={{
|
||
height: "48px",
|
||
borderRadius: "8px",
|
||
"& .MuiOutlinedInput-notchedOutline": {
|
||
border: `1px solid ${theme.palette.brightPurple.main} !important`,
|
||
},
|
||
}}
|
||
MenuProps={{
|
||
PaperProps: {
|
||
sx: {
|
||
mt: "8px",
|
||
p: "4px",
|
||
borderRadius: "8px",
|
||
border: "1px solid #EEE4FC",
|
||
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
||
},
|
||
},
|
||
MenuListProps: {
|
||
sx: {
|
||
py: 0,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: "8px",
|
||
"& .Mui-selected": {
|
||
backgroundColor: theme.palette.background.default,
|
||
color: theme.palette.brightPurple.main,
|
||
},
|
||
},
|
||
},
|
||
}}
|
||
inputProps={{
|
||
sx: {
|
||
color: theme.palette.brightPurple.main,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
px: "9px",
|
||
gap: "20px",
|
||
},
|
||
}}
|
||
IconComponent={(props) => <ArrowDownIcon {...props} />}
|
||
>
|
||
{Object.values(questions).map(
|
||
({ id, title }, index) => (
|
||
<MenuItem
|
||
key={id}
|
||
value={index}
|
||
sx={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: "20px",
|
||
p: "4px",
|
||
borderRadius: "5px",
|
||
color: theme.palette.grey2.main,
|
||
}}
|
||
>
|
||
{`${index + 1}. ${title}`}
|
||
</MenuItem>
|
||
)
|
||
)}
|
||
</MuiSelect>
|
||
</FormControl>
|
||
</Box>
|
||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||
<Box
|
||
sx={{
|
||
flexGrow: 1,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 1,
|
||
}}
|
||
>
|
||
<Typography>
|
||
{nonDeletedQuizQuestions.length > 0
|
||
? `Вопрос ${currentQuizStep + 1} из ${nonDeletedQuizQuestions.length
|
||
}`
|
||
: "Нет вопросов"}
|
||
</Typography>
|
||
{nonDeletedQuizQuestions.length > 0 && (
|
||
<LinearProgress
|
||
variant="determinate"
|
||
value={currentProgress}
|
||
sx={{
|
||
"&.MuiLinearProgress-colorPrimary": {
|
||
backgroundColor: "fadePurple.main",
|
||
},
|
||
"& .MuiLinearProgress-barColorPrimary": {
|
||
backgroundColor: "brightPurple.main",
|
||
},
|
||
}}
|
||
/>
|
||
)}
|
||
</Box>
|
||
<Box
|
||
sx={{
|
||
ml: 2,
|
||
display: "flex",
|
||
gap: 1,
|
||
}}
|
||
>
|
||
<Button
|
||
variant="outlined"
|
||
onClick={decrementCurrentQuestionIndex}
|
||
disabled={currentQuizStep === 0}
|
||
sx={{ px: 1, minWidth: 0 }}
|
||
>
|
||
<ArrowLeft />
|
||
</Button>
|
||
<Button
|
||
variant="contained"
|
||
onClick={() => incrementCurrentQuestionIndex(maxCurrentQuizStep)}
|
||
disabled={currentQuizStep >= maxCurrentQuizStep}
|
||
>
|
||
Далее
|
||
</Button>
|
||
</Box>
|
||
</Box>
|
||
</Box>
|
||
</Paper>
|
||
);
|
||
}
|
||
|
||
function QuestionPreviewComponent({ question }: {
|
||
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
||
}) {
|
||
if (question.type === null) return null;
|
||
|
||
switch (question.type) {
|
||
case "variant": return <Variant question={question} />;
|
||
case "images": return <Images question={question} />;
|
||
case "varimg": return <Varimg question={question} />;
|
||
case "emoji": return <Emoji question={question} />;
|
||
case "text": return <Text question={question} />;
|
||
case "select": return <Select question={question} />;
|
||
case "date": return <Date question={question} />;
|
||
case "number": return <Number question={question} />;
|
||
case "file": return <File question={question} />;
|
||
case "page": return <Page question={question} />;
|
||
case "rating": return <Rating question={question} />;
|
||
default: notReachable(question);
|
||
}
|
||
}
|