From f44950333b5bb46418f5e6b32167784fce321ed4 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Tue, 26 Dec 2023 15:57:44 +0300 Subject: [PATCH] feat: ConfirmLeaveModal --- src/pages/ResultPage/ResultSettings.tsx | 70 ++++- .../startPage/ConfirmLeaveModal/index.tsx | 72 +++++ src/pages/startPage/EditPage.tsx | 295 +++++++++++------- src/stores/uiTools/actions.ts | 1 + src/stores/uiTools/store.ts | 7 +- src/ui_kit/Sidebar.tsx | 220 +++++++------ 6 files changed, 433 insertions(+), 232 deletions(-) create mode 100644 src/pages/startPage/ConfirmLeaveModal/index.tsx diff --git a/src/pages/ResultPage/ResultSettings.tsx b/src/pages/ResultPage/ResultSettings.tsx index cbf592ae..d1b0c5d8 100644 --- a/src/pages/ResultPage/ResultSettings.tsx +++ b/src/pages/ResultPage/ResultSettings.tsx @@ -1,14 +1,18 @@ -import IconPlus from "@icons/IconPlus"; -import Info from "@icons/Info"; -import Plus from "@icons/Plus"; -import ArrowLeft from "@icons/questionsPage/arrowLeft"; -import { Box, Button, Typography, Paper, Modal, TextField } from "@mui/material"; +import { useEffect, useRef, useState } from "react"; +// import { useBlocker } from "react-router-dom"; +import { + Box, + Button, + Typography, + Paper, + Modal, + TextField, +} from "@mui/material"; import { incrementCurrentStep } from "@root/quizes/actions"; import CustomWrapper from "@ui_kit/CustomWrapper"; import { DescriptionForm } from "./DescriptionForm/DescriptionForm"; import { ResultListForm } from "./ResultListForm"; import { SettingForm } from "./SettingForm"; -import { useEffect, useRef, useState } from "react"; import { WhenCard } from "./cards/WhenCard"; import { ResultCard, checkEmptyData } from "./cards/ResultCard"; import { EmailSettingsCard } from "./cards/EmailSettingsCard"; @@ -17,15 +21,27 @@ import { useQuestionsStore } from "@root/questions/store"; import { deleteQuestion } from "@root/questions/actions"; import { QuizQuestionResult } from "@model/questionTypes/result"; +import IconPlus from "@icons/IconPlus"; +import Info from "@icons/Info"; +import Plus from "@icons/Plus"; +import ArrowLeft from "@icons/questionsPage/arrowLeft"; + export const ResultSettings = () => { const { questions } = useQuestionsStore(); const quiz = useCurrentQuiz(); - const results = useQuestionsStore().questions.filter((q): q is QuizQuestionResult => q.type === "result"); + const results = useQuestionsStore().questions.filter( + (q): q is QuizQuestionResult => q.type === "result" + ); const [quizExpand, setQuizExpand] = useState(true); const [resultContract, setResultContract] = useState(true); + const [triggerExit, setTriggerExit] = useState<{ + follow: boolean; + path: string; + }>({ follow: false, path: "" }); + const [openNotificationModal, setOpenNotificationModal] = + useState(true); const isReadyToLeaveRef = useRef(true); - -console.log('quiz ', quiz) + // const blocker = useBlocker(false); useEffect( function calcIsReadyToLeave() { @@ -42,11 +58,29 @@ console.log('quiz ', quiz) useEffect(() => { return () => { - if (isReadyToLeaveRef.current === false) alert("Пожалуйста, проверьте, что вы заполнили все результаты"); + if (!isReadyToLeaveRef.current && window.location.pathname !== "/edit") { + setOpenNotificationModal(true); + } }; }, []); - const cnsl = results.filter(q=> q.content.usage) + const cnsl = results.filter((q) => q.content.usage); + + const shouldBlock = true; // Replace this + + // useEffect(() => { + // if (shouldBlock) { + // blocker.proceed?.() + // } + // }, [shouldBlock]); + + const leavePage = (leave: boolean) => { + if (leave) { + console.log("ливаем"); + } + + setOpenNotificationModal(false); + }; return ( @@ -81,9 +115,13 @@ console.log('quiz ', quiz) - {quiz.config.resultInfo.when === "email" && } + {quiz.config.resultInfo.when === "email" && ( + + )} - + Создайте результат @@ -109,7 +147,11 @@ console.log('quiz ', quiz) {cnsl.map((resultQuestion) => ( - + ))} void; + cancel: () => void; +}; + +export const ConfirmLeaveModal = ({ + open, + follow, + cancel, +}: ConfirmLeaveModalProps) => ( + + + + + Пожалуйста, проверьте, что вы заполнили все результаты + + + + + + + + +); diff --git a/src/pages/startPage/EditPage.tsx b/src/pages/startPage/EditPage.tsx index 6651f449..891d3393 100755 --- a/src/pages/startPage/EditPage.tsx +++ b/src/pages/startPage/EditPage.tsx @@ -16,7 +16,13 @@ import { useMediaQuery, useTheme, } from "@mui/material"; -import { decrementCurrentStep, resetEditConfig, setQuizes, updateQuiz } from "@root/quizes/actions"; +import { + decrementCurrentStep, + resetEditConfig, + setQuizes, + updateQuiz, + setCurrentStep, +} from "@root/quizes/actions"; import { useCurrentQuiz } from "@root/quizes/hooks"; import { useQuizStore } from "@root/quizes/store"; import CustomAvatar from "@ui_kit/Header/Avatar"; @@ -32,8 +38,17 @@ import { Link, useNavigate } from "react-router-dom"; import useSWR from "swr"; import { useDebouncedCallback } from "use-debounce"; import { SidebarMobile } from "./Sidebar/SidebarMobile"; -import { cleanQuestions, createResult, setQuestions } from "@root/questions/actions"; -import { updateOpenBranchingPanel, updateCanCreatePublic, updateModalInfoWhyCantCreate } from "@root/uiTools/actions"; +import { + cleanQuestions, + createResult, + setQuestions, +} from "@root/questions/actions"; +import { + updateOpenBranchingPanel, + updateCanCreatePublic, + updateModalInfoWhyCantCreate, + setShowConfirmLeaveModal, +} from "@root/uiTools/actions"; import { BranchingPanel } from "../Questions/BranchingPanel"; import { useQuestionsStore } from "@root/questions/store"; import { useQuizes } from "@root/quizes/hooks"; @@ -46,41 +61,55 @@ import { clearAuthToken } from "@frontend/kitui"; import { logout } from "@api/auth"; import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; import { ModalInfoWhyCantCreate } from "./ModalInfoWhyCantCreate"; -import { checkQuestionHint } from "@utils/checkQuestionHint"; -import { type } from "os"; +import { ConfirmLeaveModal } from "./ConfirmLeaveModal"; +import { checkQuestionHint } from "@utils/checkQuestionHint"; export default function EditPage() { const quiz = useCurrentQuiz(); const { editQuizId } = useQuizStore(); const { questions } = useQuestionsStore(); - console.log(quiz) + console.log(quiz); useEffect(() => { const getData = async () => { const quizes = await quizApi.getList(); setQuizes(quizes); - const questions = await questionApi.getList({ quiz_id: editQuizId }); - setQuestions(questions); + if (editQuizId) { + const questions = await questionApi.getList({ quiz_id: editQuizId }); + setQuestions(questions); + } - //Всегда должен существовать хоть 1 резулт - "line" - console.log(questions) - - if (!questions?.find(q=>q.type === "result" && q.content.includes(':"line"') || q.content.includes(":'line'"))) createResult(quiz?.backendId, "line") - + //Всегда должен существовать хоть 1 резулт - "line" + console.log(questions); + + if ( + !questions?.find( + (q) => + (q.type === "result" && q.content.includes(':"line"')) || + q.content.includes(":'line'") + ) + ) + createResult(quiz?.backendId, "line"); }; getData(); }, []); - const { openBranchingPanel, whyCantCreatePublic, canCreatePublic } = useUiTools(); + const { + openBranchingPanel, + whyCantCreatePublic, + canCreatePublic, + showConfirmLeaveModal, + } = useUiTools(); const theme = useTheme(); const navigate = useNavigate(); const currentStep = useQuizStore((state) => state.currentStep); const isTablet = useMediaQuery(theme.breakpoints.down(1000)); const isMobile = useMediaQuery(theme.breakpoints.down(660)); const [mobileSidebar, setMobileSidebar] = useState(false); + const [nextStep, setNextStep] = useState(0); const quizConfig = quiz?.config; - const disableTest = quiz === undefined ? true : (quiz.config.type === null) + const disableTest = quiz === undefined ? true : quiz.config.type === null; useEffect(() => { if (editQuizId === null) navigate("/list"); @@ -95,27 +124,26 @@ export default function EditPage() { [] ); - const updateQuestionHint = useDebouncedCallback((questions: AnyTypedQuizQuestion[]) => { - - const problems = checkQuestionHint(questions) - useUiTools.setState({ whyCantCreatePublic: problems }) - if (Object.keys(problems).length > 0) { - updateQuiz(quiz?.id, (state) => { state.status = "stop" }) - updateCanCreatePublic(false) - } else { - updateCanCreatePublic(true) - } - - - }, 600); - + const updateQuestionHint = useDebouncedCallback( + (questions: AnyTypedQuizQuestion[]) => { + const problems = checkQuestionHint(questions); + useUiTools.setState({ whyCantCreatePublic: problems }); + if (Object.keys(problems).length > 0) { + updateQuiz(quiz?.id, (state) => { + state.status = "stop"; + }); + updateCanCreatePublic(false); + } else { + updateCanCreatePublic(true); + } + }, + 600 + ); useEffect(() => { - updateQuestionHint(questions) + updateQuestionHint(questions); }, [questions]); - - async function handleLogoutClick() { const [, logoutError] = await logout(); @@ -127,7 +155,11 @@ export default function EditPage() { clearUserData(); navigate("/"); } - console.log(questions) + + const followNewPage = () => { + setShowConfirmLeaveModal(false); + setCurrentStep(nextStep); + }; if (!quizConfig) return <>; return ( @@ -258,7 +290,7 @@ export default function EditPage() { display: isMobile ? "block" : "flex", }} > - {isMobile ? : } + {isMobile ? : } - {[1].includes(currentStep) && !openBranchingPanel && quizConfig.type !== "form" && ( - - updateOpenBranchingPanel(e.target.checked)} + {[1].includes(currentStep) && + !openBranchingPanel && + quizConfig.type !== "form" && ( + - - Логика ветвления + > + updateOpenBranchingPanel(e.target.checked)} + sx={{ + width: 50, + height: 30, + padding: 0, + "& .MuiSwitch-switchBase": { + padding: 0, + margin: "2px", + transitionDuration: "300ms", + "&.Mui-checked": { + transform: "translateX(20px)", + color: theme.palette.brightPurple.main, + "& + .MuiSwitch-track": { + backgroundColor: "#E8DCF9", + opacity: 1, + border: 0, + }, + "&.Mui-disabled + .MuiSwitch-track": { opacity: 0.5 }, + }, + "&.Mui-disabled .MuiSwitch-thumb": { + color: + theme.palette.mode === "light" + ? theme.palette.grey[100] + : theme.palette.grey[600], + }, + "&.Mui-disabled + .MuiSwitch-track": { + opacity: theme.palette.mode === "light" ? 0.7 : 0.3, + }, + }, + "& .MuiSwitch-thumb": { + boxSizing: "border-box", + width: 25, + height: 25, + }, + "& .MuiSwitch-track": { + borderRadius: 13, + backgroundColor: + theme.palette.mode === "light" ? "#E9E9EA" : "#39393D", + opacity: 1, + transition: theme.transitions.create( + ["background-color"], + { + duration: 500, + } + ), + }, + }} + /> + + + Логика ветвления + + - - )} + )} - {!canCreatePublic && quiz.config.type !== "form" ? + {!canCreatePublic && quiz.config.type !== "form" ? ( - : - + ) : ( + - {quiz?.status === "start" && https://hbpn.link/{quiz.qid} - } + {quiz?.status === "start" && ( + + https://hbpn.link/{quiz.qid} + + )} - + + setShowConfirmLeaveModal(false)} + /> ); } diff --git a/src/stores/uiTools/actions.ts b/src/stores/uiTools/actions.ts index cd6da193..29b63dc5 100644 --- a/src/stores/uiTools/actions.ts +++ b/src/stores/uiTools/actions.ts @@ -38,3 +38,4 @@ export const updateCanCreatePublic = (can: boolean) => useUiTools.setState({ can export const updateModalInfoWhyCantCreate = (can: boolean) => useUiTools.setState({ openModalInfoWhyCantCreate: can }); export const updateDeleteId = (deleteNodeId: string | null = null) => useUiTools.setState({ deleteNodeId }); +export const setShowConfirmLeaveModal = (showConfirmLeaveModal: boolean) => useUiTools.setState({ showConfirmLeaveModal }); diff --git a/src/stores/uiTools/store.ts b/src/stores/uiTools/store.ts index 52803348..c7ddc9a5 100644 --- a/src/stores/uiTools/store.ts +++ b/src/stores/uiTools/store.ts @@ -10,8 +10,10 @@ export type UiTools = { canCreatePublic: boolean; whyCantCreatePublic: Record//ид вопроса и список претензий к нему openModalInfoWhyCantCreate: boolean; -deleteNodeId: string | null; + deleteNodeId: string | null; + showConfirmLeaveModal: boolean; }; + export type WhyCantCreatePublic = { name: string; problems: string[] @@ -27,7 +29,8 @@ const initialState: UiTools = { canCreatePublic: false, whyCantCreatePublic: {}, openModalInfoWhyCantCreate: false, -deleteNodeId: null, + deleteNodeId: null, + showConfirmLeaveModal: false, }; export const useUiTools = create()( diff --git a/src/ui_kit/Sidebar.tsx b/src/ui_kit/Sidebar.tsx index 5c858ae5..9dd6dfe3 100755 --- a/src/ui_kit/Sidebar.tsx +++ b/src/ui_kit/Sidebar.tsx @@ -4,118 +4,132 @@ import PencilCircleIcon from "@icons/PencilCircleIcon"; import PuzzlePieceIcon from "@icons/PuzzlePieceIcon"; import TagIcon from "@icons/TagIcon"; import { quizSetupSteps } from "@model/quizSettings"; -import { - Box, - IconButton, - List, - Typography, - useTheme -} from "@mui/material"; +import { Box, IconButton, List, Typography, useTheme } from "@mui/material"; import { setCurrentStep } from "@root/quizes/actions"; import { useQuizStore } from "@root/quizes/store"; import { useState } from "react"; import MenuItem from "./MenuItem"; -import {useCurrentQuiz} from "@root/quizes/hooks"; - +import { useCurrentQuiz } from "@root/quizes/hooks"; +import { setShowConfirmLeaveModal } from "@root/uiTools/actions"; const quizSettingsMenuItems = [ - [TagIcon, "Дополнения"], - [PencilCircleIcon, "Дизайн"], - [PuzzlePieceIcon, "Интеграции"], - [GearIcon, "Настройки"], + [TagIcon, "Дополнения"], + [PencilCircleIcon, "Дизайн"], + [PuzzlePieceIcon, "Интеграции"], + [GearIcon, "Настройки"], ] as const; -export default function Sidebar() { - const theme = useTheme(); - const [isMenuCollapsed, setIsMenuCollapsed] = useState(false); - const currentStep = useQuizStore(state => state.currentStep); - const quiz = useCurrentQuiz(); +type SidebarProps = { + setNextStep: (step: number) => void; +}; +export default function Sidebar({ setNextStep }: SidebarProps) { + const theme = useTheme(); + const [isMenuCollapsed, setIsMenuCollapsed] = useState(false); + const currentStep = useQuizStore((state) => state.currentStep); + const quiz = useCurrentQuiz(); - const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev); + const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev); - return ( - { + if (currentStep === 2) { + setNextStep(index); + setShowConfirmLeaveModal(true); + + return; + } + + setCurrentStep(index); + }; + + return ( + + + {!isMenuCollapsed && ( + + Создание квиза + + )} + - - {!isMenuCollapsed && ( - - Создание квиза - - )} - - - - - - {quizSetupSteps.map((menuItem, index) => { - const Icon = menuItem.sidebarIcon; - - return ( - setCurrentStep(index)} - key={index} - text={menuItem.sidebarText} - isCollapsed={isMenuCollapsed} - isActive={currentStep === index} - disabled={index===0 ? false : quiz===undefined ? true : (quiz?.config.type === null)} - icon={ - - } - /> - ); - })} - - {/* {!isMenuCollapsed && ( + + + + + {quizSetupSteps.map((menuItem, index) => { + const Icon = menuItem.sidebarIcon; + + return ( + changePage(index)} + key={index} + text={menuItem.sidebarText} + isCollapsed={isMenuCollapsed} + isActive={currentStep === index} + disabled={ + index === 0 + ? false + : quiz === undefined + ? true + : quiz?.config.type === null + } + icon={ + + } + /> + ); + })} + + {/* {!isMenuCollapsed && ( )} */} - - {/* {quizSettingsMenuItems.map((menuItem, index) => { + + {/* {quizSettingsMenuItems.map((menuItem, index) => { const Icon = menuItem[0]; const totalIndex = index + quizSetupSteps.length; const isActive = currentStep === totalIndex + 1; @@ -159,7 +173,7 @@ export default function Sidebar() { /> ); })} */} - - - ); + + + ); }