frontPanel/src/pages/Questions/BranchingModal/Settings.tsx
2024-04-26 17:41:36 +03:00

836 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
Box,
MenuItem,
FormControl,
Checkbox,
FormControlLabel,
Radio,
RadioGroup,
Typography,
useTheme,
Select,
useMediaQuery,
IconButton,
TextField,
} from "@mui/material";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
import { QuizQuestionBase } from "model/questionTypes/shared";
import { useState, useRef, useEffect } from "react";
import { useParams } from "react-router-dom";
import { useQuestionsStore } from "@root/questions/store";
import { updateQuestion, getQuestionById } from "@root/questions/actions";
import { SelectChangeEvent } from "@mui/material/Select";
import CalendarIcon from "@icons/CalendarIcon";
import { DatePicker } from "@mui/x-date-pickers";
import dayjs from "dayjs";
import { TimePicker } from "@mui/x-date-pickers/TimePicker";
import InfoIcon from "@icons/Info";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
import type { AnyTypedQuizQuestion } from "../../../model/questionTypes/shared";
import type { QuizQuestionNumber } from "../../../model/questionTypes/number";
const CONDITIONS = [
"Все условия обязательны",
"Обязательно хотя бы одно условие",
];
interface Props {
parentQuestion: AnyTypedQuizQuestion;
targetQuestion: AnyTypedQuizQuestion;
ruleIndex: number;
setParentQuestion: (q: AnyTypedQuizQuestion) => void;
}
// Этот компонент вызывается 1 раз на каждое условие родителя для перехода к этому вопросу. Поэтому для изменения стора мы знаем индекс
export const TypeSwitch = ({
parentQuestion,
targetQuestion,
ruleIndex,
setParentQuestion,
}: Props) => {
switch (parentQuestion.type) {
case "variant":
case "images":
case "varimg":
case "emoji":
case "select":
return parentQuestion.content.variants === undefined ? (
<BlockRule text={"У родителя нет вариантов"} />
) : (
<SelectorType
targetQuestion={targetQuestion}
parentQuestion={parentQuestion}
ruleIndex={ruleIndex}
setParentQuestion={setParentQuestion}
/>
);
break;
case "date":
return (
<DateInputsType
targetQuestion={targetQuestion}
parentQuestion={parentQuestion}
ruleIndex={ruleIndex}
setParentQuestion={setParentQuestion}
/>
);
break;
case "number":
return (
<NumberInputsType
targetQuestion={targetQuestion}
parentQuestion={parentQuestion}
ruleIndex={ruleIndex}
setParentQuestion={setParentQuestion}
/>
);
break;
case "page":
return (
<BlockRule text={"У такого родителя может быть только один потомок"} />
);
break;
case "text":
return (
<TextInputsType
targetQuestion={targetQuestion}
parentQuestion={parentQuestion}
ruleIndex={ruleIndex}
setParentQuestion={setParentQuestion}
/>
);
break;
case "file":
return (
<FileInputsType
targetQuestion={targetQuestion}
parentQuestion={parentQuestion}
ruleIndex={ruleIndex}
setParentQuestion={setParentQuestion}
/>
);
break;
case "rating":
return (
<RatingInputsType
targetQuestion={targetQuestion}
parentQuestion={parentQuestion}
ruleIndex={ruleIndex}
setParentQuestion={setParentQuestion}
/>
);
break;
default:
return <BlockRule text={"Не распознан тип родительского вопроса"} />;
break;
}
};
export const BlockRule = ({ text }: { text: string }) => {
return (
<Typography
sx={{
margin: "100px 0",
textAlign: "center",
}}
>
{text}
</Typography>
);
};
const SelectorType = ({
parentQuestion,
targetQuestion,
ruleIndex,
setParentQuestion,
}: Props) => {
const theme = useTheme();
const quizId = Number(useParams().quizId);
return (
<Box
sx={{
padding: "20px",
margin: "20px",
borderRadius: "8px",
bgcolor: "#F2F3F7",
height: "280px",
overflow: "auto",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
pb: "5px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Новое условие
</Typography>
<IconButton
sx={{ borderRadius: "6px", padding: "2px" }}
onClick={() => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main.splice(ruleIndex, 1);
setParentQuestion(newParentQuestion);
}}
>
<DeleteIcon color={"#4D4D4D"} />
</IconButton>
</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
pb: "10px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Дан ответ
</Typography>
<Typography sx={{ color: "#7E2AEA", pl: "10px" }}>
(Укажите один или несколько вариантов)
</Typography>
</Box>
<Select
multiple
value={
parentQuestion.content?.rule?.main[ruleIndex]?.rules[0]?.answers || []
}
onChange={(event: SelectChangeEvent) => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers = (
event.target as HTMLSelectElement
).value;
setParentQuestion(newParentQuestion);
}}
sx={{
width: "100%",
height: "48px",
borderRadius: "8px",
"& .MuiOutlinedInput-notchedOutline": {
border: `1px solid ${theme.palette.brightPurple.main} !important`,
height: "48px",
borderRadius: "10px",
},
}}
>
{parentQuestion.content.variants.map((e: any) => {
return <MenuItem value={e.id}>{e.answer}</MenuItem>;
})}
</Select>
<FormControl>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
value={parentQuestion.content.rule.main[ruleIndex].or}
onChange={(_, value) => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main[ruleIndex].or = value;
setParentQuestion(newParentQuestion);
}}
>
{CONDITIONS.map((condition, totalIndex) => (
<FormControlLabel
key={totalIndex}
sx={{ color: theme.palette.grey2.main }}
value={Boolean(Number(totalIndex))}
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label={condition}
/>
))}
</RadioGroup>
</FormControl>
</Box>
);
};
const DateInputsType = ({
parentQuestion,
targetQuestion,
ruleIndex,
setParentQuestion,
}: Props) => {
const theme = useTheme();
const upLg = useMediaQuery(theme.breakpoints.up("md"));
const time = dayjs(new Date());
const [firstDate, setFirstDate] = useState(time);
const [secondDate, setSecondDate] = useState(time);
const [firstTime, setFirstTime] = useState(time);
const [secondTime, setSecondTime] = useState(time);
useEffect(() => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[0] = time;
if (newParentQuestion.content.dateRange)
parentQuestion.content.rule.main[ruleIndex].rules[0].answers[1] = time;
setParentQuestion(newParentQuestion);
}, [firstDate, secondDate, firstTime, secondTime]);
return (
<Box
sx={{
padding: "20px",
margin: "20px",
borderRadius: "8px",
bgcolor: "#F2F3F7",
height: "280px",
overflow: "auto",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
pb: "5px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Новое условие
</Typography>
<IconButton
sx={{ borderRadius: "6px", padding: "2px" }}
onClick={() => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main.splice(ruleIndex, 1);
setParentQuestion(newParentQuestion);
}}
>
<DeleteIcon color={"#4D4D4D"} />
</IconButton>
</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
pb: "10px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Дан ответ
</Typography>
<Typography sx={{ color: "#7E2AEA", pl: "10px" }}>
(Укажите один или несколько вариантов)
</Typography>
</Box>
<Box
sx={{
backgroundColor: "#E8EAEE",
margin: "10px",
}}
>
{parentQuestion.content.dateRange && (
<Typography sx={{ color: "#4D4D4D", p: "10px" }}>
(Начало периода)
</Typography>
)}
<DatePicker
defaultValue={dayjs(
new Date(
parentQuestion.content.rule.main[ruleIndex].rules[0].answers[0],
).toLocaleDateString(),
)}
onChange={(dateString) => {
const date = dateString?.toDate().toLocaleDateString("ru-RU", {
year: "numeric",
month: "2-digit",
day: "2-digit",
});
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers = [
date,
];
setParentQuestion(newParentQuestion);
}}
slots={{
openPickerIcon: () => <CalendarIcon />,
}}
slotProps={{
openPickerButton: {
sx: {
p: 0,
},
"data-cy": "open-datepicker",
},
}}
sx={{
p: "10px",
"& .MuiInputBase-root": {
minWidth: "325px",
backgroundColor: "#F2F3F7",
borderRadius: "10px",
pr: "31px",
"& input": {
py: "11px",
pl: upLg ? "20px" : "13px",
lineHeight: "19px",
},
"& fieldset": {
borderColor: "#9A9AAF",
},
},
}}
/>
{parentQuestion.content.time && (
<TimePicker
value={
parentQuestion.content.rule.main[ruleIndex].rules[0].answers[0]
}
sx={{
p: "10px",
"& .MuiInputBase-root": {
minWidth: "325px",
backgroundColor: "#F2F3F7",
borderRadius: "10px",
pr: "22px",
"& input": {
py: "11px",
pl: upLg ? "20px" : "13px",
lineHeight: "19px",
},
"& fieldset": {
borderColor: "#9A9AAF",
},
},
}}
/>
)}
</Box>
{parentQuestion.content.dateRange && (
<Box
sx={{
backgroundColor: "#E8EAEE",
margin: "10px",
}}
>
{parentQuestion.content.dateRange && (
<Typography sx={{ color: "#4D4D4D", p: "10px" }}>
(Конец периода)
</Typography>
)}
<DatePicker
value={
parentQuestion.content.rule.main[ruleIndex].rules[0].answers[1]
}
onChange={() => {}}
slots={{
openPickerIcon: () => <CalendarIcon />,
}}
slotProps={{
openPickerButton: {
sx: {
p: 0,
},
"data-cy": "open-datepicker",
},
}}
sx={{
p: "10px",
"& .MuiInputBase-root": {
minWidth: "325px",
backgroundColor: "#F2F3F7",
borderRadius: "10px",
pr: "31px",
"& input": {
py: "11px",
pl: upLg ? "20px" : "13px",
lineHeight: "19px",
},
"& fieldset": {
borderColor: "#9A9AAF",
},
},
}}
/>
{parentQuestion.content.time && (
<TimePicker
value={
parentQuestion.content.rule.main[ruleIndex].rules[0].answers[1]
}
sx={{
p: "10px",
"& .MuiInputBase-root": {
minWidth: "325px",
backgroundColor: "#F2F3F7",
borderRadius: "10px",
pr: "22px",
"& input": {
py: "11px",
pl: upLg ? "20px" : "13px",
lineHeight: "19px",
},
"& fieldset": {
borderColor: "#9A9AAF",
},
},
}}
/>
)}
</Box>
)}
</Box>
);
};
const NumberInputsType = ({
parentQuestion,
targetQuestion,
ruleIndex,
setParentQuestion,
}: Props) => {
const theme = useTheme();
const quizId = Number(useParams().quizId);
return (
<Box
sx={{
padding: "20px",
margin: "20px",
borderRadius: "8px",
bgcolor: "#F2F3F7",
height: "280px",
overflow: "auto",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
pb: "5px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Новое условие
</Typography>
<IconButton
sx={{ borderRadius: "6px", padding: "2px" }}
onClick={() => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main.splice(ruleIndex, 1);
setParentQuestion(newParentQuestion);
}}
>
<DeleteIcon color={"#4D4D4D"} />
</IconButton>
</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
pb: "10px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Дан ответ
</Typography>
<Typography sx={{ color: "#7E2AEA", pl: "10px" }}>
(Укажите один или несколько вариантов)
</Typography>
</Box>
<Box>
<TextField
sx={{ marginTop: "20px", width: "100%" }}
placeholder="от"
value={
parentQuestion.content.rule.main[
ruleIndex
].rules[0].answers[0]?.split("—")[0]
}
onChange={({ target }: React.ChangeEvent<HTMLInputElement>) => {
const newParentQuestion = JSON.parse(
JSON.stringify(parentQuestion),
);
const previousValue =
newParentQuestion.content.rule.main[ruleIndex].rules[0]
.answers[0];
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[0] =
(parentQuestion as QuizQuestionNumber).content.chooseRange
? previousValue
? `${target.value}${previousValue.split("—")[1] || 0}`
: `${target.value}—0`
: target.value;
setParentQuestion(newParentQuestion);
}}
/>
{(parentQuestion as QuizQuestionNumber).content.chooseRange && (
<TextField
placeholder="до"
sx={{ marginTop: "20px", width: "100%" }}
value={
parentQuestion.content.rule.main[
ruleIndex
].rules[0].answers[0]?.split("—")[1]
}
onChange={({ target }: React.ChangeEvent<HTMLInputElement>) => {
const newParentQuestion = JSON.parse(
JSON.stringify(parentQuestion),
);
const previousValue =
newParentQuestion.content.rule.main[ruleIndex].rules[0]
.answers[0];
newParentQuestion.content.rule.main[
ruleIndex
].rules[0].answers[0] = previousValue
? `${previousValue.split("—")[0] || 0}${target.value}`
: `0—${target.value}`;
setParentQuestion(newParentQuestion);
}}
/>
)}
</Box>
</Box>
);
};
const TextInputsType = ({
parentQuestion,
targetQuestion,
ruleIndex,
setParentQuestion,
}: Props) => {
const theme = useTheme();
const quizId = Number(useParams().quizId);
return (
<Box
sx={{
padding: "20px",
margin: "20px",
borderRadius: "8px",
bgcolor: "#F2F3F7",
height: "280px",
overflow: "auto",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
pb: "5px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Новое условие
</Typography>
<IconButton
sx={{ borderRadius: "6px", padding: "2px" }}
onClick={() => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main.splice(ruleIndex, 1);
setParentQuestion(newParentQuestion);
}}
>
<DeleteIcon color={"#4D4D4D"} />
</IconButton>
</Box>
<Box
sx={{
display: "inline",
alignItems: "center",
pb: "10px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Дан ответ
</Typography>
<Typography sx={{ color: "#7E2AEA", pl: "10px", fontSize: "12px" }}>
(Укажите текст, при совпадении с которым пользователь попадёт на этот
вопрос)
</Typography>
</Box>
<TextField
sx={{
marginTop: "20px",
width: "100%",
}}
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[0]}
onChange={(event: React.FormEvent<HTMLInputElement>) => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers = [
(event.target as HTMLInputElement).value,
];
setParentQuestion(newParentQuestion);
}}
/>
</Box>
);
};
const FileInputsType = ({
parentQuestion,
targetQuestion,
ruleIndex,
setParentQuestion,
}: Props) => {
const theme = useTheme();
const quizId = Number(useParams().quizId);
return (
<Box
sx={{
padding: "20px",
margin: "20px",
borderRadius: "8px",
bgcolor: "#F2F3F7",
height: "280px",
overflow: "auto",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
pb: "5px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Новое условие
</Typography>
<IconButton
sx={{ borderRadius: "6px", padding: "2px" }}
onClick={() => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main.splice(ruleIndex, 1);
setParentQuestion(newParentQuestion);
}}
>
<DeleteIcon color={"#4D4D4D"} />
</IconButton>
</Box>
<Box
sx={{
display: "inline",
alignItems: "center",
pb: "10px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Перевести на этот вопрос если пользователь загрузил файл
</Typography>
</Box>
<FormControlLabel
control={
<Checkbox
sx={{
margin: 0,
}}
checked={
parentQuestion.content.rule.main[ruleIndex].rules[0].answers[0]
}
onChange={(event: React.FormEvent<HTMLInputElement>) => {
let newParentQuestion = JSON.parse(
JSON.stringify(parentQuestion),
);
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers =
[(event.target as HTMLInputElement).checked];
setParentQuestion(newParentQuestion);
}}
/>
}
label="да"
/>
</Box>
);
};
const RatingInputsType = ({
parentQuestion,
targetQuestion,
ruleIndex,
setParentQuestion,
}: Props) => {
const theme = useTheme();
const quizId = Number(useParams().quizId);
return (
<Box
sx={{
padding: "20px",
margin: "20px",
borderRadius: "8px",
bgcolor: "#F2F3F7",
height: "280px",
overflow: "auto",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
pb: "5px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Новое условие
</Typography>
<IconButton
sx={{ borderRadius: "6px", padding: "2px" }}
onClick={() => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
newParentQuestion.content.rule.main.splice(ruleIndex, 1);
setParentQuestion(newParentQuestion);
}}
>
<DeleteIcon color={"#4D4D4D"} />
</IconButton>
</Box>
<Box
sx={{
display: "inline",
alignItems: "center",
pb: "10px",
}}
>
<Typography sx={{ color: "#7E2AEA", pl: "10px", fontSize: "12px" }}>
Ожидаемое количество ячеек(не более доступного количества)
</Typography>
</Box>
<TextField
sx={{
marginTop: "20px",
width: "100%",
}}
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[0]}
onChange={(event: React.FormEvent<HTMLInputElement>) => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion));
let valueNumber = Number(
(event.target as HTMLInputElement).value.replace(/[^0-9,\s]/g, ""),
);
valueNumber =
valueNumber > parentQuestion.content.steps
? parentQuestion.content.steps
: valueNumber;
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers = [
valueNumber,
];
setParentQuestion(newParentQuestion);
}}
/>
</Box>
);
};