frontPanel/src/pages/Questions/DraggableList/QuestionPageCardTitle.tsx

485 lines
15 KiB
TypeScript
Raw Normal View History

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 {
2024-03-15 14:56:39 +00:00
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";
2024-11-22 15:09:19 +00:00
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 (
<>
2024-05-14 14:27:14 +00:00
<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={"Заголовок вопроса"}
2024-11-22 15:09:19 +00:00
onChange={({ target }) => {
if (target.value.length > maxLengthTextField) {
enqueueSnackbar("Превышена длина вводимого текста")
} else {
setTitle(target.value || " ")
}
}}
2024-05-14 14:27:14 +00:00
onFocus={handleInputFocus}
onBlur={handleInputBlur}
inputProps={{
2024-11-22 15:09:19 +00:00
// maxLength: maxLengthTextField,
2024-05-14 14:27:14 +00:00
}}
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={{
2024-05-14 14:27:14 +00:00
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,
},
},
}}
2024-05-14 14:27:14 +00:00
/>
</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}
>
2024-05-14 14:27:14 +00:00
<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",
}}
2024-05-14 14:27:14 +00:00
/>
) : (
<ExpandLessIcon
sx={{
2024-05-14 14:27:14 +00:00
boxSizing: "border-box",
fill: theme.palette.brightPurple.main,
background: "#FFF",
borderRadius: "6px",
height: "30px",
width: "30px",
}}
2024-05-14 14:27:14 +00:00
/>
)}
</IconButton>
{isExpanded ? (
<></>
) : (
<Box
2024-05-14 14:27:14 +00:00
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={{
2024-05-14 14:27:14 +00:00
cursor: "pointer",
borderRadius: "6px",
padding: "0",
margin: "0 5px 0 10px",
}}
2024-05-14 14:27:14 +00:00
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",
}}
>
2024-05-14 14:27:14 +00:00
<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)}
>
2024-05-14 14:27:14 +00:00
Отмена
</Button>
<Button
variant="contained"
sx={{ minWidth: "150px" }}
onClick={() => {
deleteQuestionWithTimeout(questionId, () =>
DeleteFunction(questionId),
);
}}
>
2024-05-14 14:27:14 +00:00
Подтвердить
</Button>
</Box>
</Box>
</Modal>
</Box>
2024-05-14 14:27:14 +00:00
)}
{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>
2024-05-14 14:27:14 +00:00
</Box>
{questionType !== null && (
<Box
sx={{
display: "flex",
alignItems: "center",
flexWrap: isMobile ? "wrap" : undefined,
gap: "10px",
2024-05-14 14:27:14 +00:00
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 <></>;
}
};