feat: new store for view

This commit is contained in:
IlyaDoronin 2023-12-01 16:48:25 +03:00
parent 305b8707ce
commit eed127a236
14 changed files with 161 additions and 139 deletions

@ -25,8 +25,6 @@ export const Footer = ({
useEffect(() => {
clearDisabledQuestions();
const nextStepId = questions[stepNumber + 1].id;
const disabledIds = [] as string[];
const newDisabledIds = new Set([...disabledQuestionsId, ...disabledIds]);
@ -37,7 +35,7 @@ export const Footer = ({
const cleanDisabledQuestions = new Set<string>();
answers.forEach(({ step, answer }) => {
questions[step].content.rule.main.forEach(({ next, rules }) => {
questions[step - 1].content.rule.main.forEach(({ next, rules }) => {
rules.forEach(({ answers }) => {
if (answer !== answers[0]) {
cleanDisabledQuestions.add(next);

@ -1,3 +1,4 @@
import { useParams } from "react-router-dom";
import {
Box,
Button,
@ -12,7 +13,8 @@ import { devlog } from "@frontend/kitui";
import { quizApi } from "@api/quiz";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useQuizStore } from "@root/quizes/store";
import { useQuestions } from "@root/questions/hooks";
import { setQuizes } from "@root/quizes/actions";
type StartPageViewPublicationProps = {
@ -24,9 +26,12 @@ export const StartPageViewPublication = ({
setStepNumber,
showNextButton,
}: StartPageViewPublicationProps) => {
const quizId = Number(useParams().quizId);
const { quizes } = useQuizStore();
const { questions } = useQuestions();
const theme = useTheme();
const isTablet = useMediaQuery(theme.breakpoints.down(630));
const quiz = useCurrentQuiz();
const quiz = quizes.find(({ backendId }) => quizId === backendId);
const isMediaFileExist =
quiz?.config.startpage.background.desktop ||
quiz?.config.startpage.background.video;
@ -103,10 +108,8 @@ export const StartPageViewPublication = ({
<Box>
<Button
variant="contained"
sx={{
fontSize: "16px",
padding: "10px 15px",
}}
sx={{ fontSize: "16px", padding: "10px 15px" }}
disabled={!questions.length}
onClick={() => setStepNumber(1)}
>
{quiz?.config.startpage.button

@ -1,25 +1,22 @@
import { useState } from "react";
import { useParams } from "react-router-dom";
import DatePicker from "react-datepicker";
import { Box, Typography } from "@mui/material";
import { questionStore, updateQuestionsList } from "@root/questions";
import { useQuizViewStore, updateAnswer } from "@root/quizView";
import "react-datepicker/dist/react-datepicker.css";
import type { QuizQuestionDate } from "../../../model/questionTypes/date";
type DateProps = {
stepNumber: number;
question: QuizQuestionDate;
};
export const Date = ({ question }: DateProps) => {
export const Date = ({ stepNumber, question }: DateProps) => {
const [startDate, setStartDate] = useState<Date | null>(new window.Date());
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const totalIndex = listQuestions[quizId].findIndex(
({ id }) => question.id === id
);
const { answers } = useQuizViewStore();
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
return (
<Box>

@ -1,5 +1,4 @@
import { useState } from "react";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import {
Box,
Typography,
@ -9,7 +8,7 @@ import {
useTheme,
} from "@mui/material";
import { questionStore, updateQuestionsList } from "@root/questions";
import { useQuizViewStore, updateAnswer } from "@root/quizView";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
@ -17,25 +16,33 @@ import RadioIcon from "@ui_kit/RadioIcon";
import type { QuizQuestionEmoji } from "../../../model/questionTypes/emoji";
type EmojiProps = {
stepNumber: number;
question: QuizQuestionEmoji;
};
export const Emoji = ({ question }: EmojiProps) => {
const [valueIndex, setValueIndex] = useState<number>(0);
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
export const Emoji = ({ stepNumber, question }: EmojiProps) => {
const { answers } = useQuizViewStore();
const theme = useTheme();
const totalIndex = listQuestions[quizId].findIndex(
({ id }) => question.id === id
);
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
useEffect(() => {
if (!answer) {
updateAnswer(stepNumber, question.content.variants[0].id);
}
}, []);
return (
<Box>
<Typography variant="h5">{question.title}</Typography>
<RadioGroup
name={question.id}
value={valueIndex}
onChange={({ target }) => setValueIndex(Number(target.value))}
value={question.content.variants.findIndex(({ id }) => answer === id)}
onChange={({ target }) =>
updateAnswer(
stepNumber,
question.content.variants[Number(target.value)].id
)
}
sx={{
display: "flex",
flexWrap: "wrap",

@ -1,10 +1,8 @@
import { useState } from "react";
import { useParams } from "react-router-dom";
import { Box, Typography, ButtonBase } from "@mui/material";
import UploadBox from "@ui_kit/UploadBox";
import { questionStore, updateQuestionsList } from "@root/questions";
import { useQuizViewStore, updateAnswer } from "@root/quizView";
import { UPLOAD_FILE_TYPES_MAP } from "@ui_kit/QuizPreview/QuizPreviewQuestionTypes/File";
import UploadIcon from "@icons/UploadIcon";
@ -13,24 +11,19 @@ import type { ChangeEvent } from "react";
import type { QuizQuestionFile } from "../../../model/questionTypes/file";
type FileProps = {
stepNumber: number;
question: QuizQuestionFile;
};
export const File = ({ question }: FileProps) => {
const [fileName, setFileName] = useState<string>("");
const [file, setFile] = useState<string>();
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const totalIndex = listQuestions[quizId].findIndex(
({ id }) => question.id === id
);
export const File = ({ stepNumber, question }: FileProps) => {
const { answers } = useQuizViewStore();
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
const uploadFile = ({ target }: ChangeEvent<HTMLInputElement>) => {
const file = target.files?.[0];
if (file) {
setFileName(file.name);
setFile(URL.createObjectURL(file));
updateAnswer(stepNumber, `${file.name}|${URL.createObjectURL(file)}`);
}
};
@ -55,8 +48,10 @@ export const File = ({ question }: FileProps) => {
/>
<UploadBox icon={<UploadIcon />} text="5 MB максимум" />
</ButtonBase>
{fileName && (
<Typography sx={{ marginTop: "15px" }}>{fileName}</Typography>
{answer?.split("|")[0] && (
<Typography sx={{ marginTop: "15px" }}>
{answer?.split("|")[0]}
</Typography>
)}
</Box>
</Box>

@ -1,5 +1,4 @@
import { useState } from "react";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import {
Box,
Typography,
@ -10,7 +9,7 @@ import {
useMediaQuery,
} from "@mui/material";
import { questionStore, updateQuestionsList } from "@root/questions";
import { useQuizViewStore, updateAnswer } from "@root/quizView";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
@ -18,27 +17,35 @@ import RadioIcon from "@ui_kit/RadioIcon";
import type { QuizQuestionImages } from "../../../model/questionTypes/images";
type ImagesProps = {
stepNumber: number;
question: QuizQuestionImages;
};
export const Images = ({ question }: ImagesProps) => {
const [valueIndex, setValueIndex] = useState<number>(0);
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
export const Images = ({ stepNumber, question }: ImagesProps) => {
const { answers } = useQuizViewStore();
const theme = useTheme();
const totalIndex = listQuestions[quizId].findIndex(
({ id }) => question.id === id
);
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isMobile = useMediaQuery(theme.breakpoints.down(500));
useEffect(() => {
if (!answer) {
updateAnswer(stepNumber, question.content.variants[0].id);
}
}, []);
return (
<Box>
<Typography variant="h5">{question.title}</Typography>
<RadioGroup
name={question.id}
value={valueIndex}
onChange={({ target }) => setValueIndex(Number(target.value))}
value={question.content.variants.findIndex(({ id }) => answer === id)}
onChange={({ target }) =>
updateAnswer(
stepNumber,
question.content.variants[Number(target.value)].id
)
}
sx={{
display: "flex",
flexWrap: "wrap",

@ -1,25 +1,30 @@
import { useState } from "react";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import { Box, Typography, Slider, useTheme } from "@mui/material";
import CustomTextField from "@ui_kit/CustomTextField";
import { questionStore, updateQuestionsList } from "@root/questions";
import { useQuizViewStore, updateAnswer } from "@root/quizView";
import type { QuizQuestionNumber } from "../../../model/questionTypes/number";
type NumberProps = {
stepNumber: number;
question: QuizQuestionNumber;
};
export const Number = ({ question }: NumberProps) => {
const [value, setValue] = useState<number>(0);
const quizId = window.Number(useParams().quizId);
const { listQuestions } = questionStore();
export const Number = ({ stepNumber, question }: NumberProps) => {
const theme = useTheme();
const totalIndex = listQuestions[quizId].findIndex(
({ id }) => question.id === id
);
const { answers } = useQuizViewStore();
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
const min = window.Number(question.content.range.split("—")[0]);
const max = window.Number(question.content.range.split("—")[1]);
useEffect(() => {
if (!answer) {
updateAnswer(stepNumber, "1");
}
}, []);
return (
<Box>
@ -34,25 +39,34 @@ export const Number = ({ question }: NumberProps) => {
>
<CustomTextField
placeholder="0"
value={String(value)}
value={answer || ""}
onChange={({ target }) => {
updateAnswer(
stepNumber,
window.Number(target.value) > max
? String(max)
: window.Number(target.value) < min
? String(min)
: target.value
);
}}
sx={{
maxWidth: "80px",
"& .MuiInputBase-input": {
textAlign: "center",
},
"& .MuiInputBase-input": { textAlign: "center" },
}}
/>
<Slider
value={value}
min={window.Number(question.content.range.split("—")[0])}
max={window.Number(question.content.range.split("—")[1])}
value={window.Number(answer || 1)}
min={min}
max={max}
step={question.content.step || 1}
sx={{
color: theme.palette.brightPurple.main,
padding: "0",
marginTop: "25px",
}}
onChange={(_, value) => {
setValue(value as number);
updateAnswer(stepNumber, String(value));
}}
/>
</Box>

@ -1,19 +1,17 @@
import { useParams } from "react-router-dom";
import { Box, Typography } from "@mui/material";
import { questionStore, updateQuestionsList } from "@root/questions";
import { useQuizViewStore, updateAnswer } from "@root/quizView";
import type { QuizQuestionPage } from "../../../model/questionTypes/page";
type PageProps = {
stepNumber: number;
question: QuizQuestionPage;
};
export const Page = ({ question }: PageProps) => {
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const totalIndex = listQuestions[quizId].findIndex(
({ id }) => question.id === id
);
export const Page = ({ stepNumber, question }: PageProps) => {
const { answers } = useQuizViewStore();
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
return (
<Box>

@ -1,4 +1,3 @@
import { useParams } from "react-router-dom";
import {
Box,
Typography,
@ -6,23 +5,21 @@ import {
useTheme,
} from "@mui/material";
import { questionStore, updateQuestionsList } from "@root/questions";
import { useQuizViewStore, updateAnswer } from "@root/quizView";
import StarIconMini from "@icons/questionsPage/StarIconMini";
import type { QuizQuestionRating } from "../../../model/questionTypes/rating";
type RatingProps = {
stepNumber: number;
question: QuizQuestionRating;
};
export const Rating = ({ question }: RatingProps) => {
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const totalIndex = listQuestions[quizId].findIndex(
({ id }) => question.id === id
);
export const Rating = ({ stepNumber, question }: RatingProps) => {
const { answers } = useQuizViewStore();
const theme = useTheme();
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
return (
<Box>
@ -36,6 +33,8 @@ export const Rating = ({ question }: RatingProps) => {
}}
>
<RatingComponent
value={Number(answer || 0)}
onChange={(_, value) => updateAnswer(stepNumber, String(value))}
sx={{ height: "50px" }}
max={question.content.steps}
icon={

@ -1,22 +1,19 @@
import { Box, Typography } from "@mui/material";
import { useParams } from "react-router-dom";
import { Select as SelectComponent } from "../../../pages/Questions/Select";
import { questionStore, updateQuestionsList } from "@root/questions";
import { useQuizViewStore, updateAnswer } from "@root/quizView";
import type { QuizQuestionSelect } from "../../../model/questionTypes/select";
type SelectProps = {
stepNumber: number;
question: QuizQuestionSelect;
};
export const Select = ({ question }: SelectProps) => {
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const totalIndex = listQuestions[quizId].findIndex(
({ id }) => question.id === id
);
export const Select = ({ stepNumber, question }: SelectProps) => {
const { answers } = useQuizViewStore();
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
return (
<Box>
@ -30,8 +27,10 @@ export const Select = ({ question }: SelectProps) => {
}}
>
<SelectComponent
activeItemIndex={Number(answer) || 0}
items={question.content.variants.map(({ answer }) => answer)}
onChange={(action, num) => {
onChange={(_, value) => {
updateAnswer(stepNumber, String(value));
}}
/>
</Box>

@ -1,22 +1,19 @@
import { useParams } from "react-router-dom";
import { Box, Typography } from "@mui/material";
import CustomTextField from "@ui_kit/CustomTextField";
import { questionStore, updateQuestionsList } from "@root/questions";
import { useQuizViewStore, updateAnswer } from "@root/quizView";
import type { QuizQuestionText } from "../../../model/questionTypes/text";
type TextProps = {
stepNumber: number;
question: QuizQuestionText;
};
export const Text = ({ question }: TextProps) => {
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const totalIndex = listQuestions[quizId].findIndex(
({ id }) => question.id === id
);
export const Text = ({ stepNumber, question }: TextProps) => {
const { answers } = useQuizViewStore();
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
return (
<Box>
@ -29,7 +26,11 @@ export const Text = ({ question }: TextProps) => {
marginTop: "20px",
}}
>
<CustomTextField placeholder={question.content.placeholder} />
<CustomTextField
placeholder={question.content.placeholder}
value={answer || ""}
onChange={({ target }) => updateAnswer(stepNumber, target.value)}
/>
</Box>
</Box>
);

@ -23,8 +23,7 @@ type VariantProps = {
export const Variant = ({ stepNumber, question }: VariantProps) => {
const { answers } = useQuizViewStore();
const theme = useTheme();
const answerIndex = answers.findIndex(({ step }) => step === stepNumber);
const answer = answers[answerIndex]?.answer;
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
useEffect(() => {
if (!answer) {

@ -1,5 +1,4 @@
import { useState } from "react";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import {
Box,
Typography,
@ -9,7 +8,7 @@ import {
useTheme,
} from "@mui/material";
import { questionStore } from "@root/questions";
import { useQuizViewStore, updateAnswer } from "@root/quizView";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
@ -17,17 +16,21 @@ import RadioIcon from "@ui_kit/RadioIcon";
import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg";
type VarimgProps = {
stepNumber: number;
question: QuizQuestionVarImg;
};
export const Varimg = ({ question }: VarimgProps) => {
const [valueIndex, setValueIndex] = useState<number>(-1);
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
export const Varimg = ({ stepNumber, question }: VarimgProps) => {
const { answers } = useQuizViewStore();
const theme = useTheme();
const totalIndex = listQuestions[quizId].findIndex(
({ id }) => question.id === id
);
const { answer } = answers.find(({ step }) => step === stepNumber) ?? {};
const variant = question.content.variants.find(({ id }) => answer === id);
useEffect(() => {
if (!answer) {
updateAnswer(stepNumber, question.content.variants[0].id);
}
}, []);
return (
<Box>
@ -35,8 +38,13 @@ export const Varimg = ({ question }: VarimgProps) => {
<Box sx={{ display: "flex" }}>
<RadioGroup
name={question.id}
value={valueIndex}
onChange={({ target }) => setValueIndex(Number(target.value))}
value={question.content.variants.findIndex(({ id }) => answer === id)}
onChange={({ target }) =>
updateAnswer(
stepNumber,
question.content.variants[Number(target.value)].id
)
}
sx={{
display: "flex",
flexWrap: "wrap",
@ -67,15 +75,10 @@ export const Varimg = ({ question }: VarimgProps) => {
))}
</Box>
</RadioGroup>
{(question.content.variants[valueIndex]?.extendedText ||
question.content.back) && (
{(variant?.extendedText || question.content.back) && (
<Box sx={{ maxWidth: "400px", width: "100%", height: "300px" }}>
<img
src={
valueIndex >= 0
? question.content.variants[valueIndex].extendedText
: question.content.back
}
src={answer ? variant?.extendedText : question.content.back}
style={{ width: "100%", height: "100%", objectFit: "cover" }}
alt=""
/>

@ -186,16 +186,18 @@ export default function StartPage() {
>
Предпросмотр
</Button>
<Button
variant="contained"
sx={{
fontSize: "14px",
lineHeight: "18px",
height: "34px",
}}
>
Опубликовать
</Button>
<a href={`/view/${quiz?.backendId}`} target="_blank" rel="noreferrer" style={{ textDecoration: "none" }}>
<Button
variant="contained"
sx={{
fontSize: "14px",
lineHeight: "18px",
height: "34px",
}}
>
Опубликовать
</Button>
</a>
<CustomAvatar
sx={{
ml: "11px",