diff --git a/cypress/e2e/image/Bunner.png b/cypress/e2e/image/Bunner.png new file mode 100644 index 00000000..bd06b317 Binary files /dev/null and b/cypress/e2e/image/Bunner.png differ diff --git a/cypress/e2e/quizPublish.cy.ts b/cypress/e2e/quizPublish.cy.ts new file mode 100644 index 00000000..06203589 --- /dev/null +++ b/cypress/e2e/quizPublish.cy.ts @@ -0,0 +1,296 @@ +import "cypress-file-upload"; + +describe("Форма Входа", () => { + beforeEach(() => { + cy.visit("http://localhost:3000"); + cy.wait(1000); + cy.contains("Регистрация / Войти").click(); + const login = "valid_user@exammple.com"; + const password = "valid_password"; + + cy.get("#email").type(login); + cy.get("#password").type(password); + cy.get('button[type="submit"]').click(); + }); + + it("Тестирование создания, публикации и удаления опросника с проверкой обязательных вопросов", () => { + cy.get('[data-cy="create-quiz"]').click(); + cy.wait(1000); + cy.get('button[data-cy="create-quiz-card"]').eq(0).click(); + cy.wait(1000); + cy.get('button[data-cy="select-quiz-layout-standard"]').click(); + + cy.get('input[type="checkbox"]').click(); + + cy.get('[data-cy="setup-questions"]').click(); + cy.contains("button", "Варианты с картинками").click(); + + cy.contains("label", "Необязательный вопрос").click(); + + cy.get('[data-cy="quiz-variant-question-answer"]').eq(0).type("1").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(1).should("have.value", "").type("2").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(2).should("have.value", "").type("3").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(3).should("have.value", "").type("4").type("{enter}"); + + cy.visit("http://localhost:3000/view"); + cy.wait(500); + + cy.contains("Далее →").should("be.disabled"); + cy.contains("div", "1").click(); + + cy.wait(500); + cy.contains("Далее →").should("not.be.disabled"); + + cy.visit("http://localhost:3000/view"); + cy.visit("http://localhost:3000/edit"); + + cy.wait(500); + cy.get('[data-cy="delete-question"]').click(); + cy.wait(5000); + cy.get('[data-cy="create-question"]').click(); + + //Варианты ответов + + + cy.contains("button", "Варианты ответов").click(); + cy.contains("label", "Необязательный вопрос").click(); + + cy.get('[data-cy="quiz-variant-question-answer"]').eq(0).type("1").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(1).should("have.value", "").type("2").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(2).should("have.value", "").type("3").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(3).should("have.value", "").type("4").type("{enter}"); + + cy.visit("http://localhost:3000/view"); + cy.wait(500); + + cy.contains("Далее →").should("be.disabled"); + cy.contains("label", "1").click(); + + cy.wait(500); + cy.contains("Далее →").should("not.be.disabled"); + + cy.visit("http://localhost:3000/view"); + cy.visit("http://localhost:3000/edit"); + + cy.wait(500); + cy.get('[data-cy="delete-question"]').click(); + + cy.wait(5000); + + cy.get('[data-cy="create-question"]').click(); + + // Варианты и картинка + + + cy.contains("button", "Варианты и картинка").click(); + cy.contains("label", "Необязательный вопрос").click(); + + cy.get('[data-cy="quiz-variant-question-answer"]').eq(0).type("1").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(1).should("have.value", "").type("2").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(2).should("have.value", "").type("3").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(3).should("have.value", "").type("4").type("{enter}"); + + cy.visit("http://localhost:3000/view"); + cy.wait(500); + + cy.contains("Далее →").should("be.disabled"); + cy.contains("div", "1").click(); + + cy.wait(500); + cy.contains("Далее →").should("not.be.disabled"); + + cy.visit("http://localhost:3000/view"); + cy.visit("http://localhost:3000/edit"); + + cy.wait(500); + cy.get('[data-cy="delete-question"]').click(); + + cy.wait(5000); + cy.get('[data-cy="create-question"]').click(); + + //Эмоджи + + + cy.contains("button", "Эмоджи").click(); + cy.contains("label", "Необязательный вопрос").click(); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(0).type("1").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(1).should("have.value", "").type("2").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(2).should("have.value", "").type("3").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(3).should("have.value", "").type("4").type("{enter}"); + + cy.visit("http://localhost:3000/view"); + cy.wait(500); + + cy.contains("Далее →").should("be.disabled"); + cy.contains("div", "1").click(); + + cy.wait(500); + cy.contains("Далее →").should("not.be.disabled"); + + cy.visit("http://localhost:3000/view"); + cy.visit("http://localhost:3000/edit"); + + cy.wait(500); + cy.get('[data-cy="delete-question"]').click(); + + cy.wait(5000); + cy.get('[data-cy="create-question"]').click(); + + //Своё поле для ввода + + + cy.contains("button", "Своё поле для ввода").click(); + cy.contains("label", "Необязательный вопрос").click(); + + cy.get('input[placeholder="Пример ответа"]').eq(0).type("1").type("{enter}"); + + cy.visit("http://localhost:3000/view"); + cy.wait(500); + + cy.contains("Далее →").should("be.disabled"); + cy.get('input[type="text"]').type("email@invalid.com"); + + cy.wait(500); + cy.contains("Далее →").should("not.be.disabled"); + + cy.visit("http://localhost:3000/view"); + cy.visit("http://localhost:3000/edit"); + + cy.wait(500); + cy.get('[data-cy="delete-question"]').click(); + + cy.wait(5000); + cy.get('[data-cy="create-question"]').click(); + + //Выпадающий список + + + cy.contains("button", "Выпадающий список").click(); + cy.contains("label", "Необязательный вопрос").click(); + + cy.get('[data-cy="quiz-variant-question-answer"]').eq(0).type("1").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(1).should("have.value", "").type("2").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(2).should("have.value", "").type("3").type("{enter}"); + cy.get('[data-cy="quiz-variant-question-answer"]').eq(3).should("have.value", "").type("4").type("{enter}"); + + cy.visit("http://localhost:3000/view"); + cy.wait(500); + + cy.contains("Далее →").should("be.disabled"); + cy.get("#display-select").click(); + cy.get("li").eq(0).click(); + + cy.wait(500); + cy.contains("Далее →").should("not.be.disabled"); + + cy.visit("http://localhost:3000/view"); + cy.visit("http://localhost:3000/edit"); + + cy.wait(500); + cy.get('[data-cy="delete-question"]').click(); + + cy.wait(5000); + cy.get('[data-cy="create-question"]').click(); + + //Дата + + + cy.contains("button", "Дата").click(); + cy.contains("label", "Необязательный вопрос").click(); + + cy.visit("http://localhost:3000/view"); + cy.wait(500); + + cy.contains("Далее →").should("be.disabled"); + cy.get('button[data-cy="open-datepicker"]').click(); + + cy.wait(500); + + cy.get('button[role="gridcell"]').eq(16).click(); + + cy.wait(500); + cy.contains("Далее →").should("not.be.disabled"); + + cy.visit("http://localhost:3000/view"); + cy.visit("http://localhost:3000/edit"); + + cy.wait(500); + cy.get('[data-cy="delete-question"]').click(); + + cy.wait(5000); + cy.get('[data-cy="create-question"]').click(); + + //Ползунок + + + cy.contains("button", "Ползунок").click(); + cy.contains("label", "Необязательный вопрос").click(); + + cy.visit("http://localhost:3000/view"); + cy.wait(500); + + cy.contains("Далее →").should("be.disabled"); + cy.get('input[aria-invalid="false"][id=":r0:"][placeholder="0"]').type("10"); + + cy.wait(500); + cy.contains("Далее →").should("not.be.disabled"); + + cy.visit("http://localhost:3000/view"); + cy.visit("http://localhost:3000/edit"); + + cy.wait(500); + cy.get('[data-cy="delete-question"]').click(); + + cy.wait(5000); + cy.get('[data-cy="create-question"]').click(); + + //Загрузка файла + + + cy.contains("button", "Загрузка файла").click(); + cy.contains("label", "Необязательный вопрос").click(); + + cy.visit("http://localhost:3000/view"); + cy.wait(500); + + cy.contains("Далее →").should("be.disabled"); + cy.get('label.MuiButtonBase-root input[type="file"]').attachFile("./image/Bunner.png"); + + cy.wait(500); + cy.contains("Далее →").should("not.be.disabled"); + + cy.visit("http://localhost:3000/view"); + cy.visit("http://localhost:3000/edit"); + + cy.wait(500); + cy.get('[data-cy="delete-question"]').click(); + + cy.wait(5000); + cy.get('[data-cy="create-question"]').click(); + + // Рейтинг + + cy.contains("button", "Рейтинг").click(); + cy.contains("label", "Необязательный вопрос").click(); + + cy.visit("http://localhost:3000/view"); + cy.wait(500); + + cy.contains("Далее →").should("be.disabled"); + cy.contains("label", "4 Stars").click(); + + cy.wait(500); + cy.contains("Далее →").should("not.be.disabled"); + + cy.visit("http://localhost:3000/view"); + cy.visit("http://localhost:3000/edit"); + + cy.wait(500); + cy.get('[data-cy="delete-question"]').click(); + + cy.wait(5000); + cy.get('[data-cy="create-question"]').click(); + + // Удаления Квиза + + cy.visit("http://localhost:3000/list"); + cy.wait(500); + cy.get('[data-cy="delete-quiz"]').each(($button) => { + cy.wrap($button).click(); + cy.wait(500); + cy.contains("button", "Удалить").click(); + }); + }); +}); diff --git a/package.json b/package.json index d27ff52f..3208fa17 100755 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@types/react-dnd": "^3.0.2", "@types/react-dom": "^18.0.0", "axios": "^1.5.1", + "cypress-file-upload": "^5.0.8", "cytoscape": "^3.26.0", "cytoscape-popper": "^2.0.0", "dayjs": "^1.11.10", @@ -78,6 +79,6 @@ "@types/react-beautiful-dnd": "^13.1.4", "@types/react-cytoscapejs": "^1.2.4", "craco-alias": "^3.0.1", - "cypress": "^13.4.0" + "cypress": "^13.6.1" } } diff --git a/src/assets/icons/ArrowLeftSP.tsx b/src/assets/icons/ArrowLeftSP.tsx new file mode 100644 index 00000000..1ba692e1 --- /dev/null +++ b/src/assets/icons/ArrowLeftSP.tsx @@ -0,0 +1,27 @@ +import {Box, SxProps, Theme, useTheme} from "@mui/material"; + + +interface Props { + right: boolean +} + +export default function ArrowLeftSP({right} : Props) { + const theme = useTheme(); + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/CropIcon.tsx b/src/assets/icons/CropIcon.tsx index 520e90d9..16b098a7 100644 --- a/src/assets/icons/CropIcon.tsx +++ b/src/assets/icons/CropIcon.tsx @@ -1,40 +1,10 @@ import { FC, SVGProps } from "react"; -export const CropIcon: FC = () => ( - - - - - +export const CropIcon: FC> = (props) => ( + + + + + ); diff --git a/src/model/questionTypes/shared.ts b/src/model/questionTypes/shared.ts index 2d95ac3b..6809b0b8 100644 --- a/src/model/questionTypes/shared.ts +++ b/src/model/questionTypes/shared.ts @@ -96,6 +96,8 @@ export type AnyTypedQuizQuestion = | QuizQuestionRating | QuizQuestionResult; + + type FilterQuestionsWithVariants = T extends { content: { variants: QuestionVariant[]; }; } ? T : never; diff --git a/src/model/quizSettings.ts b/src/model/quizSettings.ts index 7be9e199..d11d871e 100644 --- a/src/model/quizSettings.ts +++ b/src/model/quizSettings.ts @@ -40,7 +40,7 @@ export interface QuizConfig { theme: string, reply: string, replname: string, - } + } startpage: { description: string; button: string; @@ -58,6 +58,16 @@ export interface QuizConfig { cycle: boolean; }; }; + formContact: { + title: string; + desc: string; + name: FCField; + email: FCField; + phone: FCField; + text: FCField; + address: FCField; + button: string + }; info: { phonenumber: string; clickable: boolean; @@ -68,6 +78,14 @@ export interface QuizConfig { meta: string; } +type FCField = { + text: string + innerText: string + key: string + required: boolean + used: boolean +} + export const defaultQuizConfig: QuizConfig = { type: null, noStartPage: false, @@ -81,7 +99,7 @@ export const defaultQuizConfig: QuizConfig = { theme: "", reply: "", replname: "", - }, + }, startpage: { description: "", button: "", @@ -106,5 +124,45 @@ export const defaultQuizConfig: QuizConfig = { site: "", law: "", }, + formContact: { + title: "", + desc: "", + name: { + text: "", + innerText: "", + key: "", + required: false, + used: true + }, + email: { + text: "", + innerText: "", + key: "", + required: false, + used: true + }, + phone: { + text: "", + innerText: "", + key: "", + required: false, + used: true + }, + text: { + text: "", + innerText: "", + key: "", + required: false, + used: false + }, + address: { + text: "", + innerText: "", + key: "", + required: false, + used: false + }, + button: "" + }, meta: "", }; diff --git a/src/pages/ContactFormPage/ContactFormPage.tsx b/src/pages/ContactFormPage/ContactFormPage.tsx index 4c04e997..382bd023 100644 --- a/src/pages/ContactFormPage/ContactFormPage.tsx +++ b/src/pages/ContactFormPage/ContactFormPage.tsx @@ -1,129 +1,122 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import { Box, Button, IconButton, Link, Paper, SxProps, TextField, Theme, Typography, useTheme } from "@mui/material"; -import { incrementCurrentStep } from "@root/quizes/actions"; +import { Box, Button, IconButton, Link, Paper, SxProps, TextField, Theme, Typography, useTheme, Popover, useMediaQuery, Divider } from "@mui/material"; +import { incrementCurrentStep, updateQuiz } from "@root/quizes/actions"; import CustomTextField from "@ui_kit/CustomTextField"; import React from "react"; -import InfoIcon from "../../assets/icons/InfoIcon"; +import Info from "../../assets/icons/Info"; +import Trash from "@icons/trash"; import { OneIcon } from "../../assets/icons/questionsPage/OneIcon"; import AddPlus from "../../assets/icons/questionsPage/addPlus"; import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft"; +import { decrementCurrentStep } from "@root/quizes/actions"; import ButtonSettingForms from "./ButtonSettingForms"; import DrawerNewField from "./DrawerParent"; import WindowMessengers from "./Massengers/WindowMessengers"; import WindowNewField from "./NewField/WindowNewField"; import SwitchContactForm from "./switchContactForm"; +import GearIcon from "@icons/GearIcon"; +import { useQuestionsStore } from "@root/questions/store"; +import { useCurrentQuiz } from "@root/quizes/hooks"; + + +const buttons = [ + { + name: "Имя", + desc: "Дмитрий", + key: "name" + }, + { + name: "Email", + desc: "mail@xample.ru", + key: "email" + }, + { + name: "Номер", + desc: "+7 900 000 00 00", + key: "phone" + }, + { + name: "Фамилия", + desc: "Иванов", + key: "text" + }, + { + name: "Адрес", + desc: "Москва, Лаврушинский пер., 10", + key: "address" + }, +] export default function ContactFormPage() { - const [drawerNewField, setDrawerNewField] = React.useState(false); + const theme = useTheme(); + + const quiz = useCurrentQuiz() + + const isTablet = useMediaQuery(theme.breakpoints.down(1000)) + + const [drawerNewField, setDrawerNewField] = React.useState(""); const [drawerMessenger, setDrawerMessenger] = React.useState(false); - const drawerNewFieldHC = (bool: boolean) => { - setDrawerNewField(bool); + const drawerNewFieldHC = (str: string) => { + setDrawerNewField(str); }; const drawerMessengerHC = (bool: boolean) => { setDrawerMessenger(bool); }; - const theme = useTheme(); return ( - <> - + + + + Как собрать данные посетителя {" "} - + {/* + + */} + - - - - - {" "} - {" "} - - - - Имя* - - E-mail* - - Телефон* - - - - - - drawerMessengerHC(true)} - sx={{ - fontSize: "16px", - lineHeight: "19px", - color: theme.palette.brightPurple.main, - textDecorationColor: theme.palette.brightPurple.main, - textAlign: "left", - }} - > - Добавить мессенджеры - - - - - - - - - - - - {" "} - {" "} - - - - - Будут показаны поля по умолчанию - - - - Добавить мессенджеры - - - + { + !quiz?.config.formContact.name.used && + !quiz?.config.formContact.email.used && + !quiz?.config.formContact.phone.used && + !quiz?.config.formContact.text.used && + !quiz?.config.formContact.address.used ? + + + + : + + + + } + + - - - - - + + + + + + + ); } @@ -142,10 +141,8 @@ interface Props { function ContactFormParent({ outerContainerSx: sx, children }: Props) { const theme = useTheme(); - const [switchState, setSwitchState] = React.useState("setting"); - const SSHC = (data: string) => { - setSwitchState(data); - }; + const isTablet = useMediaQuery(theme.breakpoints.down(1000)) + const quiz = useCurrentQuiz() return ( - + - + { + updateQuiz(quiz.id, (quiz) => { + quiz.config.formContact.title = target.value + }) + }} + value={quiz.config.formContact.title} + placeholder="Заголовок формы" text={""} /> { + updateQuiz(quiz.id, (quiz) => { + quiz.config.formContact.desc = target.value + }) + }} + value={quiz.config.formContact.desc} id="outlined-multiline-static" multiline rows={8} @@ -182,18 +192,216 @@ function ContactFormParent({ outerContainerSx: sx, children }: Props) { borderRadius: "10px", alignItems: "start", color: theme.palette.grey2.main, + color:"black", }, }} /> - - {children} - - - - - + + {children} ); } + + +const SettingField = ({ name, placeholder, type, drawerNewFieldHC }: { name: string, placeholder: string, type: string; drawerNewFieldHC: any }) => { + const theme = useTheme(); + const quiz = useCurrentQuiz() + return ( + <> + {quiz.config.formContact[type].text || name} + + + {quiz.config.formContact[type].innerText || placeholder} + drawerNewFieldHC(type)} + sx={{ + position: "absolute", + right: "0" + }}> + + + + updateQuiz(quiz?.id, (quiz) => { + quiz.config.formContact[type].used = false + })} + sx={{ + width: "48px", + ml: "5px" + }} + > + + + + + + ) +} + +const ButtonsCard = ({ drawerNewFieldHC }: any) => { + const theme = useTheme(); + const quiz = useCurrentQuiz() + + return ( + + + { + buttons.map((contentData) => { + const content = quiz?.config.formContact[contentData.key] + return content.used ? : <> + }) + } + + { + ( + quiz?.config.formContact.name.used && + quiz?.config.formContact.email.used && + quiz?.config.formContact.phone.used && + quiz?.config.formContact.text.used && + quiz?.config.formContact.address.used + ) ? + <> + : + + } + drawerMessengerHC(true)} + sx={{ + fontSize: "16px", + lineHeight: "19px", + color: theme.palette.brightPurple.main, + textDecorationColor: theme.palette.brightPurple.main, + textAlign: "left", + }} + > + Добавить мессенджеры + + + + + ) +} +const EmptyCard = ({ drawerNewFieldHC }: { drawerNewFieldHC: (a: string) => void }) => { + const theme = useTheme(); + const [FC, setFC] = React.useState(false) + const openFC = () => setFC(true) + const closeFC = () => setFC(false) + const popover = React.useRef(null); + + return ( + + + + Будут показаны поля по умолчанию + + + + + + + Если вам не нужно собирать контакты,

отключите форму контактов + + + + + + Добавить мессенджеры + + + + ) +} + +const PseudoButton = () => { + const quiz = useCurrentQuiz() + return ( + { + updateQuiz(quiz.id, (quiz) => { + quiz.config.formContact.button = target.value + }) + }} + value={quiz.config.formContact.button} + sx={{ + heigth: "44px", + width: "190px", + "& .MuiInputBase-root": { + backgroundColor: "#7E2AEA", + borderRadius: "8px", + color: "white", + }, + "& .MuiInputBase-input": { + padding: "10px 20px", + textAlign: "center" + }, + "& .MuiInputBase-input::placeholder": { + color: "white", + opacity: "1" + } + }} + placeholder="Название кнопки" + > + {quiz?.config.formContact.button || "Название кнопки"} + + + ) +} \ No newline at end of file diff --git a/src/pages/ContactFormPage/DrawerParent.tsx b/src/pages/ContactFormPage/DrawerParent.tsx index 34327f35..539e330e 100644 --- a/src/pages/ContactFormPage/DrawerParent.tsx +++ b/src/pages/ContactFormPage/DrawerParent.tsx @@ -7,18 +7,18 @@ import {SxProps, Theme} from "@mui/material"; interface Props { outerContainerSx?: SxProps; children?: React.ReactNode; - isOpen: boolean; - openHC: (arg0: boolean) => void + isOpenDrawer: string; + drawerNewFieldHC: (str: string) => void } -export default function DrawerNewField({outerContainerSx: sx, children, isOpen, openHC }: Props) { +export default function DrawerNewField({outerContainerSx: sx, children, isOpenDrawer: isOpen, drawerNewFieldHC }: Props) { return ( <> openHC(false)} + open={Boolean(isOpen)} + onClose={() => drawerNewFieldHC("")} > void + type: string } -export default function ButtonsNewField ({SSHC, switchState}:Props) { +export default function ButtonsNewField ({SSHC, switchState, type}:Props) { const theme = useTheme(); + const quiz = useCurrentQuiz() + console.log(quiz) + console.log(type) const buttonSetting: {icon: JSX.Element; title: string; value: string} [] =[ {icon: , title: 'Имя', value: 'name'}, {icon: , title: 'Email', value: 'email'}, @@ -31,16 +36,20 @@ export default function ButtonsNewField ({SSHC, switchState}:Props) { }} > {buttonSetting.map( (e,i) => ( + type === e.value || type === "all" ? {SSHC(e.value)}} sx={{backgroundColor: switchState === e.value ? theme.palette.brightPurple.main : 'transparent', - color: switchState === e.value ? '#ffffff' : theme.palette.grey3.main, + color: switchState === e.value && type === "all" ? '#ffffff' : theme.palette.grey3.main, }} > {e.icon} {e.title} + : + <> ))} ) diff --git a/src/pages/ContactFormPage/NewField/NewFieldParent.tsx b/src/pages/ContactFormPage/NewField/NewFieldParent.tsx index 04d47602..c4e3ae0c 100644 --- a/src/pages/ContactFormPage/NewField/NewFieldParent.tsx +++ b/src/pages/ContactFormPage/NewField/NewFieldParent.tsx @@ -1,9 +1,11 @@ import Box from "@mui/material/Box"; -import {FormControl, SxProps, TextField, Theme, Typography} from "@mui/material"; +import { FormControl, SxProps, TextField, Theme, Typography } from "@mui/material"; import CustomTextField from "@ui_kit/CustomTextField"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; import Button from "@mui/material/Button"; import * as React from "react"; +import { useCurrentQuiz } from "@root/quizes/hooks"; +import { updateQuiz } from "@root/quizes/actions"; interface Props { outerContainerSx?: SxProps; @@ -11,51 +13,88 @@ interface Props { defaultValue?: string; placeholderHelp: string; placeholderField: string; + drawerNewFieldHC:(a:string)=>void } -export default function NewFieldParent ({defaultValue, placeholderHelp, placeholderField, outerContainerSx: sx, children}: Props) { - return( - - - Подсказка - +export default function NewFieldParent({ drawerNewFieldHC, defaultValue, placeholderHelp, placeholderField, outerContainerSx: sx, children }: Props) { + const quiz = useCurrentQuiz() + console.log({ defaultValue, placeholderHelp, placeholderField, outerContainerSx: sx, children }) + return ( + + + Подсказка + { + updateQuiz(quiz.id, (quiz) => { + quiz.config.formContact[defaultValue].text = target.value + }) + }} + value={quiz.config.formContact[defaultValue].text} + + placeholder={placeholderHelp} text={''} /> - - Подсказка внутри поля - + + Подсказка внутри поля + { + updateQuiz(quiz.id, (quiz) => { + quiz.config.formContact[defaultValue].innerText = target.value + }) + }} + value={quiz.config.formContact[defaultValue].innerText} + + placeholder={placeholderField} text={''} /> - + + Ключ - - - - + { + updateQuiz(quiz.id, (quiz) => { + quiz.config.formContact[defaultValue].key = target.value + }) + }} + placeholder="text" + sx={{ + "& .css-1d3z3hw-MuiOutlinedInput-notchedOutline": { + border: 'none' + }, + "& .MuiInputBase-root": { + height: "48px", + borderRadius: "10px", + backgroundColor: '#EEE4FC', + }, + }} + + /> + { + console.log("click") + updateQuiz(quiz.id, (quiz) => { + quiz.config.formContact[defaultValue].required = target.checked + }) + }} + + label={"Обязательно к заполнению"} /> - + {/* Запрашивать на - + */} {children} - + ) } diff --git a/src/pages/ContactFormPage/NewField/SwitchNewField.tsx b/src/pages/ContactFormPage/NewField/SwitchNewField.tsx index b0217195..139add09 100644 --- a/src/pages/ContactFormPage/NewField/SwitchNewField.tsx +++ b/src/pages/ContactFormPage/NewField/SwitchNewField.tsx @@ -8,27 +8,29 @@ import CustomizedSwitch from "@ui_kit/CustomSwitch"; interface Props { switchState: string, + drawerNewFieldHC:(a:string)=>void } -export default function SwitchNewField({switchState ='name'}: Props) { +export default function SwitchNewField({switchState ='name', drawerNewFieldHC}: Props) { const [SwitchMask, setSwitchMask] = React.useState(false); + console.log(switchState) const SwitchMaskHC = (bool:boolean) => { setSwitchMask(bool) } switch (switchState) { case 'name': - return (); + return (); break; case 'email': - return (); + return (); break; case 'phone': return ( - - + {/* } label="Маска для телефона" @@ -41,7 +43,7 @@ export default function SwitchNewField({switchState ='name'}: Props) { SwitchMaskHC(!SwitchMask) }} - /> + /> */} {SwitchMask ? @@ -52,10 +54,10 @@ export default function SwitchNewField({switchState ='name'}: Props) { ); break; case 'text': - return (); + return (); break; case 'address': - return (); + return (); break; default: return (<>) diff --git a/src/pages/ContactFormPage/NewField/WindowNewField.tsx b/src/pages/ContactFormPage/NewField/WindowNewField.tsx index e891f770..a7c69458 100644 --- a/src/pages/ContactFormPage/NewField/WindowNewField.tsx +++ b/src/pages/ContactFormPage/NewField/WindowNewField.tsx @@ -1,29 +1,39 @@ import * as React from 'react'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; -import {Typography, useTheme} from "@mui/material"; +import { Typography, useTheme } from "@mui/material"; import CloseIcon from '@mui/icons-material/Close'; import ButtonsNewField from "./ButtonsNewField"; import SwitchNewField from "./SwitchNewField"; +import { useCurrentQuiz } from '@root/quizes/hooks'; -export default function WindowNewField() { +export default function WindowNewField({ type, drawerNewFieldHC }: { type: string, drawerNewFieldHC: (a: string) => void }) { + const quiz = useCurrentQuiz() const theme = useTheme(); - const [switchState, setSwitchState] = React.useState('name'); + const [switchState, setSwitchState] = React.useState(""); + React.useEffect(() => { + for (let val in quiz?.config.formContact) { + if (!quiz?.config.formContact[val]?.used && (val !== "title" && val !== "desc" && val !== "button")) { + setSwitchState(val) + return + } + } + }, []) + const SSHC = (data: string) => { setSwitchState(data) } - - return( + return ( <> - + Новое поле - + - - + + ) } \ No newline at end of file diff --git a/src/pages/InstallQuiz/InstallQuiz.tsx b/src/pages/InstallQuiz/InstallQuiz.tsx index e13a305c..c70c158b 100644 --- a/src/pages/InstallQuiz/InstallQuiz.tsx +++ b/src/pages/InstallQuiz/InstallQuiz.tsx @@ -104,10 +104,7 @@ export default function InstallQuiz() { }} > - + Ссылка на квиз - + - + Вконтакте - - Для публикации сниппета на стене группы, призывающего пройти тест. - + Для публикации сниппета на стене группы, призывающего пройти тест. - + Свой домен - - Подключите свой домен, если хотите, чтобы квиз открывался по вашей - ссылке. - + Подключите свой домен, если хотите, чтобы квиз открывался по вашей ссылке. - - - + - - Добавить квиз в группу ВК - + Добавить квиз в группу ВК - Для публикации сниппета на стене, призывающего пройти тест, - вставьте в новую запись ссылку на приложение + Для публикации сниппета на стене, призывающего пройти тест, вставьте в новую запись ссылку на приложение - - - где XXXXXXXXXXX - id вашего сообщества (полный адрес ссылки - можно узнать в браузерной строке, открыв приложение в вашей - группе + + + где XXXXXXXXXXX - id вашего сообщества (полный адрес ссылки можно узнать в браузерной строке, открыв + приложение в вашей группе @@ -496,24 +466,13 @@ export default function InstallQuiz() { flexDirection: "column", }} > - - 2. Откройте квиз в группе (вы должны быть администратором - группы) + 2. Откройте квиз в группе (вы должны быть администратором группы) + + Справа снизу нажмите на значок "редактировать" В появившемся окне введите id квиза и нажмите + "Привязать". Готово! Квиз привязан к группе - - Справа снизу нажмите на значок "редактировать" В появившемся - окне введите id квиза и нажмите "Привязать". Готово! Квиз - привязан к группе - - - ID этого квиза - - + ID этого квиза + @@ -555,9 +514,7 @@ export default function InstallQuiz() { background: theme.palette.background.default, }} > - - Подключить свой домен - + Подключить свой домен - Подключите домен к проекту, чтобы создать несколько квизов на - одном домене + Подключите домен к проекту, чтобы создать несколько квизов на одном домене 1. Настройте записи в регистраторе домена - setBackgroundType("text")} - > + setBackgroundType("text")}> Для поддоменов - setBackgroundType("video")} - > + setBackgroundType("video")}> Для доменов @@ -623,8 +573,7 @@ export default function InstallQuiz() { maxWidth: "372px", }} > - Как подключить свой домен/поддомен к квизу? Ошибки при - подключении домена + Как подключить свой домен/поддомен к квизу? Ошибки при подключении домена @@ -655,10 +604,7 @@ export default function InstallQuiz() { // onMouseDown={} edge="end" > - + } @@ -668,11 +614,8 @@ export default function InstallQuiz() { - - Привязка домена и обновление DNS записей может занять до 48 - часов + + Привязка домена и обновление DNS записей может занять до 48 часов + )} - }} - sxcont={{ - display: "flex", - justifyContent: "space-between", - alignItems: "center", - svg: { color: "#000000" }, - padding: isMobile ? 0 : "0 22px 0 40px" - }} + + + + - - - - {!isMobile && ( - - )} - - - - - - {isMobile && ( - - )} - - - - - - - {/**/} - {/* {buttonMenu.map(({ path, title }) => (*/} - {/* */} - {/* */} - {/* {title}*/} - {/* */} - {/* */} - {/* ))}*/} - {/**/} - {isMobile ? ( - - ) : ( - - - - )} - - - - - - ); + {isMobile && } + + + + + + + {/**/} + {/* {buttonMenu.map(({ path, title }) => (*/} + {/* */} + {/* */} + {/* {title}*/} + {/* */} + {/* */} + {/* ))}*/} + {/**/} + {isMobile ? ( + + ) : ( + + + + )} + + + + + + ); } diff --git a/src/pages/Questions/BranchingMap/CsComponent.tsx b/src/pages/Questions/BranchingMap/CsComponent.tsx index 48573745..d07c5b35 100644 --- a/src/pages/Questions/BranchingMap/CsComponent.tsx +++ b/src/pages/Questions/BranchingMap/CsComponent.tsx @@ -7,7 +7,7 @@ import { useCurrentQuiz } from "@root/quizes/hooks"; import { updateRootContentId } from "@root/quizes/actions" import { AnyTypedQuizQuestion } from "@model/questionTypes/shared" import { useQuestionsStore } from "@root/questions/store"; -import { deleteQuestion, updateQuestion, getQuestionByContentId, clearRuleForAll } from "@root/questions/actions"; +import { deleteQuestion, updateQuestion, getQuestionByContentId, clearRuleForAll, createFrontResult } from "@root/questions/actions"; import { updateOpenedModalSettingsId, } from "@root/uiTools/actions"; import { cleardragQuestionContentId } from "@root/uiTools/actions"; import { withErrorBoundary } from "react-error-boundary"; @@ -160,7 +160,6 @@ function CsComponent({ }, [modalQuestionTargetContentId]) const addNode = ({ parentNodeContentId, targetNodeContentId }: { parentNodeContentId: string, targetNodeContentId?: string }) => { - //запрещаем работу родителя-ребенка если это один и тот же вопрос if (parentNodeContentId === targetNodeContentId) return @@ -170,10 +169,10 @@ function CsComponent({ const parentNodeChildren = cy?.$('edge[source = "' + parentNodeContentId + '"]')?.length //если есть инфо о выбранном вопросе из модалки - берём родителя из инфо модалки. Иначе из значения дропа const targetQuestion = { ...getQuestionByContentId(targetNodeContentId || dragQuestionContentId) } as AnyTypedQuizQuestion - - if (Object.keys(targetQuestion).length !== 0 && Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) { + if (Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) { clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren }) cy?.data('changed', true) + createFrontResult(quiz.backendId, targetQuestion.content.id) const es = cy?.add([ { data: { @@ -203,6 +202,7 @@ function CsComponent({ //смотрим не добавлен ли родителю result. Если да - убираем его. Веточкам result не нужен trashQuestions.forEach((targetQuestion) => { if (targetQuestion.type === "result" && targetQuestion.content.rule.parentId === parentQuestion.content.id) { + console.log('deleteQ', targetQuestion.id) deleteQuestion(targetQuestion.id); } }) @@ -271,7 +271,8 @@ function CsComponent({ const parentQuestionContentId = cy?.$('edge[target = "' + targetNodeContentId + '"]')?.toArray()?.[0]?.data()?.source if (targetNodeContentId && parentQuestionContentId) { - +if (cy?.edges(`[source="${parentQuestionContentId}"]`).length === 0) + createFrontResult(quiz.backendId, parentQuestionContentId) clearDataAfterRemoveNode({ targetQuestionContentId: targetNodeContentId, parentQuestionContentId }) cy?.remove(cy?.$('#' + targetNodeContentId)).layout(lyopts).run() } diff --git a/src/pages/Questions/ButtonsOptionsAndPict.tsx b/src/pages/Questions/ButtonsOptionsAndPict.tsx index 972e99cd..b1b8f1d6 100644 --- a/src/pages/Questions/ButtonsOptionsAndPict.tsx +++ b/src/pages/Questions/ButtonsOptionsAndPict.tsx @@ -73,6 +73,7 @@ export default function ButtonsOptionsAndPict({ display: "flex", flexWrap: isMobile ? "wrap" : "nowrap", gap: "6px", + maxWidth: isMobile ? "200px" : undefined }} > (false); - const [open, setOpen] = useState(false); - const theme = useTheme(); - const isTablet = useMediaQuery(theme.breakpoints.down(1000)); - const isMobile = useMediaQuery(theme.breakpoints.down(790)); - const anchorRef = useRef(null); - const quiz = useCurrentQuiz(); + const { questions } = useQuestionsStore(); + const [plusVisible, setPlusVisible] = useState(false); + const [open, setOpen] = useState(false); + const theme = useTheme(); + const isTablet = useMediaQuery(theme.breakpoints.down(1000)); + const isMobile = useMediaQuery(theme.breakpoints.down(790)); + const anchorRef = useRef(null); + const quiz = useCurrentQuiz(); - const setTitle = useDebouncedCallback((title) => { - const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion; + const setTitle = useDebouncedCallback((title) => { + const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion; - updateQuestionFn(question.id, question => { - question.title = title; - }); - }, 200); + updateQuestionFn(question.id, (question) => { + question.title = title; + }); + }, 200); - return ( - <> - - - + + + + setTitle(target.value || " ")} + InputProps={{ + startAdornment: ( + + setOpen((isOpened) => !isOpened)} > - setTitle(target.value || " ")} - InputProps={{ - startAdornment: ( - - setOpen((isOpened) => !isOpened)} - > - {IconAndrom(question.expanded, question.type)} - - setOpen(false)} - anchorRef={anchorRef} - question={question} - questionType={question.type} - /> - - ), - }} - 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, - }, - }, - }} - inputProps={{ - sx: { - fontSize: "18px", - lineHeight: "21px", - py: 0, - paddingLeft: question.type === null ? 0 : "18px", - }, - "data-cy": "quiz-question-title", - }} - /> - - - toggleExpandQuestion(question.id)} - > - {question.expanded ? ( - - ) : ( - - )} - - {question.expanded ? ( - <> - ) : ( - - - } - checkedIcon={} - /> - } - label={""} - sx={{ - color: theme.palette.grey2.main, - ml: "-9px", - mr: 0, - userSelect: "none", - }} - /> - copyQuestion(question.id, question.quizId)} - > - - - { - const deleteFn = () => { - if (question.type !== null) { - if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам - updateRootContentId(quiz.id, ""); - clearRuleForAll(); - deleteQuestion(question.id); - questions.forEach(q => { - if (q.type === "result") { - deleteQuestion(q.id); - } - }); - } else if (question.content.rule.parentId.length > 0) { //удалить из стора вопрос из дерева и очистить его потомков - const clearQuestions = [] as string[]; - - //записываем потомков , а их результаты удаляем - const getChildren = (parentQuestion: AnyTypedQuizQuestion) => { - questions.forEach((targetQuestion) => { - if (targetQuestion.content.rule.parentId === parentQuestion.content.id) {//если у вопроса совпал родитель с родителем => он потомок, в кучу его - if (targetQuestion.type === "result") { - deleteQuestion(targetQuestion.id); - } else { - 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); - } else { - console.log("удаляю безтипогово"); - deleteQuestion(question.id); - } - }; - - deleteQuestionWithTimeout(question.id, deleteFn); - }} - data-cy="delete-question" - > - - - - )} - {question.type !== null && - - {question.page + 1} - - } - - - - - - {question.expanded && ( - - {question.type === null ? ( - - ) : ( - - )} - - )} - - setPlusVisible(true)} - onMouseLeave={() => setPlusVisible(false)} - sx={{ - maxWidth: "825px", - display: "flex", - alignItems: "center", - height: "40px", - cursor: "pointer", - }} - > - createUntypedQuestion(question.quizId, question.id)} - sx={{ - display: plusVisible && !isDragging ? "flex" : "none", - width: "100%", - alignItems: "center", - columnGap: "10px", - }} - data-cy="create-question" - > - + setOpen(false)} + anchorRef={anchorRef} + question={question} + questionType={question.type} /> - - - - - ); + + ), + }} + 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, + }, + }, + }} + inputProps={{ + sx: { + fontSize: "18px", + lineHeight: "21px", + py: 0, + paddingLeft: question.type === null ? 0 : "18px", + }, + "data-cy": "quiz-question-title", + }} + /> + + + toggleExpandQuestion(question.id)} + > + {question.expanded ? ( + + ) : ( + + )} + + {question.expanded ? ( + <> + ) : ( + + + } + checkedIcon={} + /> + } + label={""} + sx={{ + color: theme.palette.grey2.main, + ml: "-9px", + mr: 0, + userSelect: "none", + }} + /> + copyQuestion(question.id, question.quizId)}> + + + { + const deleteFn = () => { + if (question.type !== null) { + if (question.content.rule.parentId === "root") { + //удалить из стора root и очистить rule всем вопросам + updateRootContentId(quiz.id, ""); + clearRuleForAll(); + deleteQuestion(question.id); + questions.forEach((q) => { + if (q.type === "result") { + deleteQuestion(q.id); + } + }); + } else if (question.content.rule.parentId.length > 0) { + //удалить из стора вопрос из дерева и очистить его потомков + const clearQuestions = [] as string[]; + + //записываем потомков , а их результаты удаляем + const getChildren = (parentQuestion: AnyTypedQuizQuestion) => { + questions.forEach((targetQuestion) => { + if (targetQuestion.content.rule.parentId === parentQuestion.content.id) { + //если у вопроса совпал родитель с родителем => он потомок, в кучу его + if (targetQuestion.type === "result") { + deleteQuestion(targetQuestion.id); + } else { + 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); + } else { + console.log("удаляю безтипогово"); + deleteQuestion(question.id); + } + }; + + deleteQuestionWithTimeout(question.id, deleteFn); + }} + data-cy="delete-question" + > + + + + )} + {question.type !== null && ( + + {question.page + 1} + + )} + + + + + + {question.expanded && ( + + {question.type === null ? ( + + ) : ( + + )} + + )} + + setPlusVisible(true)} + onMouseLeave={() => setPlusVisible(false)} + sx={{ + maxWidth: "825px", + display: "flex", + alignItems: "center", + height: "40px", + cursor: "pointer", + }} + > + createUntypedQuestion(question.quizId, question.id)} + sx={{ + display: plusVisible && !isDragging ? "flex" : "none", + width: "100%", + alignItems: "center", + columnGap: "10px", + }} + data-cy="create-question" + > + + + + + + ); } const IconAndrom = (isExpanded: boolean, questionType: QuestionType | null) => { - switch (questionType) { - case "variant": - return ( - - ); - case "images": - return ( - - ); - case "varimg": - return ( - - ); - case "emoji": - return ( - - ); - case "text": - return ( - - ); - case "select": - return ( - - ); - case "date": - return ( - - ); - case "number": - return ( - - ); - case "file": - return ( - - ); - case "page": - return ( - - ); - case "rating": - return ( - - ); - default: - return <>; - } + switch (questionType) { + case "variant": + return ; + case "images": + return ; + case "varimg": + return ; + case "emoji": + return ; + case "text": + return ; + case "select": + return ; + case "date": + return ; + case "number": + return ; + case "file": + return ; + case "page": + return ; + case "rating": + return ; + default: + return <>; + } }; diff --git a/src/pages/Questions/Form/FormDraggableList/FormDraggableListItem.tsx b/src/pages/Questions/Form/FormDraggableList/FormDraggableListItem.tsx index 61b62039..d8e89df0 100644 --- a/src/pages/Questions/Form/FormDraggableList/FormDraggableListItem.tsx +++ b/src/pages/Questions/Form/FormDraggableList/FormDraggableListItem.tsx @@ -5,80 +5,77 @@ import { Draggable } from "react-beautiful-dnd"; import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "../../../../model/questionTypes/shared"; import QuestionsPageCard from "./QuestionPageCard"; - type FormDraggableListItemProps = { - question: AnyTypedQuizQuestion | UntypedQuizQuestion; - questionIndex: number; + question: AnyTypedQuizQuestion | UntypedQuizQuestion; + questionIndex: number; }; -export default memo( - ({ question, questionIndex }: FormDraggableListItemProps) => { - const theme = useTheme(); +export default memo(({ question, questionIndex }: FormDraggableListItemProps) => { + const theme = useTheme(); - return ( - - {(provided) => ( - - {question.deleted ? ( - - - Вопрос удалён. - - { - updateQuestion(question.id, question => { - question.deleted = false; - }); - }} - sx={{ - cursor: "pointer", - fontSize: "16px", - textDecoration: "underline", - color: theme.palette.brightPurple.main, - textDecorationColor: theme.palette.brightPurple.main, - }} - > - Восстановить? - - - ) : ( - - - - )} - - )} - - ); - } -); + return ( + + {(provided) => ( + + {question.deleted ? ( + + + Вопрос удалён. + + { + updateQuestion(question.id, (question) => { + question.deleted = false; + }); + }} + sx={{ + cursor: "pointer", + fontSize: "16px", + textDecoration: "underline", + color: theme.palette.brightPurple.main, + textDecorationColor: theme.palette.brightPurple.main, + }} + > + Восстановить? + + + ) : ( + + + + )} + + )} + + ); +}); diff --git a/src/pages/Questions/Form/FormDraggableList/QuestionPageCard.tsx b/src/pages/Questions/Form/FormDraggableList/QuestionPageCard.tsx index 89e8006e..d4f140f5 100644 --- a/src/pages/Questions/Form/FormDraggableList/QuestionPageCard.tsx +++ b/src/pages/Questions/Form/FormDraggableList/QuestionPageCard.tsx @@ -11,8 +11,8 @@ 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 { Box, InputAdornment, Paper } from "@mui/material"; -import { updateQuestion, updateUntypedQuestion } from "@root/questions/actions"; +import { Box, FormControlLabel, IconButton, InputAdornment, Paper, useMediaQuery, useTheme } from "@mui/material"; +import { toggleExpandQuestion, updateQuestion, updateUntypedQuestion } from "@root/questions/actions"; import CustomTextField from "@ui_kit/CustomTextField"; import { useRef, useState } from "react"; import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd"; @@ -22,142 +22,206 @@ import SwitchQuestionsPage from "../../SwitchQuestionsPage"; import { ChooseAnswerModal } from "./ChooseAnswerModal"; import FormTypeQuestions from "../FormTypeQuestions"; import { QuestionType } from "@model/question/question"; - +import { CrossedEyeIcon } from "@icons/CrossedEyeIcon"; +import { ArrowDownIcon } from "@icons/questionsPage/ArrowDownIcon"; +import { CopyIcon } from "@icons/questionsPage/CopyIcon"; +import { DeleteIcon } from "@icons/questionsPage/deleteIcon"; +import { HideIcon } from "@icons/questionsPage/hideIcon"; +import ExpandLessIcon from "@mui/icons-material/ExpandLess"; +import { NoLuggageOutlined, SignalCellularNullOutlined } from "@mui/icons-material"; interface Props { - question: AnyTypedQuizQuestion | UntypedQuizQuestion; - questionIndex: number; - draggableProps: DraggableProvidedDragHandleProps | null | undefined; + question: AnyTypedQuizQuestion | UntypedQuizQuestion; + questionIndex: number; + draggableProps: DraggableProvidedDragHandleProps | null | undefined; } -export default function QuestionsPageCard({ - question, - questionIndex, - draggableProps, -}: Props) { - const [open, setOpen] = useState(false); - const anchorRef = useRef(null); +export default function QuestionsPageCard({ question, questionIndex, draggableProps }: Props) { + const [open, setOpen] = useState(false); + const anchorRef = useRef(null); + const theme = useTheme(); + const isTablet = useMediaQuery(theme.breakpoints.down(1000)); + const isMobile = useMediaQuery(theme.breakpoints.down(790)); - const setTitle = useDebouncedCallback((title) => { - const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion; + const setTitle = useDebouncedCallback((title) => { + const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion; - updateQuestionFn(question.id, question => { - question.title = title; - }); - }, 200); + updateQuestionFn(question.id, (question) => { + question.title = title; + }); + }, 200); - return ( - <> - - - setTitle(target.value)} - sx={{ margin: "20px", width: "auto" }} - InputProps={{ - startAdornment: ( - - setOpen((isOpened) => !isOpened)} - > - {IconAndrom(question.type)} - - setOpen(false)} - anchorRef={anchorRef} - question={question} - questionType={question.type} - /> - - ), - endAdornment: ( - - {questionIndex !== 0 && ( - - - - )} - - ), - }} + return ( + <> + + + + setTitle(target.value)} + sx={{ width: "100%" }} + InputProps={{ + startAdornment: ( + + setOpen((isOpened) => !isOpened)} + > + {IconAndrom(question.type)} + + setOpen(false)} + anchorRef={anchorRef} + question={question} + questionType={question.type} /> - {question.type === null ? ( - - ) : ( - - )} + + ), + }} + /> + + + toggleExpandQuestion(question.id)} + > + {question.expanded ? ( + + ) : ( + + )} + + + + {questionIndex + 1} - - - ); + + + + + + + + + {question.type === null ? ( + + ) : ( + + )} + + + + ); } const IconAndrom = (questionType: QuestionType | null) => { - switch (questionType) { - case "variant": - return ; - case "images": - return ( - - ); - case "varimg": - return ( - - ); - case "emoji": - return ; - case "text": - return ; - case "select": - return ( - - ); - case "date": - return ; - case "number": - return ; - case "file": - return ( - - ); - case "page": - return ; - case "rating": - return ( - - ); - default: - return ( - - ); - } + switch (questionType) { + case "variant": + return ; + case "images": + return ; + case "varimg": + return ; + case "emoji": + return ; + case "text": + return ; + case "select": + return ; + case "date": + return ; + case "number": + return ; + case "file": + return ; + case "page": + return ; + case "rating": + return ; + default: + return ; + } }; diff --git a/src/pages/Questions/Form/FormDraggableList/index.tsx b/src/pages/Questions/Form/FormDraggableList/index.tsx index 9eda5284..77c01d28 100644 --- a/src/pages/Questions/Form/FormDraggableList/index.tsx +++ b/src/pages/Questions/Form/FormDraggableList/index.tsx @@ -5,31 +5,25 @@ import { DragDropContext, Droppable } from "react-beautiful-dnd"; import FormDraggableListItem from "./FormDraggableListItem"; import { useQuestions } from "@root/questions/hooks"; - export const FormDraggableList = () => { + const { questions } = useQuestions(); - const { questions } = useQuestions() - - const onDragEnd = ({ destination, source }: DropResult) => { - if (destination) reorderQuestions(source.index, destination.index); - }; + const onDragEnd = ({ destination, source }: DropResult) => { + if (destination) reorderQuestions(source.index, destination.index); + }; - return ( - - - {(provided) => ( - - {questions?.map((question, index) => ( - - ))} - {provided.placeholder} - - )} - - - ); + return ( + + + {(provided) => ( + + {questions?.map((question, index) => ( + + ))} + {provided.placeholder} + + )} + + + ); }; diff --git a/src/pages/Questions/Form/FormQuestionsPage.tsx b/src/pages/Questions/Form/FormQuestionsPage.tsx index d4e138fe..baca3a95 100644 --- a/src/pages/Questions/Form/FormQuestionsPage.tsx +++ b/src/pages/Questions/Form/FormQuestionsPage.tsx @@ -8,106 +8,103 @@ import { FormDraggableList } from "./FormDraggableList"; import { collapseAllQuestions, createUntypedQuestion } from "@root/questions/actions"; import { useCurrentQuiz } from "@root/quizes/hooks"; - export default function FormQuestionsPage() { - const theme = useTheme(); - const quiz = useCurrentQuiz(); + const theme = useTheme(); + const quiz = useCurrentQuiz(); - if (!quiz) return null; + if (!quiz) return null; - return ( - <> - - Заголовок анкеты - - - - - { - createUntypedQuestion(quiz.backendId); - }} - data-cy="create-question" - > - - - Добавить еще один вопрос - - - - - - - - {createPortal(, document.body)} - - ); + return ( + <> + + Заголовок анкеты + + + + + { + createUntypedQuestion(quiz.backendId); + }} + data-cy="create-question" + > + + Добавить еще один вопрос + + + + + + + {createPortal(, document.body)} + + ); } diff --git a/src/pages/Questions/Form/FormTypeQuestions.tsx b/src/pages/Questions/Form/FormTypeQuestions.tsx index a1bc3907..b0cd4fe7 100644 --- a/src/pages/Questions/Form/FormTypeQuestions.tsx +++ b/src/pages/Questions/Form/FormTypeQuestions.tsx @@ -12,80 +12,75 @@ import Slider from "../../../assets/icons/questionsPage/slider"; import { QuestionType } from "@model/question/question"; import { createTypedQuestion } from "@root/questions/actions"; -import type { - UntypedQuizQuestion -} from "../../../model/questionTypes/shared"; - +import type { UntypedQuizQuestion } from "../../../model/questionTypes/shared"; type ButtonTypeQuestion = { - icon: JSX.Element; - title: string; - value: QuestionType; + icon: JSX.Element; + title: string; + value: QuestionType; }; const BUTTON_TYPE_SHORT_QUESTIONS: ButtonTypeQuestion[] = [ - { - icon: , - title: "Варианты ответов", - value: "variant", - }, - { - icon: , - title: "Своё поле для ввода", - value: "text", - }, - { - icon: , - title: "Выпадающий список", - value: "select", - }, - { - icon: , - title: "Дата", - value: "date", - }, - { - icon: , - title: "Ползунок", - value: "number", - }, - { - icon: , - title: "Загрузка файла", - value: "file", - }, + { + icon: , + title: "Варианты ответов", + value: "variant", + }, + { + icon: , + title: "Своё поле для ввода", + value: "text", + }, + { + icon: , + title: "Выпадающий список", + value: "select", + }, + { + icon: , + title: "Дата", + value: "date", + }, + { + icon: , + title: "Ползунок", + value: "number", + }, + { + icon: , + title: "Загрузка файла", + value: "file", + }, ]; interface Props { - question: UntypedQuizQuestion; + question: UntypedQuizQuestion; } export default function FormTypeQuestions({ question }: Props) { - - return ( - - - {(("page" in question) && question.page === 0 - ? BUTTON_TYPE_QUESTIONS - : BUTTON_TYPE_SHORT_QUESTIONS - ).map(({ icon, title, value: questionType }) => ( - { - createTypedQuestion(question.id, questionType); - }} - icon={icon} - text={title} - /> - ))} - - - ); + return ( + + + {("page" in question && question.page === 0 ? BUTTON_TYPE_QUESTIONS : BUTTON_TYPE_SHORT_QUESTIONS).map( + ({ icon, title, value: questionType }) => ( + { + createTypedQuestion(question.id, questionType); + }} + icon={icon} + text={title} + /> + ) + )} + + + ); } diff --git a/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx b/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx index c203f580..ab440cb9 100644 --- a/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx +++ b/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx @@ -151,181 +151,181 @@ export default function OptionsAndPicture({ question }: Props) { onClose={closeCropModal} onSaveImageClick={handleCropModalSaveClick} /> - - - - - - {!isMobile && ( - - - - - - + - - - )} - - ), - endAdornment: ( - - - - - - - - - - - - ), - }} - sx={{ - "& .MuiInputBase-root": { - padding: "13.5px", - borderRadius: "10px", - background: "#ffffff", - height: "48px", - }, - "& .MuiOutlinedInput-notchedOutline": { - border: "none", - }, - }} - inputProps={{ - sx: { fontSize: "18px", lineHeight: "21px", py: 0 }, - }} - /> + {/**/} + {/* */} + {/* */} + {/* */} + {/* */} + {/* {!isMobile && (*/} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* +*/} + {/* */} + {/* */} + {/* )}*/} + {/* */} + {/* ),*/} + {/* endAdornment: (*/} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* ),*/} + {/* }}*/} + {/* sx={{*/} + {/* "& .MuiInputBase-root": {*/} + {/* padding: "13.5px",*/} + {/* borderRadius: "10px",*/} + {/* background: "#ffffff",*/} + {/* height: "48px",*/} + {/* },*/} + {/* "& .MuiOutlinedInput-notchedOutline": {*/} + {/* border: "none",*/} + {/* },*/} + {/* }}*/} + {/* inputProps={{*/} + {/* sx: { fontSize: "18px", lineHeight: "21px", py: 0 },*/} + {/* }}*/} + {/* />*/} - {isMobile && ( - - - - - - - - + - - - - )} - + {/*{isMobile && (*/} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* +*/} + {/* */} + {/* */} + {/* */} + {/*)}*/} + {/**/} { - updateOpenBranchingPanel(false) - updateEditSomeQuestion() - },[]) + const theme = useTheme(); + const { openedModalSettingsId, openBranchingPanel } = useUiTools(); + const isMobile = useMediaQuery(theme.breakpoints.down(660)); + const quiz = useCurrentQuiz(); + useLayoutEffect(() => { + updateOpenBranchingPanel(false); + updateEditSomeQuestion(); + }, []); - const ref = useRef() - if (!quiz) return null; + const ref = useRef(); + if (!quiz) return null; + return ( + <> + + {quiz.name ? quiz.name : "Заголовок квиза"} + + + + + { + createUntypedQuestion(quiz.backendId); + }} + sx={{ + position: "fixed", + left: isMobile ? "20px" : "250px", + bottom: isMobile ? "140px" : "20px", + }} + data-cy="create-question" + > + + - return ( - <> - - { - quiz.name ? quiz.name : "Заголовок квиза" } - - - - - { - createUntypedQuestion(quiz.backendId); - }} - sx={{ - position: "fixed", - left: isMobile ? "20px" : "250px", - bottom: "20px", - }} - data-cy="create-question" - > - - - - - - - - - - {createPortal(, document.body)} - {openedModalSettingsId !== null && } - - ); + + + + + + {createPortal(, document.body)} + {openedModalSettingsId !== null && } + + ); } diff --git a/src/pages/Questions/SliderOptions/SliderOptions.tsx b/src/pages/Questions/SliderOptions/SliderOptions.tsx index 5363e35c..bcc23b7d 100644 --- a/src/pages/Questions/SliderOptions/SliderOptions.tsx +++ b/src/pages/Questions/SliderOptions/SliderOptions.tsx @@ -6,217 +6,216 @@ import SwitchSlider from "./switchSlider"; import type { QuizQuestionNumber } from "../../../model/questionTypes/number"; import { updateQuestion } from "@root/questions/actions"; - interface Props { - question: QuizQuestionNumber; + question: QuizQuestionNumber; } export default function SliderOptions({ question }: Props) { - const theme = useTheme(); - const isTablet = useMediaQuery(theme.breakpoints.down(980)); - const isMobile = useMediaQuery(theme.breakpoints.down(790)); - const [switchState, setSwitchState] = useState("setting"); - const [stepError, setStepError] = useState(""); + const theme = useTheme(); + const isTablet = useMediaQuery(theme.breakpoints.down(980)); + const isMobile = useMediaQuery(theme.breakpoints.down(790)); + const [switchState, setSwitchState] = useState("setting"); + const [stepError, setStepError] = useState(""); - const SSHC = (data: string) => { - setSwitchState(data); - }; + const SSHC = (data: string) => { + setSwitchState(data); + }; - return ( - <> - + + + + Выбор значения из диапазона + + + { + updateQuestion(question.id, (question) => { + if (question.type !== "number") return; + + question.content.range = `${target.value}—${question.content.range.split("—")[1]}`; + }); + }} + onBlur={({ target }) => { + const start = question.content.start; + const min = Number(target.value); + const max = Number(question.content.range.split("—")[1]); + + if (min >= max) { + updateQuestion(question.id, (question) => { + if (question.type !== "number") return; + + question.content.range = `${max - 1 >= 0 ? max - 1 : 0}—${question.content.range.split("—")[1]}`; + }); + } + + if (start < min) { + updateQuestion(question.id, (question) => { + if (question.type !== "number") return; + + question.content.start = min; + }); + } + }} + /> + + { + updateQuestion(question.id, (question) => { + if (question.type !== "number") return; + + question.content.range = `${question.content.range.split("—")[0]}—${target.value}`; + }); + }} + onBlur={({ target }) => { + const start = question.content.start; + const step = question.content.step; + const min = Number(question.content.range.split("—")[0]); + const max = Number(target.value); + const range = max - min; + + if (max <= min) { + updateQuestion(question.id, (question) => { + if (question.type !== "number") return; + + question.content.range = `${min}—${min + 1 >= 100 ? 100 : min + 1}`; + }); + } + + if (start > max) { + updateQuestion(question.id, (question) => { + if (question.type !== "number") return; + + question.content.start = max; + }); + } + + if (step > max) { + updateQuestion(question.id, (question) => { + if (question.type !== "number") return; + + question.content.step = min; + }); + + if (range % step) { + setStepError(`Шаг должен делить без остатка диапазон ${max} - ${min} = ${max - min}`); + } else { + setStepError(""); + } + } + }} + /> + + + + + + Начальное значение + + { + updateQuestion(question.id, (question) => { + if (question.type !== "number") return; + + question.content.start = Number(target.value); + }); + }} + /> + + + - - - Выбор значения из диапазона - - - { - updateQuestion(question.id, question => { - if (question.type !== "number") return; + Шаг + + { + updateQuestion(question.id, (question) => { + if (question.type !== "number") return; - question.content.range = `${target.value}—${question.content.range.split("—")[1]}`; - }); - }} - onBlur={({ target }) => { - const start = question.content.start; - const min = Number(target.value); - const max = Number(question.content.range.split("—")[1]); + question.content.step = Number(target.value); + }); + }} + onBlur={({ target }) => { + const min = Number(question.content.range.split("—")[0]); + const max = Number(question.content.range.split("—")[1]); + const range = max - min; + const step = Number(target.value); - if (min >= max) { - updateQuestion(question.id, question => { - if (question.type !== "number") return; + if (step > max) { + updateQuestion(question.id, (question) => { + if (question.type !== "number") return; - question.content.range = `${max - 1 >= 0 ? max - 1 : 0}—${question.content.range.split("—")[1]}`; - }); - } + question.content.step = max; + }); + } - if (start < min) { - updateQuestion(question.id, question => { - if (question.type !== "number") return; - - question.content.start = min; - }); - } - }} - /> - - { - updateQuestion(question.id, question => { - if (question.type !== "number") return; - - question.content.range = `${question.content.range.split("—")[0]}—${target.value}`; - }); - }} - onBlur={({ target }) => { - const start = question.content.start; - const step = question.content.step; - const min = Number(question.content.range.split("—")[0]); - const max = Number(target.value); - const range = max - min; - - if (max <= min) { - updateQuestion(question.id, question => { - if (question.type !== "number") return; - - question.content.range = `${min}—${min + 1 >= 100 ? 100 : min + 1}`; - }); - } - - if (start > max) { - updateQuestion(question.id, question => { - if (question.type !== "number") return; - - question.content.start = max; - }); - } - - if (step > max) { - updateQuestion(question.id, question => { - if (question.type !== "number") return; - - question.content.step = min; - }); - - if (range % step) { - setStepError(`Шаг должен делить без остатка диапазон ${max} - ${min} = ${max - min}`); - } else { - setStepError(""); - } - } - }} - /> - - - - - - Начальное значение - - { - updateQuestion(question.id, question => { - if (question.type !== "number") return; - - question.content.start = Number(target.value); - }); - }} - /> - - - - Шаг - - { - updateQuestion(question.id, question => { - if (question.type !== "number") return; - - question.content.step = Number(target.value); - }); - }} - onBlur={({ target }) => { - const min = Number(question.content.range.split("—")[0]); - const max = Number(question.content.range.split("—")[1]); - const range = max - min; - const step = Number(target.value); - - if (step > max) { - updateQuestion(question.id, question => { - if (question.type !== "number") return; - - question.content.step = max; - }); - } - - if (range % step) { - setStepError(`Шаг должен делить без остатка диапазон ${max} - ${min} = ${max - min}`); - } else { - setStepError(""); - } - }} - /> - - - - - - - ); + if (range % step) { + setStepError(`Шаг должен делить без остатка диапазон ${max} - ${min} = ${max - min}`); + } else { + setStepError(""); + } + }} + /> + + + + + + + ); } diff --git a/src/pages/Questions/SwitchQuestionsPage.tsx b/src/pages/Questions/SwitchQuestionsPage.tsx index 31e29c15..44911e35 100644 --- a/src/pages/Questions/SwitchQuestionsPage.tsx +++ b/src/pages/Questions/SwitchQuestionsPage.tsx @@ -12,48 +12,46 @@ import UploadFile from "./UploadFile/UploadFile"; import AnswerOptions from "./answerOptions/AnswerOptions"; import { notReachable } from "../../utils/notReachable"; - interface Props { - question: AnyTypedQuizQuestion; + question: AnyTypedQuizQuestion; } export default function SwitchQuestionsPage({ question }: Props) { + switch (question.type) { + case "variant": + return ; - switch (question.type) { - case "variant": - return ; + case "images": + return ; - case "images": - return ; + case "varimg": + return ; - case "varimg": - return ; + case "emoji": + return ; - case "emoji": - return ; + case "text": + return ; - case "text": - return ; + case "select": + return ; - case "select": - return ; + case "date": + return ; - case "date": - return ; + case "number": + return ; - case "number": - return ; + case "file": + return ; - case "file": - return ; + case "page": + return ; - case "page": - return ; + case "rating": + return ; - case "rating": - return ; - - default: - notReachable(question) - } + default: + notReachable(question); + } } diff --git a/src/pages/Questions/UploadImage/UploadImageModal.tsx b/src/pages/Questions/UploadImage/UploadImageModal.tsx index e0e24377..d722e81f 100644 --- a/src/pages/Questions/UploadImage/UploadImageModal.tsx +++ b/src/pages/Questions/UploadImage/UploadImageModal.tsx @@ -1,172 +1,168 @@ -import { - Typography, - Box, - useTheme, - ButtonBase, - Modal, - TextField, - InputAdornment, -} from "@mui/material"; +import { Typography, Box, useTheme, ButtonBase, Modal, TextField, InputAdornment } from "@mui/material"; import UploadIcon from "../../../assets/icons/UploadIcon"; import SearchIcon from "../../../assets/icons/SearchIcon"; import UnsplashIcon from "../../../assets/icons/Unsplash.svg"; import { useRef, useState, type DragEvent } from "react"; +type ImageFormat = "jpg" | "jpeg" | "png" | "gif"; interface ModalkaProps { - isOpen: boolean; - onClose: () => void; - handleImageChange: (file: File) => void; + isOpen: boolean; + onClose: () => void; + handleImageChange: (file: File) => void; + description?: string; + accept?: ImageFormat[]; } export const UploadImageModal: React.FC = ({ - handleImageChange, - isOpen, - onClose, + handleImageChange, + isOpen, + onClose, + accept, + description, }) => { - const theme = useTheme(); - const dropZone = useRef(null); - const [ready, setReady] = useState(false); + const theme = useTheme(); + const dropZone = useRef(null); + const [ready, setReady] = useState(false); - const handleDragEnter = (event: DragEvent) => { - event.preventDefault(); - setReady(true); - }; + const handleDragEnter = (event: DragEvent) => { + event.preventDefault(); + setReady(true); + }; - const handleDrop = (event: DragEvent) => { - event.preventDefault(); - event.stopPropagation(); + const handleDrop = (event: DragEvent) => { + event.preventDefault(); + event.stopPropagation(); - const file = event.dataTransfer.files[0]; - if (!file) return; + const file = event.dataTransfer.files[0]; + if (!file) return; - handleImageChange(file); - }; + handleImageChange(file); + }; - return ( - "." + format).join(", ") : ""; + + console.log(acceptedFormats); + + return ( + + + + + Добавьте изображение + + + event.target.files?.[0] && handleImageChange(event.target.files[0])} + hidden + accept={acceptedFormats || ".jpg, .jpeg, .png , .gif"} + multiple + type="file" + data-cy="upload-image-input" + /> ) => event.preventDefault()} + onDrop={handleDrop} + ref={dropZone} + sx={{ + width: "580px", + padding: "33px 10px 33px 55px", + display: "flex", + alignItems: "center", + backgroundColor: theme.palette.background.default, + border: `1px solid ${ready ? "red" : theme.palette.grey2.main}`, + borderRadius: "8px", + gap: "55px", + }} + onDragEnter={handleDragEnter} // Применяем обработчик onDragEnter напрямую > - - - Добавьте изображение - - - event.target.files?.[0] && handleImageChange(event.target.files[0])} - hidden - accept="image/*" - multiple - type="file" - data-cy="upload-image-input" - /> - ) => - event.preventDefault() - } - onDrop={handleDrop} - ref={dropZone} - sx={{ - width: "580px", - padding: "33px 10px 33px 55px", - display: "flex", - alignItems: "center", - backgroundColor: theme.palette.background.default, - border: `1px solid ${ready ? "red" : theme.palette.grey2.main}`, - borderRadius: "8px", - gap: "55px", - }} - onDragEnter={handleDragEnter} // Применяем обработчик onDragEnter напрямую - > - - - - Загрузите или перетяните сюда файл - - - Принимает JPG, PNG, и GIF формат — максимум 5mb - - - - - - - Или выберите на фотостоке - - - - path": { stroke: "#9A9AAF" }, - }} - > - - - ), - }} - /> - + + + + Загрузите или перетяните сюда файл + + + {description || "Принимает JPG, PNG, и GIF формат — максимум 5mb"} + + - - ); + + + + Или выберите на фотостоке + + + + path": { stroke: "#9A9AAF" }, + }} + > + + + ), + }} + /> + + + + ); }; diff --git a/src/pages/ResultPage/FirstEntry.tsx b/src/pages/ResultPage/FirstEntry.tsx index ab4ec58b..b11eb41a 100644 --- a/src/pages/ResultPage/FirstEntry.tsx +++ b/src/pages/ResultPage/FirstEntry.tsx @@ -1,5 +1,4 @@ - -import { ResultSettings } from "./ResultSettings" +import { ResultSettings } from "./ResultSettings"; import { createFrontResult } from "@root/questions/actions"; import { useQuestionsStore } from "@root/questions/store"; import { useCurrentQuiz } from "@root/quizes/hooks"; @@ -7,6 +6,8 @@ import { Box, Typography, useTheme, useMediaQuery, Button } from "@mui/material" import image from "../../assets/Rectangle 110.png"; import { enqueueSnackbar } from "notistack"; import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; +import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft"; +import { decrementCurrentStep } from "@root/quizes/actions"; export const FirstEntry = () => { const theme = useTheme(); @@ -17,16 +18,20 @@ export const FirstEntry = () => { const create = () => { if (quiz?.config.haveRoot) { questions - .filter((question:AnyTypedQuizQuestion) => { - return question.type !== null && question.content.rule.parentId.length !== 0 && question.content.rule.children.length === 0 - }) - .forEach(question => { - createFrontResult(quiz.id, question.content.id) - }) + .filter((question: AnyTypedQuizQuestion) => { + return ( + question.type !== null && + question.content.rule.parentId.length !== 0 && + question.content.rule.children.length === 0 + ); + }) + .forEach((question) => { + createFrontResult(quiz.backendId, question.content.id); + }); } else { - createFrontResult(quiz.id, "line") + createFrontResult(quiz.backendId, "line"); } - } + }; return ( <> @@ -53,7 +58,7 @@ export const FirstEntry = () => { mr: !isSmallMonitor ? "104px" : 0, marginBottom: isSmallMonitor ? "20px" : 0, position: "relative", - height: "100%" + height: "100%", }} > @@ -69,7 +74,10 @@ export const FirstEntry = () => { }} > - Вы можете показывать разные результаты квиза (добавьте описание, изображение, стоимость и т.п.) разным пользователям, нужно только их создать и проставить условия. Таким образом ваш квиз получится максимально индивидуальным для каждого клиента. Показывайте картинку/видео вместо результата или переадресовывайте пользователя по нужной ссылке. + Вы можете показывать разные результаты квиза (добавьте описание, изображение, стоимость и т.п.) разным + пользователям, нужно только их создать и проставить условия. Таким образом ваш квиз получится максимально + индивидуальным для каждого клиента. Показывайте картинку/видео вместо результата или переадресовывайте + пользователя по нужной ссылке. { }} /> - + + + + + ); -} \ No newline at end of file +}; diff --git a/src/pages/ViewPublicationPage/ContactForm.tsx b/src/pages/ViewPublicationPage/ContactForm.tsx new file mode 100644 index 00000000..5bd41ebf --- /dev/null +++ b/src/pages/ViewPublicationPage/ContactForm.tsx @@ -0,0 +1,33 @@ +import { Box, Typography, Button } from "@mui/material"; + +import { useCurrentQuiz } from "@root/quizes/hooks"; + +type ContactFormProps = { + showResultForm: boolean; + setShowContactForm: (show: boolean) => void; + setShowResultForm: (show: boolean) => void; +}; + +export const ContactForm = ({ + showResultForm, + setShowContactForm, + setShowResultForm, +}: ContactFormProps) => { + const quiz = useCurrentQuiz(); + + const followNextForm = () => { + setShowContactForm(false); + setShowResultForm(true); + }; + + return ( + + Форма контактов + {!showResultForm && quiz?.config.resultInfo.when === "after" && ( + + )} + + ); +}; diff --git a/src/pages/ViewPublicationPage/Footer.tsx b/src/pages/ViewPublicationPage/Footer.tsx index 8b116b9e..9cbef9fe 100644 --- a/src/pages/ViewPublicationPage/Footer.tsx +++ b/src/pages/ViewPublicationPage/Footer.tsx @@ -2,6 +2,8 @@ import { useState, useEffect } from "react"; import { Box, Button, useTheme } from "@mui/material"; import { useQuizViewStore } from "@root/quizView"; +import { useCurrentQuiz } from "@root/quizes/hooks"; +import { useQuestionsStore } from "@root/questions/store"; import type { AnyTypedQuizQuestion, @@ -11,20 +13,26 @@ import { getQuestionByContentId } from "@root/questions/actions"; import { enqueueSnackbar } from "notistack"; type FooterProps = { - questions: AnyTypedQuizQuestion[]; setCurrentQuestion: (step: AnyTypedQuizQuestion) => void; question: AnyTypedQuizQuestion; + setShowContactForm: (show: boolean) => void; + setShowResultForm: (show: boolean) => void; + setResultQuestion: (id: string) => void; }; export const Footer = ({ setCurrentQuestion, - questions, question, + setShowContactForm, + setShowResultForm, + setResultQuestion, }: FooterProps) => { const [disablePreviousButton, setDisablePreviousButton] = useState(false); const [disableNextButton, setDisableNextButton] = useState(false); + const quiz = useCurrentQuiz(); const { answers } = useQuizViewStore(); + const questions = useQuestionsStore().questions as AnyTypedQuizQuestion[]; const theme = useTheme(); const linear = !questions.find( ({ content }) => content.rule.parentId === "root" @@ -72,32 +80,42 @@ export const Footer = ({ } if (linear) { - const questionIndex = questions.findIndex(({ id }) => id === question.id); + return; + } - const nextQuestion = questions[questionIndex + 1]; - - if (nextQuestion) { - setDisableNextButton(false); - } else { - setDisableNextButton(true); - } + const nextQuestionId = getNextQuestionId(); + if (nextQuestionId) { + setDisableNextButton(false); } else { - const nextQuestionId = getNextQuestionId(); - if (nextQuestionId) { + const nextQuestion = getQuestionByContentId( + question.content.rule.default + ); + + if (nextQuestion?.type) { setDisableNextButton(false); - } else { - const nextQuestion = getQuestionByContentId( - question.content.rule.default - ); - if (nextQuestion?.type) { - setDisableNextButton(false); - } else { - setDisableNextButton(true); - } } } }, [question, answers]); + const showResult = () => { + const resultQuestion = questions.find( + ({ type, content }) => + type === "result" && content.rule.parentId === question.content.id + ); + + if (resultQuestion) { + setResultQuestion(resultQuestion.id); + + return; + } + + if (quiz?.config.resultInfo.when === "after") { + setShowContactForm(true); + } else { + setShowResultForm(true); + } + }; + const getNextQuestionId = () => { if (answers.length) { const answer = answers.find( @@ -171,11 +189,12 @@ export const Footer = ({ const followNextStep = () => { if (linear) { const questionIndex = questions.findIndex(({ id }) => id === question.id); - const nextQuestion = questions[questionIndex + 1]; - if (nextQuestion) { + if (nextQuestion && nextQuestion.type !== "result") { setCurrentQuestion(nextQuestion); + } else { + showResult(); } return; @@ -186,7 +205,7 @@ export const Footer = ({ if (nextQuestionId) { const nextQuestion = getQuestionByContentId(nextQuestionId); - if (nextQuestion?.type) { + if (nextQuestion?.type && nextQuestion.type !== "result") { setCurrentQuestion(nextQuestion); return; } else { @@ -196,10 +215,10 @@ export const Footer = ({ const nextQuestion = getQuestionByContentId( question.content.rule.default ); - if (nextQuestion?.type) { + if (nextQuestion?.type && nextQuestion.type !== "result") { setCurrentQuestion(nextQuestion); } else { - enqueueSnackbar("не могу получить последующий вопрос (дефолтный)"); + showResult(); } } }; diff --git a/src/pages/ViewPublicationPage/Question.tsx b/src/pages/ViewPublicationPage/Question.tsx index c0d3cee2..9dd17654 100644 --- a/src/pages/ViewPublicationPage/Question.tsx +++ b/src/pages/ViewPublicationPage/Question.tsx @@ -1,5 +1,9 @@ +import { useState, useEffect } from "react"; import { Box } from "@mui/material"; +import { useCurrentQuiz } from "@root/quizes/hooks"; +import { getQuestionByContentId } from "@root/questions/actions"; + import { Variant } from "./questions/Variant"; import { Images } from "./questions/Images"; import { Varimg } from "./questions/Varimg"; @@ -12,12 +16,12 @@ import { File } from "./questions/File"; import { Page } from "./questions/Page"; import { Rating } from "./questions/Rating"; import { Footer } from "./Footer"; +import { ContactForm } from "./ContactForm"; +import { ResultForm } from "./ResultForm"; +import { ResultQuestion } from "./ResultQuestion"; -import { useState, type FC, useEffect } from "react"; import type { QuestionType } from "../../model/question/question"; import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared"; -import { useCurrentQuiz } from "@root/quizes/hooks"; -import { getQuestionByContentId } from "@root/questions/actions"; type QuestionProps = { questions: AnyTypedQuizQuestion[]; @@ -41,6 +45,9 @@ export const Question = ({ questions }: QuestionProps) => { const quiz = useCurrentQuiz(); const [currentQuestion, setCurrentQuestion] = useState(); + const [showContactForm, setShowContactForm] = useState(false); + const [showResultForm, setShowResultForm] = useState(false); + const [resultQuestion, setResultQuestion] = useState(""); useEffect(() => { const nextQuestion = getQuestionByContentId(quiz?.config.haveRoot || ""); @@ -70,13 +77,41 @@ export const Question = ({ questions }: QuestionProps) => { margin: "0 auto", }} > - + {!showContactForm && !showResultForm && !resultQuestion && ( + + )} + {resultQuestion && ( + + )} + {showContactForm && ( + + )} + {showResultForm && ( + + )} -