feat: DesignPage

This commit is contained in:
IlyaDoronin 2023-12-29 16:32:57 +03:00
parent 1d912c70e8
commit 7c00446780
7 changed files with 487 additions and 224 deletions

@ -5,6 +5,7 @@ import "dayjs/locale/ru";
import SigninDialog from "./pages/auth/Signin";
import SignupDialog from "./pages/auth/Signup";
import { ViewPage } from "./pages/ViewPublicationPage";
import { DesignPage } from "./pages/startPage/DesignPage";
import { Route, Routes, useLocation, useNavigate, Navigate } from "react-router-dom";
import "./index.css";
import ContactFormPage from "./pages/ContactFormPage/ContactFormPage";
@ -139,6 +140,7 @@ export default function App() {
<Route path="edit" element={<EditPage />} />
<Route path="crop" element={<ImageCrop />} />
<Route path="/view" element={<ViewPage />} />
<Route path="/design" element={<DesignPage />} />
</Route>
</Routes>
</>

@ -0,0 +1,161 @@
import { quizApi } from "@api/quiz";
import { Box, useMediaQuery, useTheme } from "@mui/material";
import {
resetEditConfig,
setQuizes,
updateQuiz,
setCurrentStep,
} from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useQuizStore } from "@root/quizes/store";
import Sidebar from "@ui_kit/Sidebar";
import { enqueueSnackbar } from "notistack";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useDebouncedCallback } from "use-debounce";
import { SidebarMobile } from "./Sidebar/SidebarMobile";
import {
cleanQuestions,
createResult,
setQuestions,
} from "@root/questions/actions";
import {
updateCanCreatePublic,
updateModalInfoWhyCantCreate,
setShowConfirmLeaveModal,
updateSomeWorkBackend,
} from "@root/uiTools/actions";
import { useQuestionsStore } from "@root/questions/store";
import { questionApi } from "@api/question";
import { useUiTools } from "@root/uiTools/store";
import { clearUserData } from "@root/user";
import { clearAuthToken } from "@frontend/kitui";
import { logout } from "@api/auth";
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
import { ModalInfoWhyCantCreate } from "./ModalInfoWhyCantCreate";
import { ConfirmLeaveModal } from "./ConfirmLeaveModal";
import { checkQuestionHint } from "@utils/checkQuestionHint";
import { Header } from "./Header";
export const DesignPage = () => {
const quiz = useCurrentQuiz();
const { editQuizId } = useQuizStore();
const { questions } = useQuestionsStore();
useEffect(() => {
const getData = async () => {
const quizes = await quizApi.getList();
setQuizes(quizes);
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");
console.log("Я не нашёл линейный резулт и собираюсь создать новый");
}
}
};
getData();
}, []);
const { showConfirmLeaveModal } = useUiTools();
const theme = useTheme();
const navigate = useNavigate();
const currentStep = useQuizStore((state) => state.currentStep);
const isMobile = useMediaQuery(theme.breakpoints.down(660));
const [mobileSidebar, setMobileSidebar] = useState<boolean>(false);
const [nextStep, setNextStep] = useState<number>(0);
const quizConfig = quiz?.config;
const [openBranchingPage, setOpenBranchingPage] = useState<boolean>(false);
useEffect(
() => () => {
resetEditConfig();
cleanQuestions();
updateModalInfoWhyCantCreate(false);
updateSomeWorkBackend(false);
},
[]
);
const updateQuestionHint = useDebouncedCallback(
(questions: AnyTypedQuizQuestion[]) => {
const problems = checkQuestionHint(questions, quiz);
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);
}, [questions]);
async function handleLogoutClick() {
const [, logoutError] = await logout();
if (logoutError) {
return enqueueSnackbar(logoutError);
}
clearAuthToken();
clearUserData();
navigate("/");
}
const followNewPage = () => {
setShowConfirmLeaveModal(false);
setCurrentStep(nextStep);
};
if (!quizConfig) return <></>;
const changePage = (index: number) => {
if (currentStep === 2) {
setNextStep(index);
setShowConfirmLeaveModal(true);
return;
}
setCurrentStep(index);
};
return (
<>
<Header />
<Box sx={{ display: "flex" }}>
{isMobile ? (
<SidebarMobile open={mobileSidebar} changePage={changePage} />
) : (
<Sidebar changePage={changePage} />
)}
<Box>Страница дизайна</Box>
</Box>
<ModalInfoWhyCantCreate />
<ConfirmLeaveModal
open={showConfirmLeaveModal}
follow={followNewPage}
cancel={() => setShowConfirmLeaveModal(false)}
/>
</>
);
};

