611 lines
19 KiB
TypeScript
611 lines
19 KiB
TypeScript
import * as React from "react";
|
||
|
||
import { getQuestionByContentId, updateQuestion, uploadQuestionImage } from "@root/questions/actions"
|
||
import { useCurrentQuiz } from "@root/quizes/hooks"
|
||
|
||
|
||
import CustomTextField from "@ui_kit/CustomTextField";
|
||
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||
import { UploadImageModal } from "../../Questions/UploadImage/UploadImageModal";
|
||
import { useDisclosure } from "../../../utils/useDisclosure";
|
||
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
||
|
||
import {
|
||
Box,
|
||
IconButton,
|
||
Paper,
|
||
Button,
|
||
Typography,
|
||
TextField,
|
||
useMediaQuery,
|
||
useTheme,
|
||
FormControl,
|
||
Popover
|
||
} from "@mui/material";
|
||
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
|
||
|
||
import ExpandLessIconBG from "@icons/ExpandLessIconBG";
|
||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||
import Trash from "@icons/trash";
|
||
import Info from "@icons/Info";
|
||
import SettingIcon from "@icons/questionsPage/settingIcon";
|
||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||
import { MutableRefObject } from "react";
|
||
|
||
interface Props {
|
||
resultContract: boolean;
|
||
resultData: QuizQuestionResult;
|
||
}
|
||
|
||
export const checkEmptyData = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
||
let check = true
|
||
if (
|
||
resultData.title.length > 0 ||
|
||
resultData.description.length > 0 ||
|
||
resultData.content.back.length > 0 ||
|
||
resultData.content.originalBack.length > 0 ||
|
||
resultData.content.innerName.length > 0 ||
|
||
resultData.content.text.length > 0 ||
|
||
resultData.content.video.length > 0 ||
|
||
resultData.content.hint.text.length > 0
|
||
) check = false
|
||
return check
|
||
}
|
||
|
||
const InfoView = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
||
const checkEmpty = checkEmptyData({ resultData })
|
||
const question = getQuestionByContentId(resultData.content.rule.parentId)
|
||
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);
|
||
|
||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||
setAnchorEl(event.currentTarget);
|
||
};
|
||
|
||
const handleClose = () => {
|
||
setAnchorEl(null);
|
||
};
|
||
|
||
const open = Boolean(anchorEl);
|
||
const id = open ? 'simple-popover' : undefined;
|
||
|
||
return (
|
||
<>
|
||
<Info
|
||
sx={{
|
||
"MuiIconButton-root": {
|
||
|
||
boxShadow: "0 0 10px 10px red"
|
||
}
|
||
}}
|
||
className={checkEmpty ? "blink" : ""}
|
||
onClick={handleClick}
|
||
|
||
/>
|
||
<Popover
|
||
id={id}
|
||
open={open}
|
||
anchorEl={anchorEl}
|
||
onClose={handleClose}
|
||
anchorOrigin={{
|
||
vertical: 'bottom',
|
||
horizontal: 'left',
|
||
}}
|
||
>
|
||
<Paper
|
||
sx={{
|
||
p: '20px',
|
||
display: "flex",
|
||
justifyContent: "center",
|
||
alignItems: "center",
|
||
flexDirection: "column"
|
||
}}
|
||
>
|
||
<Typography>
|
||
{resultData?.content.rule.parentId === "line" ? "Единый результат в конце прохождения опросника без ветвления"
|
||
:
|
||
`Заголовок вопроса, после которого появится результат: "${question?.title || "нет заголовка"}"`
|
||
}
|
||
|
||
</Typography>
|
||
{checkEmpty &&
|
||
<Typography color="red">
|
||
Вы не заполнили этот результат никакими данными
|
||
</Typography>
|
||
}
|
||
|
||
</Paper>
|
||
</Popover>
|
||
</>
|
||
)
|
||
}
|
||
|
||
export const ResultCard = ({ resultContract, resultData }: Props) => {
|
||
console.log("resultData", resultData)
|
||
|
||
const quizQid = useCurrentQuiz()?.qid;
|
||
const theme = useTheme();
|
||
|
||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||
const isTablet = useMediaQuery(theme.breakpoints.down(800));
|
||
|
||
const [expand, setExpand] = React.useState(true)
|
||
const [resultCardSettings, setResultCardSettings] = React.useState(false)
|
||
const [buttonPlus, setButtonPlus] = React.useState(true)
|
||
|
||
React.useEffect(() => {
|
||
setExpand(true)
|
||
}, [resultContract])
|
||
|
||
|
||
const {
|
||
isCropModalOpen,
|
||
openCropModal,
|
||
closeCropModal,
|
||
imageBlob,
|
||
originalImageUrl,
|
||
setCropModalImageBlob,
|
||
} = useCropModalState();
|
||
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
||
|
||
|
||
|
||
async function handleImageUpload(file: File) {
|
||
const url = await uploadQuestionImage(resultData.id, quizQid, file, (question, url) => {
|
||
|
||
question.content.back = url;
|
||
question.content.originalBack = url;
|
||
});
|
||
closeImageUploadModal();
|
||
openCropModal(file, url);
|
||
}
|
||
|
||
function handleCropModalSaveClick(imageBlob: Blob) {
|
||
uploadQuestionImage(resultData.id, quizQid, imageBlob, (question, url) => {
|
||
question.content.back = url;
|
||
});
|
||
}
|
||
|
||
|
||
|
||
|
||
return (
|
||
<Paper
|
||
data-cy="quiz-question-card"
|
||
sx={{
|
||
maxWidth: "796px",
|
||
width: "100%",
|
||
borderRadius: "12px",
|
||
backgroundColor: expand ? "white" : "#EEE4FC",
|
||
border: expand ? "none" : "1px solid #9A9AAF",
|
||
boxShadow: "0px 10px 30px #e7e7e7",
|
||
m: "20px 0"
|
||
}}
|
||
>
|
||
<Box
|
||
sx={{
|
||
display: expand ? "none" : "flex",
|
||
alignItems: "center",
|
||
padding: isMobile ? "10px" : "20px",
|
||
flexDirection: isMobile ? "column" : null,
|
||
justifyContent: "space-between",
|
||
minHeight: "40px",
|
||
|
||
}}
|
||
>
|
||
<FormControl
|
||
variant="standard"
|
||
sx={{
|
||
p: 0,
|
||
maxWidth: isTablet ? "549px" : "640px",
|
||
width: "100%",
|
||
marginRight: isMobile ? "0px" : "16.1px",
|
||
}}
|
||
>
|
||
<TextField
|
||
value={resultData.title}
|
||
placeholder={"Заголовок результата"}
|
||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, question => question.title = target.value)}
|
||
sx={{
|
||
margin: isMobile ? "10px 0" : 0,
|
||
"& .MuiInputBase-root": {
|
||
color: "#000000",
|
||
backgroundColor: expand
|
||
? theme.palette.background.default
|
||
: "transparent",
|
||
height: "48px",
|
||
borderRadius: "10px",
|
||
".MuiOutlinedInput-notchedOutline": {
|
||
borderWidth: "1px !important",
|
||
border: !expand ? "none" : null,
|
||
},
|
||
"& .MuiInputBase-input::placeholder": {
|
||
color: "#4D4D4D",
|
||
opacity: 0.8,
|
||
},
|
||
},
|
||
}}
|
||
inputProps={{
|
||
sx: {
|
||
p: 0,
|
||
fontSize: "18px",
|
||
lineHeight: "21px",
|
||
},
|
||
}}
|
||
/>
|
||
</FormControl>
|
||
<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={() => setExpand(!expand)}
|
||
>
|
||
{expand ? (
|
||
<ExpandLessIconBG />
|
||
) : (
|
||
<ExpandLessIcon
|
||
sx={{
|
||
boxSizing: "border-box",
|
||
fill: theme.palette.brightPurple.main,
|
||
background: "#FFF",
|
||
borderRadius: "6px",
|
||
height: "30px",
|
||
width: "30px",
|
||
}}
|
||
/>
|
||
)}
|
||
</IconButton>
|
||
<InfoView resultData={resultData} />
|
||
</Box>
|
||
</Box>
|
||
{expand && (
|
||
<>
|
||
<Box
|
||
sx={{
|
||
overflow: "hidden",
|
||
maxWidth: "796px",
|
||
height: "100%",
|
||
bgcolor: "#FFFFFF",
|
||
borderRadius: "12px",
|
||
boxShadow: "0px 10px 30px #e7e7e7",
|
||
}}
|
||
>
|
||
<Box sx={{ p: "0 20px", pt: "30px" }}>
|
||
<Box
|
||
sx={{
|
||
width: "100%",
|
||
maxWidth: "760px",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: "10px",
|
||
mb: "19px",
|
||
}}
|
||
>
|
||
<CustomTextField
|
||
value={resultData.title}
|
||
placeholder={"Заголовок результата"}
|
||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, question => question.title = target.value)} />
|
||
<IconButton
|
||
sx={{ padding: "0", margin: "5px" }}
|
||
disableRipple
|
||
data-cy="expand-question"
|
||
onClick={() => setExpand(!expand)}
|
||
>
|
||
<ExpandLessIconBG />
|
||
</IconButton>
|
||
<InfoView resultData={resultData} />
|
||
|
||
|
||
</Box>
|
||
|
||
<Box
|
||
sx={{
|
||
margin: "20px 0"
|
||
}}
|
||
>
|
||
<CustomTextField
|
||
value={resultData.description}
|
||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.description = target.value)}
|
||
placeholder={"Заголовок пожирнее"}
|
||
sx={{
|
||
borderRadius: "8px",
|
||
height: "48px",
|
||
width: "100%",
|
||
}}
|
||
/>
|
||
</Box>
|
||
|
||
<TextField
|
||
|
||
value={resultData.content.text}
|
||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.content.text = target.value)}
|
||
fullWidth
|
||
placeholder="Описание"
|
||
multiline
|
||
rows={4}
|
||
sx={{
|
||
"& .MuiInputBase-root": {
|
||
backgroundColor: "#F2F3F7",
|
||
width: "100%",
|
||
height: "110px",
|
||
borderRadius: "10px",
|
||
},
|
||
}}
|
||
inputProps={{
|
||
sx: {
|
||
height: "85px",
|
||
borderRadius: "10px",
|
||
fontSize: "18px",
|
||
lineHeight: "21px",
|
||
py: 0,
|
||
},
|
||
}}
|
||
/>
|
||
|
||
|
||
|
||
<Box
|
||
sx={{
|
||
mt: "20px",
|
||
display: "flex",
|
||
gap: "10px",
|
||
flexDirection: "column"
|
||
}}
|
||
>
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
}}
|
||
>
|
||
<Button
|
||
sx={{
|
||
color: resultData.content.useImage ? "#7E2AEA" : "#9A9AAF",
|
||
fontSize: "16px",
|
||
"&:hover": {
|
||
background: "none",
|
||
},
|
||
}}
|
||
variant="text"
|
||
onClick={() => updateQuestion(resultData.id, (question) => question.content.useImage = true)}
|
||
>
|
||
Изображение
|
||
</Button>
|
||
<Button
|
||
sx={{
|
||
color: resultData.content.useImage ? "#9A9AAF" : "#7E2AEA",
|
||
fontSize: "16px",
|
||
"&:hover": {
|
||
background: "none",
|
||
},
|
||
}}
|
||
variant="text"
|
||
onClick={() => updateQuestion(resultData.id, (question) => question.content.useImage = false)}
|
||
>
|
||
Видео
|
||
</Button>
|
||
</Box>
|
||
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
}}
|
||
>
|
||
<UploadImageModal
|
||
isOpen={isImageUploadOpen}
|
||
onClose={closeImageUploadModal}
|
||
handleImageChange={handleImageUpload}
|
||
/>
|
||
<CropModal
|
||
isOpen={isCropModalOpen}
|
||
imageBlob={imageBlob}
|
||
originalImageUrl={originalImageUrl}
|
||
setCropModalImageBlob={setCropModalImageBlob}
|
||
onClose={closeCropModal}
|
||
onSaveImageClick={handleCropModalSaveClick}
|
||
/>
|
||
</Box>
|
||
|
||
{
|
||
resultData.content.useImage &&
|
||
<Box
|
||
sx={{
|
||
cursor: "pointer",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: "20px",
|
||
mb: "30px"
|
||
}}
|
||
>
|
||
<AddOrEditImageButton
|
||
imageSrc={resultData.content.back}
|
||
onImageClick={() => {
|
||
if (resultData.content.back) {
|
||
return openCropModal(
|
||
resultData.content.back,
|
||
resultData.content.originalBack
|
||
);
|
||
}
|
||
|
||
openImageUploadModal();
|
||
}}
|
||
onPlusClick={() => {
|
||
openImageUploadModal();
|
||
}}
|
||
/>
|
||
</Box>
|
||
}
|
||
{
|
||
!resultData.content.useImage &&
|
||
<Box
|
||
sx={{
|
||
cursor: "pointer",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: "20px",
|
||
mb: "30px"
|
||
}}
|
||
>
|
||
<CustomTextField
|
||
placeholder="URL видео"
|
||
text={resultData.content.video ?? ""}
|
||
onChange={e => updateQuestion(resultData.id, q => {
|
||
q.content.video = e.target.value;
|
||
})}
|
||
/>
|
||
</Box>
|
||
}
|
||
</Box>
|
||
|
||
|
||
|
||
{
|
||
buttonPlus ?
|
||
<Button
|
||
onClick={() => {
|
||
setButtonPlus(false)
|
||
}}
|
||
sx={{
|
||
display: "inline flex",
|
||
height: "48px",
|
||
padding: "10px 20px",
|
||
justifyContent: "center",
|
||
alignItems: "center",
|
||
gap: "8px",
|
||
flexShrink: 0,
|
||
borderRadius: "8px",
|
||
border: "1px solid #9A9AAF",
|
||
background: " #F2F3F7",
|
||
color: "#9A9AAF",
|
||
mb: "30px"
|
||
}}
|
||
>
|
||
Кнопка +
|
||
</Button>
|
||
:
|
||
<Box
|
||
sx={{
|
||
mb: "30px"
|
||
}}
|
||
>
|
||
<Box>
|
||
<Typography component={"span"} sx={{ weight: "500", fontSize: "18px", mb: "10px" }}>
|
||
Призыв к действию
|
||
</Typography>
|
||
<IconButton
|
||
onClick={() => {
|
||
setButtonPlus(true)
|
||
updateQuestion(resultData.id, (q) => q.content.hint.text = "")
|
||
}}
|
||
>
|
||
<Trash />
|
||
</IconButton>
|
||
</Box>
|
||
|
||
<TextField
|
||
value={resultData.content.hint.text}
|
||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.content.hint.text = target.value)}
|
||
fullWidth
|
||
placeholder="Например: узнать подробнее"
|
||
sx={{
|
||
"& .MuiInputBase-root": {
|
||
backgroundColor: "#F2F3F7",
|
||
width: "409px",
|
||
height: "48px",
|
||
borderRadius: "8px",
|
||
},
|
||
}}
|
||
inputProps={{
|
||
sx: {
|
||
height: "85px",
|
||
borderRadius: "10px",
|
||
fontSize: "18px",
|
||
lineHeight: "21px",
|
||
py: 0,
|
||
},
|
||
}}
|
||
/>
|
||
</Box>
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</Box>
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
width: "100%",
|
||
background: "#F2F3F7",
|
||
}}
|
||
>
|
||
<Box
|
||
sx={{
|
||
padding: "20px",
|
||
display: "flex",
|
||
flexWrap: "wrap",
|
||
gap: "10px",
|
||
}}
|
||
>
|
||
<MiniButtonSetting
|
||
onClick={() => {
|
||
setResultCardSettings(!resultCardSettings)
|
||
}}
|
||
sx={{
|
||
backgroundColor:
|
||
resultCardSettings
|
||
? theme.palette.brightPurple.main
|
||
: "transparent",
|
||
color:
|
||
resultCardSettings ? "#ffffff" : theme.palette.grey3.main,
|
||
"&:hover": {
|
||
backgroundColor: resultCardSettings ? "#581CA7" : "#7E2AEA",
|
||
color: "white"
|
||
}
|
||
}}
|
||
>
|
||
<SettingIcon
|
||
color={
|
||
resultCardSettings ? "#ffffff" : theme.palette.grey3.main
|
||
}
|
||
/>
|
||
{!isTablet && "Настройки"}
|
||
</MiniButtonSetting>
|
||
</Box>
|
||
</Box>
|
||
</Box>
|
||
{
|
||
resultCardSettings &&
|
||
<Box
|
||
sx={{
|
||
backgroundColor: "white",
|
||
p: "20px",
|
||
borderRadius: "0 0 12px 12px"
|
||
}}
|
||
>
|
||
<CustomTextField
|
||
placeholder={"Внутреннее описание вопроса"}
|
||
value={resultData.innerName}
|
||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.content.innerName = target.value)}
|
||
/>
|
||
</Box>
|
||
}
|
||
</>
|
||
)
|
||
}
|
||
</Paper >
|
||
)
|
||
}
|