485 lines
15 KiB
TypeScript
485 lines
15 KiB
TypeScript
import { ArrowDownIcon } from "@icons/questionsPage/ArrowDownIcon";
|
||
import { CopyIcon } from "@icons/questionsPage/CopyIcon";
|
||
import CopyIconPurple from "@icons/CopyIcon";
|
||
import { PointsIcon } from "@icons/questionsPage/PointsIcon";
|
||
import Answer from "@icons/questionsPage/answer";
|
||
import Date from "@icons/questionsPage/date";
|
||
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
|
||
import Download from "@icons/questionsPage/download";
|
||
import DropDown from "@icons/questionsPage/drop_down";
|
||
import Emoji from "@icons/questionsPage/emoji";
|
||
import Input from "@icons/questionsPage/input";
|
||
import OptionsAndPict from "@icons/questionsPage/options_and_pict";
|
||
import OptionsPict from "@icons/questionsPage/options_pict";
|
||
import Page from "@icons/questionsPage/page";
|
||
import RatingIcon from "@icons/questionsPage/rating";
|
||
import Slider from "@icons/questionsPage/slider";
|
||
import { QuestionType } from "@model/question/question";
|
||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||
import {
|
||
Box,
|
||
Button,
|
||
FormControl,
|
||
IconButton,
|
||
InputAdornment,
|
||
Modal,
|
||
TextField as MuiTextField,
|
||
TextFieldProps,
|
||
Typography,
|
||
useMediaQuery,
|
||
useTheme,
|
||
} from "@mui/material";
|
||
import {
|
||
collapseAllQuestions,
|
||
copyQuestion,
|
||
deleteQuestion,
|
||
deleteQuestionWithTimeout,
|
||
toggleExpandQuestion,
|
||
updateQuestion,
|
||
updateUntypedQuestion,
|
||
} from "@root/questions/actions";
|
||
import { DeleteFunction } from "@utils/deleteFunc";
|
||
import { FC, memo, useRef, useState } from "react";
|
||
import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
|
||
import { ChooseAnswerModal } from "./ChooseAnswerModal";
|
||
import { enqueueSnackbar } from "notistack";
|
||
|
||
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
|
||
|
||
const maxLengthTextField = 500;
|
||
|
||
interface Props {
|
||
draggableProps: DraggableProvidedDragHandleProps | null | undefined;
|
||
quizId: number;
|
||
questionId: string;
|
||
questionBackendId: number;
|
||
questionContentId: string | null;
|
||
title: string;
|
||
questionType: QuestionType | null;
|
||
page: number | null;
|
||
isExpanded: boolean;
|
||
questionHasParent: boolean;
|
||
}
|
||
|
||
const QuestionPageCardTitle = memo<Props>(function ({
|
||
draggableProps,
|
||
quizId,
|
||
questionId,
|
||
questionBackendId,
|
||
questionContentId,
|
||
title,
|
||
questionType,
|
||
page,
|
||
isExpanded,
|
||
questionHasParent,
|
||
}) {
|
||
const theme = useTheme();
|
||
const [open, setOpen] = useState<boolean>(false);
|
||
const [isTextFieldtActive, setIsTextFieldtActive] = useState(false);
|
||
const [openDelete, setOpenDelete] = useState<boolean>(false);
|
||
const anchorRef = useRef<HTMLDivElement>(null);
|
||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||
|
||
const setTitle = (title: string) => {
|
||
const updateQuestionFn =
|
||
questionType === null ? updateUntypedQuestion : updateQuestion;
|
||
|
||
updateQuestionFn(questionId, (question) => {
|
||
question.title = title;
|
||
});
|
||
};
|
||
|
||
const handleInputFocus = () => {
|
||
setIsTextFieldtActive(true);
|
||
};
|
||
|
||
const handleInputBlur = () => {
|
||
setIsTextFieldtActive(false);
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
padding: isMobile ? "10px" : "20px 10px 20px 20px",
|
||
flexDirection: "row",
|
||
flexWrap: isMobile && isExpanded ? "wrap" : "nowrap",
|
||
}}
|
||
>
|
||
<FormControl
|
||
variant="standard"
|
||
sx={{
|
||
p: 0,
|
||
maxWidth: isTablet ? "549px" : "640px",
|
||
width: "100%",
|
||
marginRight: isMobile ? "0px" : "16.1px",
|
||
display: "flex",
|
||
flexDirection: "row",
|
||
flexBasis: isMobile && isExpanded ? "calc(100% - 30px)" : null,
|
||
}}
|
||
>
|
||
<TextField
|
||
id="questionTitle"
|
||
value={title}
|
||
placeholder={"Заголовок вопроса"}
|
||
onChange={({ target }) => {
|
||
if (target.value.length > maxLengthTextField) {
|
||
enqueueSnackbar("Превышена длина вводимого текста")
|
||
} else {
|
||
setTitle(target.value || " ")
|
||
}
|
||
|
||
}}
|
||
onFocus={handleInputFocus}
|
||
onBlur={handleInputBlur}
|
||
inputProps={{
|
||
// maxLength: maxLengthTextField,
|
||
}}
|
||
InputProps={{
|
||
startAdornment: (
|
||
<Box>
|
||
<InputAdornment
|
||
ref={anchorRef}
|
||
position="start"
|
||
sx={{ cursor: "pointer" }}
|
||
onClick={() => setOpen((isOpened) => !isOpened)}
|
||
>
|
||
{IconAndrom(isExpanded, questionType)}
|
||
</InputAdornment>
|
||
<ChooseAnswerModal
|
||
open={open}
|
||
onClose={() => setOpen(false)}
|
||
anchorRef={anchorRef}
|
||
questionId={questionId}
|
||
questionContentId={questionContentId}
|
||
questionType={questionType}
|
||
/>
|
||
</Box>
|
||
),
|
||
endAdornment: isTextFieldtActive &&
|
||
title.length >= maxLengthTextField - 7 && (
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
marginTop: "5px",
|
||
marginLeft: "auto",
|
||
position: "absolute",
|
||
bottom: "-28px",
|
||
right: "0",
|
||
}}
|
||
>
|
||
<Typography fontSize="14px">{title.length}</Typography>
|
||
<span>/</span>
|
||
<Typography fontSize="14px">
|
||
{maxLengthTextField}
|
||
</Typography>
|
||
</Box>
|
||
),
|
||
}}
|
||
sx={{
|
||
flexGrow: 1,
|
||
margin: isMobile ? "10px 0" : 0,
|
||
"& .MuiInputBase-root": {
|
||
color: "#000000",
|
||
backgroundColor: isExpanded
|
||
? theme.palette.background.default
|
||
: "transparent",
|
||
height: "48px",
|
||
borderRadius: "10px",
|
||
".MuiOutlinedInput-notchedOutline": {
|
||
borderWidth: "1px !important",
|
||
border: !isExpanded ? "none" : null,
|
||
},
|
||
"& .MuiInputBase-input::placeholder": {
|
||
color: "#4D4D4D",
|
||
opacity: 0.8,
|
||
},
|
||
},
|
||
}}
|
||
/>
|
||
</FormControl>
|
||
<IconButton
|
||
disableRipple
|
||
sx={{
|
||
order: isMobile && isExpanded ? "0" : "1",
|
||
padding: isMobile ? "0" : "0 5px",
|
||
right: isMobile ? "0" : null,
|
||
bottom: isMobile ? "0" : null,
|
||
marginLeft: !isMobile && isExpanded ? "10px" : null,
|
||
}}
|
||
{...draggableProps}
|
||
onMouseDown={collapseAllQuestions}
|
||
onTouchStart={collapseAllQuestions}
|
||
>
|
||
<PointsIcon style={{ color: "#4D4D4D", fontSize: "30px" }} />
|
||
</IconButton>
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "flex-end",
|
||
width: isMobile ? "100%" : "auto",
|
||
position: "relative",
|
||
}}
|
||
>
|
||
<IconButton
|
||
sx={{ padding: "0", margin: "5px" }}
|
||
disableRipple
|
||
data-cy="expand-question"
|
||
onClick={() => toggleExpandQuestion(questionId)}
|
||
>
|
||
{isExpanded ? (
|
||
<ArrowDownIcon
|
||
style={{
|
||
width: "18px",
|
||
color: "#4D4D4D",
|
||
}}
|
||
/>
|
||
) : (
|
||
<ExpandLessIcon
|
||
sx={{
|
||
boxSizing: "border-box",
|
||
fill: theme.palette.brightPurple.main,
|
||
background: "#FFF",
|
||
borderRadius: "6px",
|
||
height: "30px",
|
||
width: "30px",
|
||
}}
|
||
/>
|
||
)}
|
||
</IconButton>
|
||
{isExpanded ? (
|
||
<></>
|
||
) : (
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
height: "30px",
|
||
borderRight: "solid 1px #4D4D4D",
|
||
}}
|
||
>
|
||
<IconButton
|
||
sx={{ padding: "0" }}
|
||
onClick={() => copyQuestion(questionId, quizId)}
|
||
>
|
||
<CopyIcon style={{ color: theme.palette.brightPurple.main }} />
|
||
</IconButton>
|
||
<IconButton
|
||
sx={{
|
||
cursor: "pointer",
|
||
borderRadius: "6px",
|
||
padding: "0",
|
||
margin: "0 5px 0 10px",
|
||
}}
|
||
onClick={() => {
|
||
if (questionType === null) {
|
||
deleteQuestion(questionId);
|
||
return;
|
||
}
|
||
if (questionHasParent) {
|
||
setOpenDelete(true);
|
||
} else {
|
||
deleteQuestionWithTimeout(questionId, () =>
|
||
DeleteFunction(questionId),
|
||
);
|
||
}
|
||
}}
|
||
data-cy="delete-question"
|
||
>
|
||
<DeleteIcon
|
||
style={{ color: theme.palette.brightPurple.main }}
|
||
/>
|
||
</IconButton>
|
||
<Modal open={openDelete} onClose={() => setOpenDelete(false)}>
|
||
<Box
|
||
sx={{
|
||
position: "absolute",
|
||
top: "50%",
|
||
left: "50%",
|
||
transform: "translate(-50%, -50%)",
|
||
padding: "30px",
|
||
borderRadius: "10px",
|
||
background: "#FFFFFF",
|
||
}}
|
||
>
|
||
<Typography variant="h6" sx={{ textAlign: "center" }}>
|
||
Вы удаляете вопрос, участвующий в ветвлении. Все его потомки
|
||
потеряют данные ветвления. Вы уверены, что хотите удалить
|
||
вопрос?
|
||
</Typography>
|
||
<Box
|
||
sx={{
|
||
marginTop: "30px",
|
||
display: "flex",
|
||
justifyContent: "center",
|
||
gap: "15px",
|
||
}}
|
||
>
|
||
<Button
|
||
variant="contained"
|
||
sx={{ minWidth: "150px" }}
|
||
onClick={() => setOpenDelete(false)}
|
||
>
|
||
Отмена
|
||
</Button>
|
||
<Button
|
||
variant="contained"
|
||
sx={{ minWidth: "150px" }}
|
||
onClick={() => {
|
||
deleteQuestionWithTimeout(questionId, () =>
|
||
DeleteFunction(questionId),
|
||
);
|
||
}}
|
||
>
|
||
Подтвердить
|
||
</Button>
|
||
</Box>
|
||
</Box>
|
||
</Modal>
|
||
</Box>
|
||
)}
|
||
{page !== null && (
|
||
<Box
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
height: "30px",
|
||
width: "30px",
|
||
marginLeft: "3px",
|
||
borderRadius: "50%",
|
||
fontSize: "16px",
|
||
color: isExpanded ? theme.palette.brightPurple.main : "#FFF",
|
||
background: isExpanded
|
||
? "#EEE4FC"
|
||
: theme.palette.brightPurple.main,
|
||
}}
|
||
>
|
||
{page + 1}
|
||
</Box>
|
||
)}
|
||
</Box>
|
||
</Box>
|
||
{questionType !== null && (
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
flexWrap: isMobile ? "wrap" : undefined,
|
||
gap: "10px",
|
||
padding: "0 20px 20px 20px",
|
||
}}
|
||
>
|
||
<Typography>ID Вопроса</Typography>
|
||
<Typography id={"id-copy"}>{questionBackendId}</Typography>
|
||
<IconButton
|
||
edge="end"
|
||
onClick={() =>
|
||
navigator.clipboard.writeText(
|
||
document.querySelector("#id-copy").innerText,
|
||
)
|
||
}
|
||
>
|
||
<CopyIconPurple
|
||
color={"#ffffff"}
|
||
width={"30px"}
|
||
bgcolor={theme.palette.brightPurple.main}
|
||
/>
|
||
</IconButton>
|
||
</Box>
|
||
)}
|
||
</>
|
||
);
|
||
});
|
||
|
||
QuestionPageCardTitle.displayName = "QuestionPageCardTitle";
|
||
|
||
export default QuestionPageCardTitle;
|
||
|
||
const IconAndrom = (isExpanded: boolean, questionType: QuestionType | null) => {
|
||
switch (questionType) {
|
||
case "variant":
|
||
return (
|
||
<Answer
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
case "images":
|
||
return (
|
||
<OptionsPict
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
case "varimg":
|
||
return (
|
||
<OptionsAndPict
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
case "emoji":
|
||
return (
|
||
<Emoji
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
case "text":
|
||
return (
|
||
<Input
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
case "select":
|
||
return (
|
||
<DropDown
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
case "date":
|
||
return (
|
||
<Date
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
case "number":
|
||
return (
|
||
<Slider
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
case "file":
|
||
return (
|
||
<Download
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
case "page":
|
||
return (
|
||
<Page
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
case "rating":
|
||
return (
|
||
<RatingIcon
|
||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||
sx={{ height: "22px", width: "20px" }}
|
||
/>
|
||
);
|
||
default:
|
||
return <></>;
|
||
}
|
||
};
|