@ -3,7 +3,6 @@ import { LogoutButton } from "@ui_kit/LogoutButton";
import BackArrowIcon from "@icons/BackArrowIcon";
import { Burger } from "@icons/Burger";
import EyeIcon from "@icons/EyeIcon";
import { PenaLogoIcon } from "@icons/PenaLogoIcon";
import VisibilityIcon from "@mui/icons-material/Visibility";
import {
@ -19,7 +18,6 @@ import {
useTheme,
} from "@mui/material";
import {
decrementCurrentStep,
resetEditConfig,
setQuizes,
updateQuiz,
@ -29,15 +27,12 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
import { useQuizStore } from "@root/quizes/store";
import CustomAvatar from "@ui_kit/Header/Avatar";
import NavMenuItem from "@ui_kit/Header/NavMenuItem";
import PenaLogo from "@ui_kit/PenaLogo";
import Sidebar from "@ui_kit/Sidebar";
import Stepper from "@ui_kit/Stepper";
import SwitchStepPages from "@ui_kit/switchStepPages";
import { isAxiosError } from "axios";
import { enqueueSnackbar } from "notistack";
import React, { useEffect, useLayoutEffect, useState } from "react";
import { useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import useSWR from "swr";
import { useDebouncedCallback } from "use-debounce";
import { SidebarMobile } from "./Sidebar/SidebarMobile";
import {
@ -52,9 +47,8 @@ import {
setShowConfirmLeaveModal,
updateSomeWorkBackend,
} from "@root/uiTools/actions";
import { BranchingPanel } from "../Questions/BranchingPanel";
import { Header } from "./Header";
import { useQuestionsStore } from "@root/questions/store";
import { useQuizes } from "@root/quizes/hooks";
import { questionApi } from "@api/question";
import { useUiTools } from "@root/uiTools/store";
@ -71,14 +65,11 @@ import { toggleQuizPreview } from "@root/quizPreview";
import { LinkSimple } from "@icons/LinkSimple";
import { BackButtonIcon } from "@icons/BackButtonIcon";
let init: () => void;
export default function EditPage() {
const quiz = useCurrentQuiz();
const { editQuizId } = useQuizStore();
const { questions } = useQuestionsStore();
console.log("quiz ", quiz);
console.log(questions);
useEffect(() => {
const getData = async () => {
const quizes = await quizApi.getList();
@ -165,18 +156,6 @@ export default function EditPage() {
updateQuestionHint(questions);
}, [questions]);
async function handleLogoutClick() {
const [, logoutError] = await logout();
if (logoutError) {
return enqueueSnackbar(logoutError);
}
clearAuthToken();
clearUserData();
navigate("/");
}
const followNewPage = () => {
setShowConfirmLeaveModal(false);
setCurrentStep(nextStep);
@ -221,133 +200,7 @@ export default function EditPage() {
return (
<>
{/*хедер*/}
<Container
component="nav"
maxWidth={false}
disableGutters
sx={{
px: "16px",
display: "flex",
height: isMobile ? "51px" : "80px",
alignItems: "center",
bgcolor: isMobile ? "#333647" : "white",
borderBottom: "1px solid #E3E3E3",
zIndex: theme.zIndex.drawer + 1,
}}
>
<Link to="/" style={{ display: "flex" }}>
{isMobile ? <Logotip width={100} /> : <Logotip width={124} />}
</Link>
<Box
sx={{
display: isMobile ? "none" : "flex",
alignItems: "center",
ml: "37px",
}}
>
<Link to="/list">
<IconButton sx={{ p: "6px" }}>
<BackArrowIcon />
</IconButton>
</Link>
<FormControl fullWidth variant="standard">
<TextField
value={quiz.name}
onChange={(e) =>
updateQuiz(quiz.id, (quiz) => {
quiz.name = e.target.value;
})
}
fullWidth
id="project-name"
placeholder="Название проекта окно"
sx={{
width: "270px",
"& .MuiInputBase-root": {
height: "34px",
borderRadius: "8px",
p: 0,
},
}}
inputProps={{
sx: {
height: "20px",
borderRadius: "8px",
fontSize: "16px",
lineHeight: "20px",
p: "7px",
color: "black",
"&::placeholder": {
opacity: 1,
},
},
}}
/>
</FormControl>
</Box>
{isTablet ? (
<Box
sx={{
display: "flex",
ml: "auto",
}}
>
{isMobile ? (
<Burger
onClick={() => setMobileSidebar(!mobileSidebar)}
style={{ fontSize: "30px", color: "white", cursor: "pointer" }}
/>
) : (
<Box>
<CustomAvatar
sx={{
ml: "11px",
backgroundColor: theme.palette.orange.main,
height: "36px",
width: "36px",
}}
/>
</Box>
)}
</Box>
) : (
<>
<Box
sx={{
display: "flex",
gap: "30px",
overflow: "hidden",
ml: "20px",
}}
>
<NavMenuItem text="Редактировать" isActive />
<NavMenuItem text="Заявки" />
<NavMenuItem text="Аналитика" />
<NavMenuItem text="История" />
<NavMenuItem text="Помощь" />
</Box>
<Box
sx={{
display: "flex",
ml: "auto",
gap: "15px",
}}
>
<CustomAvatar
sx={{
ml: "11px",
backgroundColor: theme.palette.orange.main,
height: "36px",
width: "36px",
}}
/>
<LogoutButton onClick={handleLogoutClick} />
</Box>
</>
)}
</Container>
<Header />
<Box
sx={{
display: isMobile ? "block" : "flex",

@ -0,0 +1,175 @@
import { LogoutButton } from "@ui_kit/LogoutButton";
import BackArrowIcon from "@icons/BackArrowIcon";
import { Burger } from "@icons/Burger";
import {
Box,
Container,
FormControl,
IconButton,
TextField,
useMediaQuery,
useTheme,
} from "@mui/material";
import { updateQuiz } from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import CustomAvatar from "@ui_kit/Header/Avatar";
import NavMenuItem from "@ui_kit/Header/NavMenuItem";
import { enqueueSnackbar } from "notistack";
import { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import Logotip from "../Landing/images/icons/QuizLogo";
import { clearUserData } from "@root/user";
import { clearAuthToken } from "@frontend/kitui";
import { logout } from "@api/auth";
export const Header = () => {
const quiz = useCurrentQuiz();
const theme = useTheme();
const navigate = useNavigate();
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isMobile = useMediaQuery(theme.breakpoints.down(660));
const [mobileSidebar, setMobileSidebar] = useState<boolean>(false);
async function handleLogoutClick() {
const [, logoutError] = await logout();
if (logoutError) {
return enqueueSnackbar(logoutError);
}
clearAuthToken();
clearUserData();
navigate("/");
}
return (
<Container
component="nav"
maxWidth={false}
disableGutters
sx={{
px: "16px",
display: "flex",
height: isMobile ? "51px" : "80px",
alignItems: "center",
bgcolor: isMobile ? "#333647" : "white",
borderBottom: "1px solid #E3E3E3",
zIndex: theme.zIndex.drawer + 1,
}}
>
<Link to="/" style={{ display: "flex" }}>
{isMobile ? <Logotip width={100} /> : <Logotip width={124} />}
</Link>
<Box
sx={{
display: isMobile ? "none" : "flex",
alignItems: "center",
ml: "37px",
}}
>
<Link to="/list">
<IconButton sx={{ p: "6px" }}>
<BackArrowIcon />
</IconButton>
</Link>
<FormControl fullWidth variant="standard">
<TextField
value={quiz.name}
onChange={(e) =>
updateQuiz(quiz.id, (quiz) => {
quiz.name = e.target.value;
})
}
fullWidth
id="project-name"
placeholder="Название проекта окно"
sx={{
width: "270px",
"& .MuiInputBase-root": {
height: "34px",
borderRadius: "8px",
p: 0,
},
}}
inputProps={{
sx: {
height: "20px",
borderRadius: "8px",
fontSize: "16px",
lineHeight: "20px",
p: "7px",
color: "black",
"&::placeholder": {
opacity: 1,
},
},
}}
/>
</FormControl>
</Box>
{isTablet ? (
<Box
sx={{
display: "flex",
ml: "auto",
}}
>
{isMobile ? (
<Burger
onClick={() => setMobileSidebar(!mobileSidebar)}
style={{ fontSize: "30px", color: "white", cursor: "pointer" }}
/>
) : (
<Box>
<CustomAvatar
sx={{
ml: "11px",
backgroundColor: theme.palette.orange.main,
height: "36px",
width: "36px",
}}
/>
</Box>
)}
</Box>
) : (
<>
<Box
sx={{
display: "flex",
gap: "30px",
overflow: "hidden",
ml: "20px",
}}
>
<NavMenuItem text="Редактировать" isActive />
<NavMenuItem text="Заявки" />
<NavMenuItem text="Аналитика" />
<NavMenuItem text="История" />
<NavMenuItem text="Помощь" />
</Box>
<Box
sx={{
display: "flex",
ml: "auto",
gap: "15px",
}}
>
<CustomAvatar
sx={{
ml: "11px",
backgroundColor: theme.palette.orange.main,
height: "36px",
width: "36px",
}}
/>
<LogoutButton onClick={handleLogoutClick} />
</Box>
</>
)}
</Container>
);
};

@ -1,7 +1,10 @@
import BackArrowIcon from "@icons/BackArrowIcon";
import { People } from "@mui/icons-material";
import { FC, useState } from "react";
import { Box, Typography } from "@mui/material";
import { FC } from "react";
import { People } from "@mui/icons-material";
import { SidebarModal } from "./SidebarModal";
import BackArrowIcon from "@icons/BackArrowIcon";
import { ChartLineUp } from "./icons/ChartLineUp";
import { ReturnTime } from "./icons/ReturnTime";
import { Question } from "./icons/Question";
@ -26,90 +29,99 @@ const quizSetupSteps = [
{ sidebarIcon: <Question style={{ color: "#974BFA", fontSize: "24px" }} /> },
] as const;
export const SidebarMobile: FC<Iprops> = ({ open, changePage }) => (
<Box
sx={{
display: open ? "block" : "none",
minHeight: "134px",
padding: "20px 16px 16px 16px",
background: "#333647",
borderTop: "1px solid #9A9AAF",
borderBottomLeftRadius: "8px",
borderBottomRightRadius: "8px",
transitionDuration: "200ms",
}}
>
<Box sx={{ display: "flex", alignItems: "center", position: "relative" }}>
<BackArrowIcon color="white" />
export const SidebarMobile: FC<Iprops> = ({ open, changePage }) => {
const [openModal, setOpenModal] = useState<boolean>(false);
<Box sx={{ ml: "15px", display: "flex", alignItems: "end" }}>
<Box>
<Typography sx={{ fontSize: "12px", color: "#9A9AAF" }}>
Название
</Typography>
<Typography
sx={{ color: "#FFF", fontSize: "18px", fontWeight: "500" }}
>
Название проекта
</Typography>
</Box>
<Pencil
style={{
position: "absolute",
right: "0",
color: "white",
fontSize: "24px",
}}
/>
</Box>
</Box>
const onClose = () => {
setOpenModal(false);
};
return (
<Box
sx={{
width: "100%",
justifyContent: "center",
display: "flex",
marginTop: "20px",
flexWrap: "wrap",
gap: "5px",
display: open ? "block" : "none",
minHeight: "134px",
padding: "20px 16px 16px 16px",
background: "#333647",
borderTop: "1px solid #9A9AAF",
borderBottomLeftRadius: "8px",
borderBottomRightRadius: "8px",
transitionDuration: "200ms",
}}
>
{quizSetupSteps.map(({ sidebarIcon }, index) => (
<Box sx={{ display: "flex", alignItems: "center", position: "relative" }}>
<BackArrowIcon color="white" />
<Box sx={{ ml: "15px", display: "flex", alignItems: "end" }}>
<Box>
<Typography sx={{ fontSize: "12px", color: "#9A9AAF" }}>
Название
</Typography>
<Typography
sx={{ color: "#FFF", fontSize: "18px", fontWeight: "500" }}
>
Название проекта
</Typography>
</Box>
<Pencil
style={{
position: "absolute",
right: "0",
color: "white",
fontSize: "24px",
}}
/>
</Box>
</Box>
<Box
sx={{
width: "100%",
justifyContent: "center",
display: "flex",
marginTop: "20px",
flexWrap: "wrap",
gap: "5px",
}}
>
{quizSetupSteps.map(({ sidebarIcon }, index) => (
<Box
onClick={() => changePage(index)}
sx={{
cursor: "pointer",
width: "44px",
height: "44px",
background: "#262835",
display: "flex",
justifyContent: "center",
alignItems: "center",
borderRadius: "8px",
}}
>
{sidebarIcon}
</Box>
))}
<Box
onClick={() => changePage(index)}
onClick={() => setOpenModal(true)}
sx={{
cursor: "pointer",
width: "44px",
px: "10px",
width: "70px",
height: "44px",
background: "#262835",
display: "flex",
justifyContent: "center",
justifyContent: "space-between",
alignItems: "center",
borderRadius: "8px",
border: "1px solid #FFFFFF66",
marginLeft: "28px",
}}
>
{sidebarIcon}
<Settings
style={{ color: "#974BFA", fontSize: "24px", marginLeft: "10px" }}
/>
<ArrowDown style={{ color: "#F2F3F7" }} />
</Box>
))}
<Box
sx={{
px: "10px",
width: "70px",
height: "44px",
background: "#262835",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
borderRadius: "8px",
border: "1px solid #FFFFFF66",
marginLeft: "28px",
}}
>
<Settings
style={{ color: "#974BFA", fontSize: "24px", marginLeft: "10px" }}
/>
<ArrowDown style={{ color: "#F2F3F7" }} />
</Box>
<SidebarModal open={openModal} onClose={onClose} />
</Box>
</Box>
);
);
};

@ -0,0 +1,49 @@
import { Box, Button, Modal, Typography } from "@mui/material";
type SidebarModalProps = {
open: boolean;
onClose: () => void;
};
export const SidebarModal = ({ open, onClose }: SidebarModalProps) => {
return (
<Modal open={open} onClose={onClose}>
<Box
sx={{
outline: "none",
position: "absolute",
overflow: "hidden",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
maxWidth: "620px",
width: "100%",
bgcolor: "background.paper",
borderRadius: "12px",
boxShadow: 24,
p: 0,
}}
>
<Box
sx={{
boxSizing: "border-box",
background: "#F2F3F7",
height: "70px",
padding: "0 25px",
display: "flex",
alignItems: "center",
}}
>
меню
</Box>
<Box
sx={{
display: "flex",
justifyContent: "end",
gap: "10px",
margin: "20px",
}}
></Box>
</Box>
</Modal>
);
};

@ -9,6 +9,7 @@ import { useQuizStore } from "@root/quizes/store";
import { useState } from "react";
import MenuItem from "./MenuItem";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useLocation, useNavigate } from "react-router-dom";
const quizSettingsMenuItems = [
[TagIcon, "Дополнения"],
@ -26,6 +27,16 @@ export default function Sidebar({ changePage }: SidebarProps) {
const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
const currentStep = useQuizStore((state) => state.currentStep);
const quiz = useCurrentQuiz();
const { pathname } = useLocation();
const navigate = useNavigate();
const changeMenuItem = (index: number) => {
if (!pathname.startsWith("/edit")) {
navigate("/edit");
}
changePage(index);
};
const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev);
@ -87,7 +98,7 @@ export default function Sidebar({ changePage }: SidebarProps) {
return (
<MenuItem
onClick={() => changePage(index)}
onClick={() => changeMenuItem(index)}
key={index}
text={menuItem.sidebarText}
isCollapsed={isMenuCollapsed}