2023-11-14 20:15:52 +00:00
import { CrossedEyeIcon } from "@icons/CrossedEyeIcon" ;
import { ArrowDownIcon } from "@icons/questionsPage/ArrowDownIcon" ;
import { CopyIcon } from "@icons/questionsPage/CopyIcon" ;
2023-09-25 13:43:15 +00:00
import { OneIcon } from "@icons/questionsPage/OneIcon" ;
import { PointsIcon } from "@icons/questionsPage/PointsIcon" ;
2023-07-06 15:54:12 +00:00
import Answer from "@icons/questionsPage/answer" ;
import Date from "@icons/questionsPage/date" ;
2023-11-14 20:15:52 +00:00
import { DeleteIcon } from "@icons/questionsPage/deleteIcon" ;
2023-07-06 15:54:12 +00:00
import Download from "@icons/questionsPage/download" ;
2023-11-14 20:15:52 +00:00
import DropDown from "@icons/questionsPage/drop_down" ;
import Emoji from "@icons/questionsPage/emoji" ;
import { HideIcon } from "@icons/questionsPage/hideIcon" ;
import Input from "@icons/questionsPage/input" ;
import OptionsAndPict from "@icons/questionsPage/options_and_pict" ;
import OptionsPict from "@icons/questionsPage/options_pict" ;
2023-07-06 15:54:12 +00:00
import Page from "@icons/questionsPage/page" ;
import RatingIcon from "@icons/questionsPage/rating" ;
2023-11-14 20:15:52 +00:00
import Slider from "@icons/questionsPage/slider" ;
import ExpandLessIcon from "@mui/icons-material/ExpandLess" ;
2023-11-17 15:42:49 +00:00
import {
2023-12-19 23:08:33 +00:00
Box ,
Button ,
Checkbox ,
FormControl ,
FormControlLabel ,
IconButton ,
InputAdornment ,
Modal ,
Paper ,
TextField ,
Typography ,
useMediaQuery ,
useTheme ,
2023-11-17 15:42:49 +00:00
} from "@mui/material" ;
2023-12-19 23:08:33 +00:00
import {
copyQuestion ,
createUntypedQuestion ,
deleteQuestion ,
clearRuleForAll ,
toggleExpandQuestion ,
updateQuestion ,
updateUntypedQuestion ,
getQuestionByContentId ,
deleteQuestionWithTimeout ,
} from "@root/questions/actions" ;
2023-12-11 10:08:54 +00:00
import { updateRootContentId } from "@root/quizes/actions" ;
2023-11-17 15:42:49 +00:00
import { useRef , useState } from "react" ;
2023-11-14 20:15:52 +00:00
import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd" ;
2023-11-17 15:42:49 +00:00
import { useDebouncedCallback } from "use-debounce" ;
2023-09-13 10:58:07 +00:00
import { ReactComponent as PlusIcon } from "../../../assets/icons/plus.svg" ;
2023-11-29 13:49:52 +00:00
import type { AnyTypedQuizQuestion , UntypedQuizQuestion } from "../../../model/questionTypes/shared" ;
2023-11-15 18:38:02 +00:00
import SwitchQuestionsPage from "../SwitchQuestionsPage" ;
import { ChooseAnswerModal } from "./ChooseAnswerModal" ;
2023-11-29 13:49:52 +00:00
import TypeQuestions from "../TypeQuestions" ;
import { QuestionType } from "@model/question/question" ;
2023-12-08 13:36:00 +00:00
import { useCurrentQuiz } from "@root/quizes/hooks" ;
2023-12-11 10:08:54 +00:00
import { useQuestionsStore } from "@root/questions/store" ;
2023-08-08 11:01:37 +00:00
2023-06-28 23:32:43 +00:00
interface Props {
2023-11-29 13:49:52 +00:00
question : AnyTypedQuizQuestion | UntypedQuizQuestion ;
2023-11-14 20:15:52 +00:00
draggableProps : DraggableProvidedDragHandleProps | null | undefined ;
isDragging : boolean ;
2023-11-30 06:27:15 +00:00
index : number ;
2023-06-28 23:32:43 +00:00
}
2023-11-30 06:27:15 +00:00
export default function QuestionsPageCard ( { question , draggableProps , isDragging , index } : Props ) {
2023-12-20 14:56:06 +00:00
const maxLengthTextField = 225 ;
2023-12-19 23:08:33 +00:00
2023-12-12 19:05:30 +00:00
const { questions } = useQuestionsStore ( ) ;
2023-11-14 20:15:52 +00:00
const [ plusVisible , setPlusVisible ] = useState < boolean > ( false ) ;
const [ open , setOpen ] = useState < boolean > ( false ) ;
2023-12-19 23:08:33 +00:00
const [ isTextFieldtActive , setIsTextFieldtActive ] = useState ( false ) ;
2023-11-17 15:42:49 +00:00
2023-12-16 16:06:46 +00:00
const [ openDelete , setOpenDelete ] = useState < boolean > ( false ) ;
2023-11-14 20:15:52 +00:00
const theme = useTheme ( ) ;
const isTablet = useMediaQuery ( theme . breakpoints . down ( 1000 ) ) ;
const isMobile = useMediaQuery ( theme . breakpoints . down ( 790 ) ) ;
const anchorRef = useRef ( null ) ;
2023-12-04 16:33:50 +00:00
const quiz = useCurrentQuiz ( ) ;
2023-11-17 15:42:49 +00:00
const setTitle = useDebouncedCallback ( ( title ) = > {
2023-11-29 13:49:52 +00:00
const updateQuestionFn = question . type === null ? updateUntypedQuestion : updateQuestion ;
updateQuestionFn ( question . id , question = > {
2023-11-17 15:42:49 +00:00
question . title = title ;
} ) ;
2023-11-14 20:15:52 +00:00
} , 200 ) ;
2023-09-27 14:14:48 +00:00
2023-12-16 16:06:46 +00:00
const deleteFn = ( ) = > {
if ( question . type !== null ) {
if ( question . content . rule . parentId === "root" ) { //удалить из стора root и очистить rule всем вопросам
updateRootContentId ( quiz . id , "" ) ;
clearRuleForAll ( ) ;
deleteQuestion ( question . id ) ;
} else if ( question . content . rule . parentId . length > 0 ) { //удалить из стора вопрос из дерева и очистить е г о потомков
const clearQuestions = [ ] as string [ ] ;
//записываем потомков , а их результаты удаляем
const getChildren = ( parentQuestion : AnyTypedQuizQuestion ) = > {
questions . forEach ( ( targetQuestion ) = > {
2023-12-21 08:29:51 +00:00
if ( targetQuestion . type !== null && targetQuestion . content . rule . parentId === parentQuestion . content . id ) { //если у вопроса совпал родитель с родителем => он потомок, в кучу е г о
if ( targetQuestion . type !== null && targetQuestion . type !== "result" ) {
2023-12-16 16:06:46 +00:00
if ( ! clearQuestions . includes ( targetQuestion . content . id ) ) clearQuestions . push ( targetQuestion . content . id ) ;
getChildren ( targetQuestion ) ; //и ищем е г о потомков
}
}
} ) ;
} ;
getChildren ( question ) ;
//чистим потомков от инфы ветвления
clearQuestions . forEach ( ( id ) = > {
updateQuestion ( id , question = > {
question . content . rule . parentId = "" ;
question . content . rule . main = [ ] ;
question . content . rule . default = "" ;
} ) ;
} ) ;
//чистим rule родителя
const parentQuestion = getQuestionByContentId ( question . content . rule . parentId ) ;
const newRule = { } ;
newRule . main = parentQuestion . content . rule . main . filter ( ( data ) = > data . next !== question . content . id ) ; //удаляем условия перехода от родителя к этому вопросу
newRule . parentId = parentQuestion . content . rule . parentId ;
newRule . default = parentQuestion . content . rule . parentId === question . content . id ? "" : parentQuestion . content . rule . parentId ;
newRule . children = [ . . . parentQuestion . content . rule . children ] . splice ( parentQuestion . content . rule . children . indexOf ( question . content . id ) , 1 ) ;
updateQuestion ( question . content . rule . parentId , ( PQ ) = > {
PQ . content . rule = newRule ;
} ) ;
deleteQuestion ( question . id ) ;
}
deleteQuestion ( question . id ) ;
2023-12-23 02:16:02 +00:00
const result = questions . find ( q = > q . type === "result" && q . content . rule . parentId === question . content . id )
if ( result ) deleteQuestion ( result . id ) ;
2023-12-16 16:06:46 +00:00
} else {
deleteQuestion ( question . id ) ;
}
} ;
2023-12-19 23:08:33 +00:00
const handleInputFocus = ( ) = > {
setIsTextFieldtActive ( true ) ;
} ;
2023-12-16 16:06:46 +00:00
2023-12-19 23:08:33 +00:00
const handleInputBlur = ( event : React.FocusEvent < HTMLInputElement > ) = > {
setIsTextFieldtActive ( false ) ;
} ;
2023-12-16 16:06:46 +00:00
2023-12-20 14:56:06 +00:00
2023-11-14 20:15:52 +00:00
return (
2023-12-19 23:08:33 +00:00
< >
< Paper
id = { question . id }
data - cy = "quiz-question-card"
sx = { {
maxWidth : "796px" ,
width : "100%" ,
borderRadius : "12px" ,
backgroundColor : question.expanded ? "white" : "#EEE4FC" ,
border : question.expanded ? "none" : "1px solid #9A9AAF" ,
boxShadow : "0px 10px 30px #e7e7e7" ,
} }
>
< Box
sx = { {
display : "flex" ,
alignItems : "center" ,
padding : isMobile ? "10px" : "20px 10px 20px 20px" ,
flexDirection : isMobile ? "column" : null ,
} }
>
< FormControl
variant = "standard"
sx = { {
p : 0 ,
maxWidth : isTablet ? "549px" : "640px" ,
width : "100%" ,
marginRight : isMobile ? "0px" : "16.1px" ,
} }
>
< TextField
defaultValue = { question . title }
placeholder = { "Заголовок вопроса" }
onChange = { ( { target } : { target : HTMLInputElement } ) = > 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 ( question . expanded , question . type ) }
< / InputAdornment >
< ChooseAnswerModal
open = { open }
onClose = { ( ) = > setOpen ( false ) }
anchorRef = { anchorRef }
question = { question }
questionType = { question . type }
/ >
< / Box >
) ,
endAdornment : isTextFieldtActive && question . title . length >= maxLengthTextField - 7 && (
< Box
sx = { {
display : "flex" ,
marginTop : "5px" ,
marginLeft : "auto" ,
position : "absolute" ,
bottom : "-28px" ,
right : "0" ,
} }
>
< Typography fontSize = "14px" > { question . title . length } < / Typography >
< span > / < / span >
< Typography fontSize = "14px" > { maxLengthTextField } < / Typography >
< / Box >
) ,
} }
sx = { {
margin : isMobile ? "10px 0" : 0 ,
"& .MuiInputBase-root" : {
color : "#000000" ,
backgroundColor : question.expanded ? theme . palette . background . default : "transparent" ,
height : "48px" ,
borderRadius : "10px" ,
".MuiOutlinedInput-notchedOutline" : {
borderWidth : "1px !important" ,
border : ! question . expanded ? "none" : null ,
} ,
"& .MuiInputBase-input::placeholder" : {
color : "#4D4D4D" ,
opacity : 0.8 ,
} ,
} ,
} }
/ >
< / 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 = { ( ) = > toggleExpandQuestion ( question . id ) }
>
{ question . expanded ? (
< 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 >
{ question . expanded ? (
< > < / >
) : (
< Box
2023-10-09 14:39:40 +00:00
sx = { {
2023-12-19 23:08:33 +00:00
display : "flex" ,
height : "30px" ,
borderRight : "solid 1px #4D4D4D" ,
2023-10-09 14:39:40 +00:00
} }
2023-12-19 23:08:33 +00:00
>
< FormControlLabel
control = {
< Checkbox
icon = {
< HideIcon
style = { {
boxSizing : "border-box" ,
color : "#7E2AEA" ,
background : "#FFF" ,
borderRadius : "6px" ,
height : "30px" ,
width : "30px" ,
padding : "3px" ,
} }
/ >
}
checkedIcon = { < CrossedEyeIcon / > }
/ >
}
label = { "" }
sx = { {
color : theme.palette.grey2.main ,
ml : "-9px" ,
mr : 0 ,
userSelect : "none" ,
} }
/ >
< IconButton sx = { { padding : "0" } } onClick = { ( ) = > copyQuestion ( question . id , question . quizId ) } >
< CopyIcon style = { { color : theme.palette.brightPurple.main } } / >
< / IconButton >
< IconButton
sx = { {
cursor : "pointer" ,
borderRadius : "6px" ,
padding : "0" ,
margin : "0 5px 0 10px" ,
} }
onClick = { ( ) = > {
if ( question . content . rule . parentId . length !== 0 ) {
setOpenDelete ( true ) ;
} else {
deleteQuestionWithTimeout ( question . id , deleteFn ) ;
}
} }
data - cy = "delete-question"
>
< DeleteIcon style = { { color : theme.palette.brightPurple.main } } / >
< / IconButton >
< Modal open = { openDelete } onClose = { ( ) = > setOpenDelete ( false ) } >
< Box
2023-11-14 20:15:52 +00:00
sx = { {
2023-12-19 23:08:33 +00:00
position : "absolute" ,
top : "50%" ,
left : "50%" ,
transform : "translate(-50%, -50%)" ,
padding : "30px" ,
borderRadius : "10px" ,
background : "#FFFFFF" ,
2023-11-14 20:15:52 +00:00
} }
2023-12-19 23:08:33 +00:00
>
< Typography variant = "h6" sx = { { textAlign : "center" } } >
В ы у д а л я е т е в о п р о с , у ч а с т в у ю щ и й в в е т в л е н и и . В с е е г о п о т о м к и п о т е р я ю т д а н н ы е в е т в л е н и я . В ы
у в е р е н ы , ч т о х о т и т е у д а л и т ь в о п р о с ?
< / Typography >
2023-11-14 20:15:52 +00:00
< Box
2023-12-19 23:08:33 +00:00
sx = { {
marginTop : "30px" ,
display : "flex" ,
justifyContent : "center" ,
gap : "15px" ,
} }
2023-11-14 20:15:52 +00:00
>
2023-12-19 23:08:33 +00:00
< Button variant = "contained" sx = { { minWidth : "150px" } } onClick = { ( ) = > setOpenDelete ( false ) } >
О т м е н а
< / Button >
< Button
variant = "contained"
sx = { { minWidth : "150px" } }
onClick = { ( ) = > {
deleteQuestionWithTimeout ( question . id , deleteFn ) ;
2023-11-14 20:15:52 +00:00
} }
2023-12-19 23:08:33 +00:00
>
П о д т в е р д и т ь
< / Button >
2023-11-14 20:15:52 +00:00
< / Box >
2023-12-19 23:08:33 +00:00
< / Box >
< / Modal >
< / Box >
) }
{ question . type !== null && (
< Box
style = { {
display : "flex" ,
alignItems : "center" ,
justifyContent : "center" ,
height : "30px" ,
width : "30px" ,
marginLeft : "3px" ,
borderRadius : "50%" ,
fontSize : "16px" ,
color : question.expanded ? theme . palette . brightPurple . main : "#FFF" ,
background : question.expanded ? "#EEE4FC" : theme . palette . brightPurple . main ,
2023-11-14 20:15:52 +00:00
} }
2023-12-19 23:08:33 +00:00
>
{ question . page + 1 }
< / Box >
) }
< IconButton
disableRipple
sx = { {
padding : isMobile ? "0" : "0 5px" ,
right : isMobile ? "0" : null ,
bottom : isMobile ? "0" : null ,
} }
{ . . . draggableProps }
2023-09-15 12:37:12 +00:00
>
2023-12-19 23:08:33 +00:00
< PointsIcon style = { { color : "#4D4D4D" , fontSize : "30px" } } / >
< / IconButton >
< / Box >
< / Box >
{ question . expanded && (
< Box
sx = { {
display : "flex" ,
flexDirection : "column" ,
padding : 0 ,
borderRadius : "12px" ,
} }
>
{ question . type === null ? (
< TypeQuestions question = { question } / >
) : (
< SwitchQuestionsPage question = { question } / >
) }
< / Box >
) }
< / Paper >
< Box
onMouseEnter = { ( ) = > setPlusVisible ( true ) }
onMouseLeave = { ( ) = > setPlusVisible ( false ) }
sx = { {
maxWidth : "825px" ,
display : "flex" ,
alignItems : "center" ,
height : "40px" ,
cursor : "pointer" ,
} }
>
< Box
onClick = { ( ) = > createUntypedQuestion ( question . quizId , question . id ) }
sx = { {
display : plusVisible && ! isDragging ? "flex" : "none" ,
width : "100%" ,
alignItems : "center" ,
columnGap : "10px" ,
} }
data - cy = "create-question"
>
< Box
sx = { {
boxSizing : "border-box" ,
width : "100%" ,
height : "1px" ,
backgroundPosition : "bottom" ,
backgroundRepeat : "repeat-x" ,
backgroundSize : "20px 1px" ,
backgroundImage : "radial-gradient(circle, #7E2AEA 6px, #F2F3F7 1px)" ,
} }
/ >
< PlusIcon / >
< / Box >
< / Box >
< / >
) ;
2023-12-20 14:56:06 +00:00
2023-08-08 11:01:37 +00:00
}
2023-11-14 20:15:52 +00:00
2023-11-29 13:49:52 +00:00
const IconAndrom = ( isExpanded : boolean , questionType : QuestionType | null ) = > {
switch ( questionType ) {
2023-11-14 20:15:52 +00:00
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 < > < / > ;
}
} ;