Merge branch 'dev' into 'main'
chore: Restrict image uploads to JPG and PNG formats See merge request frontend/squiz!76
This commit is contained in:
commit
b657ab4ac4
BIN
cypress/e2e/image/Bunner.png
Normal file
BIN
cypress/e2e/image/Bunner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
296
cypress/e2e/quizPublish.cy.ts
Normal file
296
cypress/e2e/quizPublish.cy.ts
Normal file
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -21,6 +21,7 @@
|
|||||||
"@types/react-dnd": "^3.0.2",
|
"@types/react-dnd": "^3.0.2",
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.0.0",
|
||||||
"axios": "^1.5.1",
|
"axios": "^1.5.1",
|
||||||
|
"cypress-file-upload": "^5.0.8",
|
||||||
"cytoscape": "^3.26.0",
|
"cytoscape": "^3.26.0",
|
||||||
"cytoscape-popper": "^2.0.0",
|
"cytoscape-popper": "^2.0.0",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
@ -78,6 +79,6 @@
|
|||||||
"@types/react-beautiful-dnd": "^13.1.4",
|
"@types/react-beautiful-dnd": "^13.1.4",
|
||||||
"@types/react-cytoscapejs": "^1.2.4",
|
"@types/react-cytoscapejs": "^1.2.4",
|
||||||
"craco-alias": "^3.0.1",
|
"craco-alias": "^3.0.1",
|
||||||
"cypress": "^13.4.0"
|
"cypress": "^13.6.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
src/assets/icons/ArrowLeftSP.tsx
Normal file
27
src/assets/icons/ArrowLeftSP.tsx
Normal file
@ -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 (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
height: "14px",
|
||||||
|
width: "19px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
transform: right ? "rotate(180deg)" : undefined
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg width="13" height="11" viewBox="0 0 13 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.12 10.808H5.24L0.855999 5.88L5.24 0.952H8.12L4.648 4.936H12.184V6.824H4.648L8.12 10.808Z" fill={theme.palette.brightPurple.main}/>
|
||||||
|
</svg>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@ -1,40 +1,10 @@
|
|||||||
import { FC, SVGProps } from "react";
|
import { FC, SVGProps } from "react";
|
||||||
|
|
||||||
export const CropIcon: FC = () => (
|
export const CropIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
|
||||||
<svg
|
<svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<path d="M6 6H2.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
width="24"
|
<path d="M6 2.25V18H21.75" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
height="24"
|
<path d="M18 15V6H9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
viewBox="0 0 24 24"
|
<path d="M18 21.75V18" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M6 6H2.25"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M6 2.25V18H21.75"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M18 15V6H9"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M18 21.75V18"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
@ -96,6 +96,8 @@ export type AnyTypedQuizQuestion =
|
|||||||
| QuizQuestionRating
|
| QuizQuestionRating
|
||||||
| QuizQuestionResult;
|
| QuizQuestionResult;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
type FilterQuestionsWithVariants<T> = T extends {
|
type FilterQuestionsWithVariants<T> = T extends {
|
||||||
content: { variants: QuestionVariant[]; };
|
content: { variants: QuestionVariant[]; };
|
||||||
} ? T : never;
|
} ? T : never;
|
||||||
|
@ -40,7 +40,7 @@ export interface QuizConfig {
|
|||||||
theme: string,
|
theme: string,
|
||||||
reply: string,
|
reply: string,
|
||||||
replname: string,
|
replname: string,
|
||||||
}
|
}
|
||||||
startpage: {
|
startpage: {
|
||||||
description: string;
|
description: string;
|
||||||
button: string;
|
button: string;
|
||||||
@ -58,6 +58,16 @@ export interface QuizConfig {
|
|||||||
cycle: boolean;
|
cycle: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
formContact: {
|
||||||
|
title: string;
|
||||||
|
desc: string;
|
||||||
|
name: FCField;
|
||||||
|
email: FCField;
|
||||||
|
phone: FCField;
|
||||||
|
text: FCField;
|
||||||
|
address: FCField;
|
||||||
|
button: string
|
||||||
|
};
|
||||||
info: {
|
info: {
|
||||||
phonenumber: string;
|
phonenumber: string;
|
||||||
clickable: boolean;
|
clickable: boolean;
|
||||||
@ -68,6 +78,14 @@ export interface QuizConfig {
|
|||||||
meta: string;
|
meta: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FCField = {
|
||||||
|
text: string
|
||||||
|
innerText: string
|
||||||
|
key: string
|
||||||
|
required: boolean
|
||||||
|
used: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export const defaultQuizConfig: QuizConfig = {
|
export const defaultQuizConfig: QuizConfig = {
|
||||||
type: null,
|
type: null,
|
||||||
noStartPage: false,
|
noStartPage: false,
|
||||||
@ -81,7 +99,7 @@ export const defaultQuizConfig: QuizConfig = {
|
|||||||
theme: "",
|
theme: "",
|
||||||
reply: "",
|
reply: "",
|
||||||
replname: "",
|
replname: "",
|
||||||
},
|
},
|
||||||
startpage: {
|
startpage: {
|
||||||
description: "",
|
description: "",
|
||||||
button: "",
|
button: "",
|
||||||
@ -106,5 +124,45 @@ export const defaultQuizConfig: QuizConfig = {
|
|||||||
site: "",
|
site: "",
|
||||||
law: "",
|
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: "",
|
meta: "",
|
||||||
};
|
};
|
||||||
|
@ -1,129 +1,122 @@
|
|||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
import { Box, Button, IconButton, Link, Paper, SxProps, TextField, Theme, Typography, useTheme } from "@mui/material";
|
import { Box, Button, IconButton, Link, Paper, SxProps, TextField, Theme, Typography, useTheme, Popover, useMediaQuery, Divider } from "@mui/material";
|
||||||
import { incrementCurrentStep } from "@root/quizes/actions";
|
import { incrementCurrentStep, updateQuiz } from "@root/quizes/actions";
|
||||||
import CustomTextField from "@ui_kit/CustomTextField";
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
import React from "react";
|
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 { OneIcon } from "../../assets/icons/questionsPage/OneIcon";
|
||||||
import AddPlus from "../../assets/icons/questionsPage/addPlus";
|
import AddPlus from "../../assets/icons/questionsPage/addPlus";
|
||||||
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
||||||
|
import { decrementCurrentStep } from "@root/quizes/actions";
|
||||||
import ButtonSettingForms from "./ButtonSettingForms";
|
import ButtonSettingForms from "./ButtonSettingForms";
|
||||||
import DrawerNewField from "./DrawerParent";
|
import DrawerNewField from "./DrawerParent";
|
||||||
import WindowMessengers from "./Massengers/WindowMessengers";
|
import WindowMessengers from "./Massengers/WindowMessengers";
|
||||||
import WindowNewField from "./NewField/WindowNewField";
|
import WindowNewField from "./NewField/WindowNewField";
|
||||||
import SwitchContactForm from "./switchContactForm";
|
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() {
|
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 [drawerMessenger, setDrawerMessenger] = React.useState(false);
|
||||||
|
|
||||||
const drawerNewFieldHC = (bool: boolean) => {
|
const drawerNewFieldHC = (str: string) => {
|
||||||
setDrawerNewField(bool);
|
setDrawerNewField(str);
|
||||||
};
|
};
|
||||||
const drawerMessengerHC = (bool: boolean) => {
|
const drawerMessengerHC = (bool: boolean) => {
|
||||||
setDrawerMessenger(bool);
|
setDrawerMessenger(bool);
|
||||||
};
|
};
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
sx={{
|
||||||
|
p: isTablet ? "0 0 150px 0" : "0"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "10px",
|
||||||
|
m: "67px 0 41px 0",
|
||||||
|
}}>
|
||||||
<Link
|
<Link
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
lineHeight: "19px",
|
lineHeight: "19px",
|
||||||
color: theme.palette.brightPurple.main,
|
color: theme.palette.brightPurple.main,
|
||||||
textDecorationColor: theme.palette.brightPurple.main,
|
textDecorationColor: theme.palette.brightPurple.main,
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Как собрать данные посетителя
|
Как собрать данные посетителя
|
||||||
</Link>{" "}
|
</Link>{" "}
|
||||||
<InfoIcon />
|
{/* <Popover>
|
||||||
|
<Info />
|
||||||
|
</Popover> */}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
<ContactFormParent>
|
|
||||||
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "end" }}>
|
|
||||||
<OneIcon />
|
|
||||||
<IconButton>
|
|
||||||
{" "}
|
|
||||||
<ExpandMoreIcon />{" "}
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", gap: "20px", padding: "10px 20px" }}>
|
|
||||||
<Typography>Имя*</Typography>
|
|
||||||
<CustomTextField placeholder="Дмитрий" text={""} />
|
|
||||||
<Typography>E-mail*</Typography>
|
|
||||||
<CustomTextField placeholder="+7 900 000 00 00" text={""} />
|
|
||||||
<Typography>Телефон*</Typography>
|
|
||||||
<CustomTextField placeholder="+7 900 000 00 00" text={""} />
|
|
||||||
<Button
|
|
||||||
onClick={() => drawerNewFieldHC(true)}
|
|
||||||
variant="contained"
|
|
||||||
sx={{ maxWidth: "fit-content", padding: "10px 20px" }}
|
|
||||||
>
|
|
||||||
Добавить поле +
|
|
||||||
</Button>
|
|
||||||
<DrawerNewField isOpen={drawerNewField} openHC={drawerNewFieldHC}>
|
|
||||||
<WindowNewField />
|
|
||||||
</DrawerNewField>
|
|
||||||
<Link
|
|
||||||
component="button"
|
|
||||||
onClick={() => drawerMessengerHC(true)}
|
|
||||||
sx={{
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "19px",
|
|
||||||
color: theme.palette.brightPurple.main,
|
|
||||||
textDecorationColor: theme.palette.brightPurple.main,
|
|
||||||
textAlign: "left",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Добавить мессенджеры
|
|
||||||
</Link>
|
|
||||||
<DrawerNewField isOpen={drawerMessenger} openHC={drawerMessengerHC}>
|
|
||||||
<WindowMessengers />
|
|
||||||
</DrawerNewField>
|
|
||||||
|
|
||||||
<Button variant="contained" sx={{ padding: "10px 20px", maxWidth: "fit-content" }}>
|
|
||||||
Название кнопки
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</ContactFormParent>
|
|
||||||
|
|
||||||
<ContactFormParent>
|
{
|
||||||
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "end" }}>
|
!quiz?.config.formContact.name.used &&
|
||||||
<OneIcon />
|
!quiz?.config.formContact.email.used &&
|
||||||
<IconButton>
|
!quiz?.config.formContact.phone.used &&
|
||||||
{" "}
|
!quiz?.config.formContact.text.used &&
|
||||||
<ExpandMoreIcon />{" "}
|
!quiz?.config.formContact.address.used ?
|
||||||
</IconButton>
|
<ContactFormParent>
|
||||||
</Box>
|
<EmptyCard drawerNewFieldHC={drawerNewFieldHC} />
|
||||||
<Button variant="contained" sx={{ maxWidth: "fit-content", padding: "10px 20px" }}>
|
</ContactFormParent>
|
||||||
Добавить поле +
|
:
|
||||||
</Button>
|
<ContactFormParent>
|
||||||
<Box sx={{ display: "flex" }}>
|
<ButtonsCard drawerNewFieldHC={drawerNewFieldHC} />
|
||||||
<Typography sx={{ color: theme.palette.orange.main }}>Будут показаны поля по умолчанию</Typography>
|
</ContactFormParent>
|
||||||
<InfoIcon />
|
}
|
||||||
</Box>
|
|
||||||
<Link
|
|
||||||
sx={{
|
|
||||||
mt: "20px",
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "19px",
|
|
||||||
color: theme.palette.brightPurple.main,
|
|
||||||
textDecorationColor: theme.palette.brightPurple.main,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Добавить мессенджеры
|
|
||||||
</Link>
|
|
||||||
<Button variant="contained" sx={{ padding: "10px 20px", maxWidth: "fit-content" }}>
|
|
||||||
Название кнопки
|
|
||||||
</Button>
|
|
||||||
</ContactFormParent>
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "space-between", maxWidth: "796px" }}>
|
<Box sx={{ display: "flex", justifyContent: "space-between", maxWidth: "796px" }}>
|
||||||
<IconButton>
|
|
||||||
<AddPlus />
|
|
||||||
</IconButton>
|
|
||||||
<Box sx={{ display: "flex", gap: "8px" }}>
|
<Box sx={{ display: "flex", gap: "8px" }}>
|
||||||
<Button variant="outlined">
|
<Button
|
||||||
|
onClick={decrementCurrentStep}
|
||||||
|
variant="outlined">
|
||||||
<ArrowLeft />
|
<ArrowLeft />
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="contained" sx={{ padding: "10px 20px" }} onClick={incrementCurrentStep}>
|
<Button variant="contained" sx={{ padding: "10px 20px" }} onClick={incrementCurrentStep}>
|
||||||
@ -131,7 +124,13 @@ export default function ContactFormPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
|
||||||
|
|
||||||
|
<DrawerNewField isOpenDrawer={drawerNewField} drawerNewFieldHC={drawerNewFieldHC}>
|
||||||
|
<WindowNewField type={drawerNewField} drawerNewFieldHC={drawerNewFieldHC} />
|
||||||
|
</DrawerNewField>
|
||||||
|
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,10 +141,8 @@ interface Props {
|
|||||||
|
|
||||||
function ContactFormParent({ outerContainerSx: sx, children }: Props) {
|
function ContactFormParent({ outerContainerSx: sx, children }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [switchState, setSwitchState] = React.useState("setting");
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
|
||||||
const SSHC = (data: string) => {
|
const quiz = useCurrentQuiz()
|
||||||
setSwitchState(data);
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
sx={{
|
sx={{
|
||||||
@ -154,23 +151,36 @@ function ContactFormParent({ outerContainerSx: sx, children }: Props) {
|
|||||||
borderRadius: "12px",
|
borderRadius: "12px",
|
||||||
margin: "20px 0",
|
margin: "20px 0",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ width: "100%", display: "flex" }}>
|
<Box sx={{ width: "100%", display: "flex", flexDirection: isTablet ? "column" : "row", }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
borderRight: `1px solid ${theme.palette.grey2.main}`,
|
// borderRight: isTablet ? "none" : `1px solid ${theme.palette.grey2.main}`,
|
||||||
maxWidth: "386px",
|
maxWidth: "386px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
padding: "113px 20px 20px 20px",
|
padding: "100px 20px 20px 20px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: "20px",
|
gap: "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomTextField placeholder="Заголовок формы" text={""} />
|
<CustomTextField
|
||||||
|
onChange={({ target }) => {
|
||||||
|
updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.formContact.title = target.value
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
value={quiz.config.formContact.title}
|
||||||
|
placeholder="Заголовок формы" text={""} />
|
||||||
<TextField
|
<TextField
|
||||||
|
|
||||||
|
onChange={({ target }) => {
|
||||||
|
updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.formContact.desc = target.value
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
value={quiz.config.formContact.desc}
|
||||||
id="outlined-multiline-static"
|
id="outlined-multiline-static"
|
||||||
multiline
|
multiline
|
||||||
rows={8}
|
rows={8}
|
||||||
@ -182,18 +192,216 @@ function ContactFormParent({ outerContainerSx: sx, children }: Props) {
|
|||||||
borderRadius: "10px",
|
borderRadius: "10px",
|
||||||
alignItems: "start",
|
alignItems: "start",
|
||||||
color: theme.palette.grey2.main,
|
color: theme.palette.grey2.main,
|
||||||
|
color:"black",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", padding: "20px", width: "100%", gap: "20px" }}>
|
<Divider sx={{
|
||||||
{children}
|
height: isTablet ? "1px" : "80%",
|
||||||
</Box>
|
width: isTablet ? "80%" : "1px",
|
||||||
</Box>
|
margin: "auto"
|
||||||
<Box>
|
}}
|
||||||
<ButtonSettingForms switchState={switchState} SSHC={SSHC} />
|
orientation={isTablet ? "horisontal" : "vertical"}
|
||||||
<SwitchContactForm switchState={switchState} />
|
/>
|
||||||
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const SettingField = ({ name, placeholder, type, drawerNewFieldHC }: { name: string, placeholder: string, type: string; drawerNewFieldHC: any }) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const quiz = useCurrentQuiz()
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography>{quiz.config.formContact[type].text || name}</Typography>
|
||||||
|
<Box
|
||||||
|
sx={{ display: "flex", mb: "10px" }}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
color: quiz.config.formContact[type].innerText ? "black" : "#9A9AAF",
|
||||||
|
fontSize: "20px",
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
height: "48px",
|
||||||
|
borderRadius: "10px",
|
||||||
|
border: "1px #9A9AAF solid",
|
||||||
|
lineHeight: "21px",
|
||||||
|
p: " 0 25px 0 14px ",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
width: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
position: "relative"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{quiz.config.formContact[type].innerText || placeholder}
|
||||||
|
<IconButton
|
||||||
|
onClick={() => drawerNewFieldHC(type)}
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
right: "0"
|
||||||
|
}}>
|
||||||
|
<GearIcon height="20px" width="20px" color="#7e2aea" />
|
||||||
|
</IconButton>
|
||||||
|
</Typography>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => updateQuiz(quiz?.id, (quiz) => {
|
||||||
|
quiz.config.formContact[type].used = false
|
||||||
|
})}
|
||||||
|
sx={{
|
||||||
|
width: "48px",
|
||||||
|
ml: "5px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash />
|
||||||
|
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ButtonsCard = ({ drawerNewFieldHC }: any) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const quiz = useCurrentQuiz()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", padding: "20px", width: "100%", gap: "20px" }}>
|
||||||
|
|
||||||
|
{
|
||||||
|
buttons.map((contentData) => {
|
||||||
|
const content = quiz?.config.formContact[contentData.key]
|
||||||
|
return content.used ? <SettingField drawerNewFieldHC={drawerNewFieldHC} key={contentData.key} type={contentData.key} name={content.text || contentData.name} placeholder={content.innerText || contentData.desc} /> : <></>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
(
|
||||||
|
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
|
||||||
|
) ?
|
||||||
|
<></>
|
||||||
|
:
|
||||||
|
<Button
|
||||||
|
onClick={() => drawerNewFieldHC("all")}
|
||||||
|
variant="contained"
|
||||||
|
sx={{ maxWidth: "fit-content", padding: "10px 20px" }}
|
||||||
|
>
|
||||||
|
Добавить поле +
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
<Link
|
||||||
|
component="button"
|
||||||
|
// onClick={() => drawerMessengerHC(true)}
|
||||||
|
sx={{
|
||||||
|
fontSize: "16px",
|
||||||
|
lineHeight: "19px",
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
textDecorationColor: theme.palette.brightPurple.main,
|
||||||
|
textAlign: "left",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Добавить мессенджеры
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<PseudoButton />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
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 (
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", padding: "100px 20px 20px 20px", width: "100%", gap: "20px", }}>
|
||||||
|
<Button
|
||||||
|
onClick={() => drawerNewFieldHC("all")}
|
||||||
|
variant="contained" sx={{ maxWidth: "fit-content", padding: "10px 20px" }}>
|
||||||
|
Добавить поле +
|
||||||
|
</Button>
|
||||||
|
<Box sx={{ display: "flex" }}>
|
||||||
|
<Typography sx={{ color: theme.palette.orange.main }}>Будут показаны поля по умолчанию</Typography>
|
||||||
|
<Box ref={popover}>
|
||||||
|
<Info sx={{ ml: "6px", p: "0" }} onClick={openFC} />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Popover
|
||||||
|
open={FC}
|
||||||
|
onClose={closeFC}
|
||||||
|
anchorEl={popover.current}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: 'bottom',
|
||||||
|
horizontal: 'center',
|
||||||
|
}}
|
||||||
|
transformOrigin={{
|
||||||
|
vertical: 'top',
|
||||||
|
horizontal: 'center',
|
||||||
|
}}
|
||||||
|
disableRestoreFocus
|
||||||
|
>
|
||||||
|
<Typography sx={{ m: "20px", textAlign: "center" }}>
|
||||||
|
Если вам не нужно собирать контакты, <br></br> отключите форму контактов
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
</Popover>
|
||||||
|
</Box>
|
||||||
|
<Link
|
||||||
|
sx={{
|
||||||
|
mt: "20px",
|
||||||
|
fontSize: "16px",
|
||||||
|
lineHeight: "19px",
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
textDecorationColor: theme.palette.brightPurple.main,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Добавить мессенджеры
|
||||||
|
</Link>
|
||||||
|
<PseudoButton />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const PseudoButton = () => {
|
||||||
|
const quiz = useCurrentQuiz()
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
onChange={({ target }) => {
|
||||||
|
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 || "Название кнопки"}
|
||||||
|
</TextField>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
@ -7,18 +7,18 @@ import {SxProps, Theme} from "@mui/material";
|
|||||||
interface Props {
|
interface Props {
|
||||||
outerContainerSx?: SxProps<Theme>;
|
outerContainerSx?: SxProps<Theme>;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
isOpen: boolean;
|
isOpenDrawer: string;
|
||||||
openHC: (arg0: boolean) => void
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Drawer
|
<Drawer
|
||||||
anchor='right'
|
anchor='right'
|
||||||
open={isOpen}
|
open={Boolean(isOpen)}
|
||||||
onClose={() => openHC(false)}
|
onClose={() => drawerNewFieldHC("")}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{ width: 450 }}
|
sx={{ width: 450 }}
|
||||||
|
@ -6,14 +6,19 @@ import EmailIcon from "@icons/ContactFormIcon/EmailIcon";
|
|||||||
import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon";
|
import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon";
|
||||||
import TextIcon from "@icons/ContactFormIcon/TextIcon";
|
import TextIcon from "@icons/ContactFormIcon/TextIcon";
|
||||||
import AddressIcon from "@icons/ContactFormIcon/AddressIcon";
|
import AddressIcon from "@icons/ContactFormIcon/AddressIcon";
|
||||||
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
switchState: string
|
switchState: string
|
||||||
SSHC: (data:string) => void
|
SSHC: (data:string) => void
|
||||||
|
type: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ButtonsNewField ({SSHC, switchState}:Props) {
|
export default function ButtonsNewField ({SSHC, switchState, type}:Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const quiz = useCurrentQuiz()
|
||||||
|
console.log(quiz)
|
||||||
|
console.log(type)
|
||||||
const buttonSetting: {icon: JSX.Element; title: string; value: string} [] =[
|
const buttonSetting: {icon: JSX.Element; title: string; value: string} [] =[
|
||||||
{icon: <NameIcon color={switchState === 'name' ? '#ffffff' : theme.palette.grey3.main}/>, title: 'Имя', value: 'name'},
|
{icon: <NameIcon color={switchState === 'name' ? '#ffffff' : theme.palette.grey3.main}/>, title: 'Имя', value: 'name'},
|
||||||
{icon: <EmailIcon color={switchState === 'email' ? '#ffffff' : theme.palette.grey3.main}/>, title: 'Email', value: 'email'},
|
{icon: <EmailIcon color={switchState === 'email' ? '#ffffff' : theme.palette.grey3.main}/>, title: 'Email', value: 'email'},
|
||||||
@ -31,16 +36,20 @@ export default function ButtonsNewField ({SSHC, switchState}:Props) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{buttonSetting.map( (e,i) => (
|
{buttonSetting.map( (e,i) => (
|
||||||
|
type === e.value || type === "all" ?
|
||||||
<MiniButtonSetting
|
<MiniButtonSetting
|
||||||
|
disabled = {quiz?.config.formContact[e.value]?.used}
|
||||||
key={i}
|
key={i}
|
||||||
onClick={()=>{SSHC(e.value)}}
|
onClick={()=>{SSHC(e.value)}}
|
||||||
sx={{backgroundColor: switchState === e.value ? theme.palette.brightPurple.main : 'transparent',
|
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.icon}
|
||||||
{e.title}
|
{e.title}
|
||||||
</MiniButtonSetting>
|
</MiniButtonSetting>
|
||||||
|
:
|
||||||
|
<></>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import Box from "@mui/material/Box";
|
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 CustomTextField from "@ui_kit/CustomTextField";
|
||||||
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
import { updateQuiz } from "@root/quizes/actions";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
outerContainerSx?: SxProps<Theme>;
|
outerContainerSx?: SxProps<Theme>;
|
||||||
@ -11,51 +13,88 @@ interface Props {
|
|||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
placeholderHelp: string;
|
placeholderHelp: string;
|
||||||
placeholderField: string;
|
placeholderField: string;
|
||||||
|
drawerNewFieldHC:(a:string)=>void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NewFieldParent ({defaultValue, placeholderHelp, placeholderField, outerContainerSx: sx, children}: Props) {
|
export default function NewFieldParent({ drawerNewFieldHC, defaultValue, placeholderHelp, placeholderField, outerContainerSx: sx, children }: Props) {
|
||||||
return(
|
const quiz = useCurrentQuiz()
|
||||||
<Box sx={{padding: '20px', display: 'flex', flexDirection: 'column', gap: '20px'}}>
|
console.log({ defaultValue, placeholderHelp, placeholderField, outerContainerSx: sx, children })
|
||||||
<Box sx={{display: 'flex', flexDirection: 'column', gap: '15px'}}>
|
return (
|
||||||
<Typography>Подсказка</Typography>
|
<Box sx={{ padding: '20px', display: 'flex', flexDirection: 'column', gap: '20px' }}>
|
||||||
<CustomTextField placeholder={placeholderHelp} text={''}/>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
|
||||||
|
<Typography
|
||||||
|
>Подсказка</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
onChange={({ target }) => {
|
||||||
|
updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.formContact[defaultValue].text = target.value
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
value={quiz.config.formContact[defaultValue].text}
|
||||||
|
|
||||||
|
placeholder={placeholderHelp} text={''} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{display: 'flex', flexDirection: 'column', gap: '15px'}}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
|
||||||
<Typography>Подсказка внутри поля</Typography>
|
<Typography
|
||||||
<CustomTextField placeholder={placeholderField} text={''}/>
|
>Подсказка внутри поля</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
onChange={({ target }) => {
|
||||||
|
updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.formContact[defaultValue].innerText = target.value
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
value={quiz.config.formContact[defaultValue].innerText}
|
||||||
|
|
||||||
|
placeholder={placeholderField} text={''} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{display: 'flex', flexDirection: 'column', gap: '15px'}}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
|
||||||
|
|
||||||
<Typography>Ключ</Typography>
|
<Typography>Ключ</Typography>
|
||||||
<FormControl
|
<TextField
|
||||||
fullWidth
|
value={quiz.config.formContact[defaultValue].key}
|
||||||
variant="standard"
|
onChange={({ target }) => {
|
||||||
sx={{ p: 0, border: 0 }}
|
updateQuiz(quiz.id, (quiz) => {
|
||||||
>
|
quiz.config.formContact[defaultValue].key = target.value
|
||||||
<TextField
|
})
|
||||||
disabled
|
}}
|
||||||
id="outlined-disabled"
|
placeholder="text"
|
||||||
value={defaultValue}
|
sx={{
|
||||||
sx={{
|
"& .css-1d3z3hw-MuiOutlinedInput-notchedOutline": {
|
||||||
"& .css-1d3z3hw-MuiOutlinedInput-notchedOutline": {
|
border: 'none'
|
||||||
border: 'none'
|
},
|
||||||
},
|
"& .MuiInputBase-root": {
|
||||||
"& .MuiInputBase-root": {
|
height: "48px",
|
||||||
height: "48px",
|
borderRadius: "10px",
|
||||||
borderRadius: "10px",
|
backgroundColor: '#EEE4FC',
|
||||||
backgroundColor: '#EEE4FC',
|
},
|
||||||
},
|
}}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
<CustomCheckbox
|
||||||
<CustomCheckbox label={"Обязательно к заполнению"}/>
|
checked={quiz.config.formContact[defaultValue].required}
|
||||||
|
handleChange={({ target }) => {
|
||||||
|
console.log("click")
|
||||||
|
updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.formContact[defaultValue].required = target.checked
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
|
||||||
|
label={"Обязательно к заполнению"} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
{/* <Box>
|
||||||
<Typography>Запрашивать на</Typography>
|
<Typography>Запрашивать на</Typography>
|
||||||
<CustomCheckbox label={'Шаг 1'}/>
|
<CustomCheckbox label={'Шаг 1'}/>
|
||||||
<CustomCheckbox label={'Шаг 2'}/>
|
<CustomCheckbox label={'Шаг 2'}/>
|
||||||
</Box>
|
</Box> */}
|
||||||
{children}
|
{children}
|
||||||
<Button variant='contained' sx={{maxWidth: '125px'}}>Добавить</Button>
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.formContact[defaultValue].used = true
|
||||||
|
})
|
||||||
|
drawerNewFieldHC("")
|
||||||
|
}}
|
||||||
|
variant='contained' sx={{ maxWidth: '125px' }}>Добавить</Button>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -8,27 +8,29 @@ import CustomizedSwitch from "@ui_kit/CustomSwitch";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
switchState: string,
|
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);
|
const [SwitchMask, setSwitchMask] = React.useState(false);
|
||||||
|
console.log(switchState)
|
||||||
const SwitchMaskHC = (bool:boolean) => {
|
const SwitchMaskHC = (bool:boolean) => {
|
||||||
setSwitchMask(bool)
|
setSwitchMask(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (switchState) {
|
switch (switchState) {
|
||||||
case 'name':
|
case 'name':
|
||||||
return (<NewFieldParent placeholderHelp={'Введите имя'} placeholderField={'Дмитрий'} defaultValue={'name'}/>);
|
return (<NewFieldParent drawerNewFieldHC={drawerNewFieldHC} placeholderHelp={'Введите имя'} placeholderField={'Дмитрий'} defaultValue={'name'}/>);
|
||||||
break;
|
break;
|
||||||
case 'email':
|
case 'email':
|
||||||
return (<NewFieldParent placeholderHelp={'Введите Email'} placeholderField={'mail@example.ru'} defaultValue={'email'}/>);
|
return (<NewFieldParent drawerNewFieldHC={drawerNewFieldHC} placeholderHelp={'Введите Email'} placeholderField={'mail@example.ru'} defaultValue={'email'}/>);
|
||||||
break;
|
break;
|
||||||
case 'phone':
|
case 'phone':
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NewFieldParent placeholderHelp={'Введите номер'} placeholderField={'+7 900 000 00 00'} defaultValue={'phone'}>
|
<NewFieldParent drawerNewFieldHC={drawerNewFieldHC} placeholderHelp={'Введите номер'} placeholderField={'+7 900 000 00 00'} defaultValue={'phone'}>
|
||||||
<FormControlLabel
|
{/* <FormControlLabel
|
||||||
value="start"
|
value="start"
|
||||||
control={<CustomizedSwitch/>}
|
control={<CustomizedSwitch/>}
|
||||||
label="Маска для телефона"
|
label="Маска для телефона"
|
||||||
@ -41,7 +43,7 @@ export default function SwitchNewField({switchState ='name'}: Props) {
|
|||||||
SwitchMaskHC(!SwitchMask)
|
SwitchMaskHC(!SwitchMask)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
/>
|
/> */}
|
||||||
|
|
||||||
{SwitchMask ?
|
{SwitchMask ?
|
||||||
<SelectMask/>
|
<SelectMask/>
|
||||||
@ -52,10 +54,10 @@ export default function SwitchNewField({switchState ='name'}: Props) {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'text':
|
case 'text':
|
||||||
return (<NewFieldParent placeholderHelp={'Введите фамилию'} placeholderField={'Иванов'} defaultValue={'text'}/>);
|
return (<NewFieldParent drawerNewFieldHC={drawerNewFieldHC} placeholderHelp={'Введите фамилию'} placeholderField={'Иванов'} defaultValue={'text'}/>);
|
||||||
break;
|
break;
|
||||||
case 'address':
|
case 'address':
|
||||||
return (<NewFieldParent placeholderHelp={'Введите адрес'} placeholderField={'Москва, Лаврушинский пер., 10'} defaultValue={'address'}/>);
|
return (<NewFieldParent drawerNewFieldHC={drawerNewFieldHC} placeholderHelp={'Введите адрес'} placeholderField={'Москва, Лаврушинский пер., 10'} defaultValue={'address'}/>);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return (<></>)
|
return (<></>)
|
||||||
|
@ -1,29 +1,39 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Button from '@mui/material/Button';
|
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 CloseIcon from '@mui/icons-material/Close';
|
||||||
import ButtonsNewField from "./ButtonsNewField";
|
import ButtonsNewField from "./ButtonsNewField";
|
||||||
import SwitchNewField from "./SwitchNewField";
|
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 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) => {
|
const SSHC = (data: string) => {
|
||||||
setSwitchState(data)
|
setSwitchState(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
return(
|
|
||||||
<>
|
<>
|
||||||
<Box sx={{padding: '10px 10px 10px 20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: theme.palette.background.default}}>
|
<Box sx={{ padding: '10px 10px 10px 20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: theme.palette.background.default }}>
|
||||||
<Typography>Новое поле</Typography>
|
<Typography>Новое поле</Typography>
|
||||||
<Button sx={{padding: 0}}><CloseIcon fontSize='large'/></Button>
|
<Button onClick={() => drawerNewFieldHC("")} sx={{ padding: 0 }}><CloseIcon fontSize='large' /></Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box><ButtonsNewField switchState={switchState} SSHC={SSHC}/></Box>
|
<Box><ButtonsNewField type={type} switchState={switchState} SSHC={SSHC} /></Box>
|
||||||
<SwitchNewField switchState={switchState}/>
|
<SwitchNewField switchState={switchState} drawerNewFieldHC={drawerNewFieldHC} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -104,10 +104,7 @@ export default function InstallQuiz() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||||
<LinkIcon
|
<LinkIcon color={theme.palette.brightPurple.main} bgcolor={"#EEE4FC"} />
|
||||||
color={theme.palette.brightPurple.main}
|
|
||||||
bgcolor={"#EEE4FC"}
|
|
||||||
/>
|
|
||||||
<Typography>Ссылка на квиз</Typography>
|
<Typography>Ссылка на квиз</Typography>
|
||||||
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@ -242,11 +239,7 @@ export default function InstallQuiz() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<CopyIcon
|
<CopyIcon color={theme.palette.brightPurple.main} bgcolor={"#EEE4FC"} marL={"10px"} />
|
||||||
color={theme.palette.brightPurple.main}
|
|
||||||
bgcolor={"#EEE4FC"}
|
|
||||||
marL={"10px"}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -274,16 +267,11 @@ export default function InstallQuiz() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||||
<VkIcon
|
<VkIcon color={theme.palette.brightPurple.main} bgcolor={"#EEE4FC"} />
|
||||||
color={theme.palette.brightPurple.main}
|
|
||||||
bgcolor={"#EEE4FC"}
|
|
||||||
/>
|
|
||||||
<Typography> Вконтакте</Typography>
|
<Typography> Вконтакте</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Typography>
|
<Typography>Для публикации сниппета на стене группы, призывающего пройти тест.</Typography>
|
||||||
Для публикации сниппета на стене группы, призывающего пройти тест.
|
|
||||||
</Typography>
|
|
||||||
<Link
|
<Link
|
||||||
component="button"
|
component="button"
|
||||||
onClick={handleOpenVk}
|
onClick={handleOpenVk}
|
||||||
@ -312,16 +300,10 @@ export default function InstallQuiz() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||||
<DomenIcon
|
<DomenIcon color={theme.palette.brightPurple.main} bgcolor={"#EEE4FC"} />
|
||||||
color={theme.palette.brightPurple.main}
|
|
||||||
bgcolor={"#EEE4FC"}
|
|
||||||
/>
|
|
||||||
<Typography>Свой домен</Typography>
|
<Typography>Свой домен</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography>
|
<Typography>Подключите свой домен, если хотите, чтобы квиз открывался по вашей ссылке.</Typography>
|
||||||
Подключите свой домен, если хотите, чтобы квиз открывался по вашей
|
|
||||||
ссылке.
|
|
||||||
</Typography>
|
|
||||||
<Link
|
<Link
|
||||||
component="button"
|
component="button"
|
||||||
onClick={handleOpenDom}
|
onClick={handleOpenDom}
|
||||||
@ -397,16 +379,13 @@ export default function InstallQuiz() {
|
|||||||
<AutoOpenInstall />
|
<AutoOpenInstall />
|
||||||
<VidjetInstall />
|
<VidjetInstall />
|
||||||
<InstallQzCode />
|
<InstallQzCode />
|
||||||
<Box
|
<Box sx={{ display: "flex", gap: "8px", justifyContent: "end", mt: "30px" }}>
|
||||||
sx={{ display: "flex", gap: "8px", justifyContent: "end", mt: "30px" }}
|
<Button variant="outlined" sx={{ padding: "10px 20px", borderRadius: "8px" }}>
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
sx={{ padding: "10px 20px", borderRadius: "8px" }}
|
|
||||||
>
|
|
||||||
<ArrowLeft />
|
<ArrowLeft />
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="contained" onClick={incrementCurrentStep}>Запустить рекламу</Button>
|
<Button variant="contained" onClick={incrementCurrentStep}>
|
||||||
|
Запустить рекламу
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@ -438,9 +417,7 @@ export default function InstallQuiz() {
|
|||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
<Typography sx={{ color: theme.palette.grey2.main }}>Добавить квиз в группу ВК</Typography>
|
||||||
Добавить квиз в группу ВК
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -473,19 +450,12 @@ export default function InstallQuiz() {
|
|||||||
Добавить приложение
|
Добавить приложение
|
||||||
</Button>
|
</Button>
|
||||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||||
Для публикации сниппета на стене, призывающего пройти тест,
|
Для публикации сниппета на стене, призывающего пройти тест, вставьте в новую запись ссылку на приложение
|
||||||
вставьте в новую запись ссылку на приложение
|
|
||||||
</Typography>
|
</Typography>
|
||||||
<CustomTextField
|
<CustomTextField placeholder={""} text={"https://vk.com/app6656524_-XXXXXXXXXXX"} />
|
||||||
placeholder={""}
|
<Typography sx={{ fontSize: "14px", color: theme.palette.grey2.main }}>
|
||||||
text={"https://vk.com/app6656524_-XXXXXXXXXXX"}
|
где XXXXXXXXXXX - id вашего сообщества (полный адрес ссылки можно узнать в браузерной строке, открыв
|
||||||
/>
|
приложение в вашей группе
|
||||||
<Typography
|
|
||||||
sx={{ fontSize: "14px", color: theme.palette.grey2.main }}
|
|
||||||
>
|
|
||||||
где XXXXXXXXXXX - id вашего сообщества (полный адрес ссылки
|
|
||||||
можно узнать в браузерной строке, открыв приложение в вашей
|
|
||||||
группе
|
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -496,24 +466,13 @@ export default function InstallQuiz() {
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>
|
<Typography>2. Откройте квиз в группе (вы должны быть администратором группы)</Typography>
|
||||||
2. Откройте квиз в группе (вы должны быть администратором
|
<Typography sx={{ fontSize: "14px", color: theme.palette.grey2.main }}>
|
||||||
группы)
|
Справа снизу нажмите на значок "редактировать" В появившемся окне введите id квиза и нажмите
|
||||||
|
"Привязать". Готово! Квиз привязан к группе
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography sx={{ color: theme.palette.grey2.main }}>ID этого квиза</Typography>
|
||||||
sx={{ fontSize: "14px", color: theme.palette.grey2.main }}
|
<CustomTextField placeholder={""} text={"639727c5177be5004f11a0f2"} />
|
||||||
>
|
|
||||||
Справа снизу нажмите на значок "редактировать" В появившемся
|
|
||||||
окне введите id квиза и нажмите "Привязать". Готово! Квиз
|
|
||||||
привязан к группе
|
|
||||||
</Typography>
|
|
||||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
|
||||||
ID этого квиза
|
|
||||||
</Typography>
|
|
||||||
<CustomTextField
|
|
||||||
placeholder={""}
|
|
||||||
text={"639727c5177be5004f11a0f2"}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "end", gap: "10px" }}>
|
<Box sx={{ display: "flex", justifyContent: "end", gap: "10px" }}>
|
||||||
@ -555,9 +514,7 @@ export default function InstallQuiz() {
|
|||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
<Typography sx={{ color: theme.palette.grey2.main }}>Подключить свой домен</Typography>
|
||||||
Подключить свой домен
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -575,21 +532,14 @@ export default function InstallQuiz() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||||
Подключите домен к проекту, чтобы создать несколько квизов на
|
Подключите домен к проекту, чтобы создать несколько квизов на одном домене
|
||||||
одном домене
|
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>1. Настройте записи в регистраторе домена</Typography>
|
<Typography>1. Настройте записи в регистраторе домена</Typography>
|
||||||
<Box sx={{ display: "flex" }}>
|
<Box sx={{ display: "flex" }}>
|
||||||
<SelectableButton
|
<SelectableButton isSelected={backgroundType === "text"} onClick={() => setBackgroundType("text")}>
|
||||||
isSelected={backgroundType === "text"}
|
|
||||||
onClick={() => setBackgroundType("text")}
|
|
||||||
>
|
|
||||||
Для поддоменов
|
Для поддоменов
|
||||||
</SelectableButton>
|
</SelectableButton>
|
||||||
<SelectableButton
|
<SelectableButton isSelected={backgroundType === "video"} onClick={() => setBackgroundType("video")}>
|
||||||
isSelected={backgroundType === "video"}
|
|
||||||
onClick={() => setBackgroundType("video")}
|
|
||||||
>
|
|
||||||
Для доменов
|
Для доменов
|
||||||
</SelectableButton>
|
</SelectableButton>
|
||||||
</Box>
|
</Box>
|
||||||
@ -623,8 +573,7 @@ export default function InstallQuiz() {
|
|||||||
maxWidth: "372px",
|
maxWidth: "372px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Как подключить свой домен/поддомен к квизу? Ошибки при
|
Как подключить свой домен/поддомен к квизу? Ошибки при подключении домена
|
||||||
подключении домена
|
|
||||||
</Link>
|
</Link>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -655,10 +604,7 @@ export default function InstallQuiz() {
|
|||||||
// onMouseDown={}
|
// onMouseDown={}
|
||||||
edge="end"
|
edge="end"
|
||||||
>
|
>
|
||||||
<CopyIcon
|
<CopyIcon color={"#ffffff"} bgcolor={theme.palette.brightPurple.main} />
|
||||||
color={"#ffffff"}
|
|
||||||
bgcolor={theme.palette.brightPurple.main}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
@ -668,11 +614,8 @@ export default function InstallQuiz() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "end", gap: "10px" }}>
|
<Box sx={{ display: "flex", justifyContent: "end", gap: "10px" }}>
|
||||||
<Typography
|
<Typography sx={{ fontSize: "14px", color: theme.palette.grey2.main }}>
|
||||||
sx={{ fontSize: "14px", color: theme.palette.grey2.main }}
|
Привязка домена и обновление DNS записей может занять до 48 часов
|
||||||
>
|
|
||||||
Привязка домена и обновление DNS записей может занять до 48
|
|
||||||
часов
|
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button variant="outlined" onClick={handleCloseDom}>
|
<Button variant="outlined" onClick={handleCloseDom}>
|
||||||
Отмена
|
Отмена
|
||||||
|
@ -18,7 +18,7 @@ export default function Component() {
|
|||||||
const [select, setSelect] = React.useState(0);
|
const [select, setSelect] = React.useState(0);
|
||||||
const userId = useUserStore((state) => state.userId);
|
const userId = useUserStore((state) => state.userId);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation()
|
const location = useLocation();
|
||||||
|
|
||||||
const onClick = () => (userId ? navigate("/list") : navigate("/signin"));
|
const onClick = () => (userId ? navigate("/list") : navigate("/signin"));
|
||||||
|
|
||||||
@ -41,7 +41,9 @@ export default function Component() {
|
|||||||
padding: 0,
|
padding: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<QuizLogo width={isMobile ? 100 : 150} />
|
<Link to="/">
|
||||||
|
<QuizLogo width={isMobile ? 100 : 150} />
|
||||||
|
</Link>
|
||||||
{/*<Box*/}
|
{/*<Box*/}
|
||||||
{/* sx={{*/}
|
{/* sx={{*/}
|
||||||
{/* maxWidth: '595px',*/}
|
{/* maxWidth: '595px',*/}
|
||||||
@ -67,7 +69,7 @@ export default function Component() {
|
|||||||
{/* ))}*/}
|
{/* ))}*/}
|
||||||
{/*</Box>*/}
|
{/*</Box>*/}
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
sx={{
|
sx={{
|
||||||
color: "black",
|
color: "black",
|
||||||
|
@ -4,17 +4,17 @@ import {} from "react-router-dom";
|
|||||||
import { useLocation, Link } from "react-router-dom";
|
import { useLocation, Link } from "react-router-dom";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
ListItem,
|
ListItem,
|
||||||
AppBar,
|
AppBar,
|
||||||
List,
|
List,
|
||||||
Toolbar,
|
Toolbar,
|
||||||
IconButton,
|
IconButton,
|
||||||
Box,
|
Box,
|
||||||
Slide,
|
Slide,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { TransitionProps } from "@mui/material/transitions";
|
import { TransitionProps } from "@mui/material/transitions";
|
||||||
|
|
||||||
@ -38,212 +38,202 @@ import Logotip from "./images/icons/QuizLogo";
|
|||||||
// ];
|
// ];
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
theme?: "dark" | "light";
|
theme?: "dark" | "light";
|
||||||
bgColor?: string;
|
bgColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Transition = React.forwardRef(function Transition(
|
const Transition = React.forwardRef(function Transition(
|
||||||
props: TransitionProps & {
|
props: TransitionProps & {
|
||||||
children: React.ReactElement;
|
children: React.ReactElement;
|
||||||
},
|
},
|
||||||
|
|
||||||
ref: React.Ref<unknown>
|
ref: React.Ref<unknown>
|
||||||
) {
|
) {
|
||||||
return <Slide direction={"left"} ref={ref} {...props} />;
|
return <Slide direction={"left"} ref={ref} {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
const height = "80px";
|
const height = "80px";
|
||||||
export default function FullScreenDialog({
|
export default function FullScreenDialog({ theme = "dark", bgColor = "#F2F3F7" }: Props) {
|
||||||
theme = "dark",
|
const [open, setOpen] = useState(false);
|
||||||
bgColor = "#F2F3F7",
|
const location = useLocation();
|
||||||
}: Props) {
|
const themeMUI = useTheme();
|
||||||
const [open, setOpen] = useState(false);
|
const isMobile = useMediaQuery(themeMUI.breakpoints.down("md"));
|
||||||
const location = useLocation();
|
|
||||||
const themeMUI = useTheme();
|
|
||||||
const isMobile = useMediaQuery(themeMUI.breakpoints.down("md"));
|
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
const handleClickOpen = () => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SectionStyled
|
<SectionStyled
|
||||||
tag="header"
|
tag="header"
|
||||||
bg={bgColor}
|
bg={bgColor}
|
||||||
mwidth={"1200px"}
|
mwidth={"1200px"}
|
||||||
sxsect={{
|
sxsect={{
|
||||||
minHeight: isMobile? "50px" : {height},
|
minHeight: isMobile ? "50px" : { height },
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
zIndex: 11
|
zIndex: 11,
|
||||||
|
}}
|
||||||
|
sxcont={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
svg: { color: "#000000" },
|
||||||
|
padding: isMobile ? 0 : "0 22px 0 40px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ bgcolor: "none", paddingTop: isMobile ? "6px" : 0 }}>
|
||||||
|
<Link to="/">
|
||||||
|
<Logotip width={150} />
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
{!isMobile && (
|
||||||
|
<Button
|
||||||
|
// onClick={() => setIsContactFormOpen(true)}
|
||||||
|
disableRipple
|
||||||
|
sx={{
|
||||||
|
color: "black",
|
||||||
|
border: "1px solid black",
|
||||||
|
ml: "auto",
|
||||||
|
mr: "38px",
|
||||||
|
textTransform: "none",
|
||||||
|
fontWeight: "400",
|
||||||
|
fontSize: "18px",
|
||||||
|
lineHeight: "24px",
|
||||||
|
borderRadius: "8px",
|
||||||
|
padding: "8px 17px",
|
||||||
|
"&:hover": {
|
||||||
|
background: "rgba(126, 42, 234, 0.07)",
|
||||||
|
bgcolor: "#7E2AEA",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Предрегистрация
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
}}
|
<Button disableRipple variant="text" onClick={handleClickOpen}>
|
||||||
sxcont={{
|
<MenuIcon />
|
||||||
display: "flex",
|
</Button>
|
||||||
justifyContent: "space-between",
|
<Dialog
|
||||||
alignItems: "center",
|
fullScreen
|
||||||
svg: { color: "#000000" },
|
sx={{ width: isMobile ? "100%" : "320px", ml: "auto", height: "100%" }}
|
||||||
padding: isMobile ? 0 : "0 22px 0 40px"
|
open={open}
|
||||||
}}
|
onClose={handleClose}
|
||||||
|
TransitionComponent={Transition}
|
||||||
|
>
|
||||||
|
<AppBar
|
||||||
|
sx={{
|
||||||
|
position: "relative",
|
||||||
|
background: theme === "dark" ? "#252734" : "#F2F3F7",
|
||||||
|
boxShadow: "none",
|
||||||
|
height: isMobile ? "66px" : "100px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Toolbar
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
svg: { color: theme === "dark" ? "#ffffff" : "#000000" },
|
||||||
|
pt: "12px",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ bgcolor: "none", paddingTop: isMobile? "6px" : 0 }}>
|
{isMobile && <Logotip width={150} />}
|
||||||
<Logotip width={150}/>
|
<IconButton sx={{ ml: "auto" }} edge="start" color="inherit" onClick={handleClose} aria-label="close">
|
||||||
</Box>
|
<CloseIcon />
|
||||||
{!isMobile && (
|
</IconButton>
|
||||||
<Button
|
</Toolbar>
|
||||||
// onClick={() => setIsContactFormOpen(true)}
|
</AppBar>
|
||||||
disableRipple
|
<List
|
||||||
sx={{
|
sx={{
|
||||||
color: "black",
|
background: theme === "dark" ? "#252734" : "#F2F3F7",
|
||||||
border: "1px solid black",
|
height: "100vh",
|
||||||
ml: "auto",
|
p: "0",
|
||||||
mr: "38px",
|
}}
|
||||||
textTransform: "none",
|
>
|
||||||
fontWeight: "400",
|
{/*<ListItem*/}
|
||||||
fontSize: "18px",
|
{/* sx={{*/}
|
||||||
lineHeight: "24px",
|
{/* pl: "40px",*/}
|
||||||
borderRadius: "8px",
|
{/* flexDirection: "column",*/}
|
||||||
padding: "8px 17px",
|
{/* alignItems: isMobile ? "stretch" : "end",*/}
|
||||||
"&:hover": {
|
{/* }}*/}
|
||||||
background: "rgba(126, 42, 234, 0.07)",
|
{/*>*/}
|
||||||
bgcolor: "#7E2AEA",
|
{/* {buttonMenu.map(({ path, title }) => (*/}
|
||||||
},
|
{/* <Link*/}
|
||||||
}}
|
{/* key={path}*/}
|
||||||
>
|
{/* to={path}*/}
|
||||||
Предрегистрация
|
{/* style={{*/}
|
||||||
</Button>
|
{/* textDecoration: "none",*/}
|
||||||
)}
|
{/* height: "20px",*/}
|
||||||
|
{/* marginBottom: "25px",*/}
|
||||||
<Button disableRipple variant="text" onClick={handleClickOpen}>
|
{/* }}*/}
|
||||||
<MenuIcon />
|
{/* >*/}
|
||||||
</Button>
|
{/* <Button*/}
|
||||||
<Dialog
|
{/* disableRipple*/}
|
||||||
fullScreen
|
{/* variant="text"*/}
|
||||||
sx={{ width: isMobile ? "100%" : "320px", ml: "auto", height: "100%" }}
|
{/* sx={{*/}
|
||||||
open={open}
|
{/* color:*/}
|
||||||
onClose={handleClose}
|
{/* location.pathname === path*/}
|
||||||
TransitionComponent={Transition}
|
{/* ? "#7E2AEA"*/}
|
||||||
>
|
{/* : theme === "dark"*/}
|
||||||
<AppBar
|
{/* ? "white"*/}
|
||||||
sx={{
|
{/* : "black",*/}
|
||||||
position: "relative",
|
{/* height: "20px",*/}
|
||||||
background: theme === "dark" ? "#252734" : "#F2F3F7",
|
{/* textTransform: "none",*/}
|
||||||
boxShadow: "none",
|
{/* fontSize: "16px",*/}
|
||||||
height: isMobile ? "66px" : "100px",
|
{/* "&:hover": {*/}
|
||||||
}}
|
{/* background: "none",*/}
|
||||||
>
|
{/* color: "#7E2AEA",*/}
|
||||||
<Toolbar
|
{/* },*/}
|
||||||
sx={{
|
{/* }}*/}
|
||||||
display: "flex",
|
{/* >*/}
|
||||||
justifyContent: "space-between",
|
{/* {title}*/}
|
||||||
svg: { color: theme === "dark" ? "#ffffff" : "#000000" },
|
{/* </Button>*/}
|
||||||
pt: "12px",
|
{/* </Link>*/}
|
||||||
}}
|
{/* ))}*/}
|
||||||
>
|
{/*</ListItem>*/}
|
||||||
{isMobile && (
|
{isMobile ? (
|
||||||
<Logotip width={150}/>
|
<Button
|
||||||
)}
|
// onClick={() => setIsContactFormOpen(true)}
|
||||||
<IconButton
|
variant="outlined"
|
||||||
sx={{ ml: "auto" }}
|
sx={{
|
||||||
edge="start"
|
position: "absolute",
|
||||||
color="inherit"
|
bottom: 0,
|
||||||
onClick={handleClose}
|
mb: "60px",
|
||||||
aria-label="close"
|
width: "188px",
|
||||||
>
|
color: theme === "dark" ? "white" : "black",
|
||||||
<CloseIcon />
|
border: "1px solid black",
|
||||||
</IconButton>
|
ml: "40px",
|
||||||
</Toolbar>
|
mt: "180px",
|
||||||
</AppBar>
|
textTransform: "none",
|
||||||
<List
|
fontWeight: "400",
|
||||||
sx={{
|
fontSize: "18px",
|
||||||
background: theme === "dark" ? "#252734" : "#F2F3F7",
|
lineHeight: "24px",
|
||||||
height: "100vh",
|
borderRadius: "8px",
|
||||||
p: "0",
|
padding: "8px 17px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/*<ListItem*/}
|
Предрегистрация
|
||||||
{/* sx={{*/}
|
</Button>
|
||||||
{/* pl: "40px",*/}
|
) : (
|
||||||
{/* flexDirection: "column",*/}
|
<Box
|
||||||
{/* alignItems: isMobile ? "stretch" : "end",*/}
|
sx={{
|
||||||
{/* }}*/}
|
position: "absolute",
|
||||||
{/*>*/}
|
right: "40px",
|
||||||
{/* {buttonMenu.map(({ path, title }) => (*/}
|
bottom: "60px",
|
||||||
{/* <Link*/}
|
}}
|
||||||
{/* key={path}*/}
|
>
|
||||||
{/* to={path}*/}
|
<Logotip width={150} />
|
||||||
{/* style={{*/}
|
</Box>
|
||||||
{/* textDecoration: "none",*/}
|
)}
|
||||||
{/* height: "20px",*/}
|
</List>
|
||||||
{/* marginBottom: "25px",*/}
|
</Dialog>
|
||||||
{/* }}*/}
|
</SectionStyled>
|
||||||
{/* >*/}
|
<Box sx={{ height: isMobile ? "50px" : { height } }} />
|
||||||
{/* <Button*/}
|
</>
|
||||||
{/* disableRipple*/}
|
);
|
||||||
{/* variant="text"*/}
|
|
||||||
{/* sx={{*/}
|
|
||||||
{/* color:*/}
|
|
||||||
{/* location.pathname === path*/}
|
|
||||||
{/* ? "#7E2AEA"*/}
|
|
||||||
{/* : theme === "dark"*/}
|
|
||||||
{/* ? "white"*/}
|
|
||||||
{/* : "black",*/}
|
|
||||||
{/* height: "20px",*/}
|
|
||||||
{/* textTransform: "none",*/}
|
|
||||||
{/* fontSize: "16px",*/}
|
|
||||||
{/* "&:hover": {*/}
|
|
||||||
{/* background: "none",*/}
|
|
||||||
{/* color: "#7E2AEA",*/}
|
|
||||||
{/* },*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/* >*/}
|
|
||||||
{/* {title}*/}
|
|
||||||
{/* </Button>*/}
|
|
||||||
{/* </Link>*/}
|
|
||||||
{/* ))}*/}
|
|
||||||
{/*</ListItem>*/}
|
|
||||||
{isMobile ? (
|
|
||||||
<Button
|
|
||||||
// onClick={() => setIsContactFormOpen(true)}
|
|
||||||
variant="outlined"
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
bottom: 0,
|
|
||||||
mb: "60px",
|
|
||||||
width: "188px",
|
|
||||||
color: theme === "dark" ? "white" : "black",
|
|
||||||
border: "1px solid black",
|
|
||||||
ml: "40px",
|
|
||||||
mt: "180px",
|
|
||||||
textTransform: "none",
|
|
||||||
fontWeight: "400",
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
padding: "8px 17px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Предрегистрация
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
right: "40px",
|
|
||||||
bottom: "60px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Logotip width={150}/>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</List>
|
|
||||||
</Dialog>
|
|
||||||
</SectionStyled>
|
|
||||||
<Box sx={{height: isMobile ? "50px" : {height}}} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
|
|||||||
import { updateRootContentId } from "@root/quizes/actions"
|
import { updateRootContentId } from "@root/quizes/actions"
|
||||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
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 { updateOpenedModalSettingsId, } from "@root/uiTools/actions";
|
||||||
import { cleardragQuestionContentId } from "@root/uiTools/actions";
|
import { cleardragQuestionContentId } from "@root/uiTools/actions";
|
||||||
import { withErrorBoundary } from "react-error-boundary";
|
import { withErrorBoundary } from "react-error-boundary";
|
||||||
@ -161,7 +161,6 @@ function CsComponent({
|
|||||||
|
|
||||||
const addNode = ({ parentNodeContentId, targetNodeContentId }: { parentNodeContentId: string, targetNodeContentId?: string }) => {
|
const addNode = ({ parentNodeContentId, targetNodeContentId }: { parentNodeContentId: string, targetNodeContentId?: string }) => {
|
||||||
|
|
||||||
|
|
||||||
//запрещаем работу родителя-ребенка если это один и тот же вопрос
|
//запрещаем работу родителя-ребенка если это один и тот же вопрос
|
||||||
if (parentNodeContentId === targetNodeContentId) return
|
if (parentNodeContentId === targetNodeContentId) return
|
||||||
|
|
||||||
@ -170,10 +169,10 @@ function CsComponent({
|
|||||||
const parentNodeChildren = cy?.$('edge[source = "' + parentNodeContentId + '"]')?.length
|
const parentNodeChildren = cy?.$('edge[source = "' + parentNodeContentId + '"]')?.length
|
||||||
//если есть инфо о выбранном вопросе из модалки - берём родителя из инфо модалки. Иначе из значения дропа
|
//если есть инфо о выбранном вопросе из модалки - берём родителя из инфо модалки. Иначе из значения дропа
|
||||||
const targetQuestion = { ...getQuestionByContentId(targetNodeContentId || dragQuestionContentId) } as AnyTypedQuizQuestion
|
const targetQuestion = { ...getQuestionByContentId(targetNodeContentId || dragQuestionContentId) } as AnyTypedQuizQuestion
|
||||||
|
if (Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) {
|
||||||
if (Object.keys(targetQuestion).length !== 0 && Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) {
|
|
||||||
clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren })
|
clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren })
|
||||||
cy?.data('changed', true)
|
cy?.data('changed', true)
|
||||||
|
createFrontResult(quiz.backendId, targetQuestion.content.id)
|
||||||
const es = cy?.add([
|
const es = cy?.add([
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
@ -203,6 +202,7 @@ function CsComponent({
|
|||||||
//смотрим не добавлен ли родителю result. Если да - убираем его. Веточкам result не нужен
|
//смотрим не добавлен ли родителю result. Если да - убираем его. Веточкам result не нужен
|
||||||
trashQuestions.forEach((targetQuestion) => {
|
trashQuestions.forEach((targetQuestion) => {
|
||||||
if (targetQuestion.type === "result" && targetQuestion.content.rule.parentId === parentQuestion.content.id) {
|
if (targetQuestion.type === "result" && targetQuestion.content.rule.parentId === parentQuestion.content.id) {
|
||||||
|
console.log('deleteQ', targetQuestion.id)
|
||||||
deleteQuestion(targetQuestion.id);
|
deleteQuestion(targetQuestion.id);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -271,7 +271,8 @@ function CsComponent({
|
|||||||
|
|
||||||
const parentQuestionContentId = cy?.$('edge[target = "' + targetNodeContentId + '"]')?.toArray()?.[0]?.data()?.source
|
const parentQuestionContentId = cy?.$('edge[target = "' + targetNodeContentId + '"]')?.toArray()?.[0]?.data()?.source
|
||||||
if (targetNodeContentId && parentQuestionContentId) {
|
if (targetNodeContentId && parentQuestionContentId) {
|
||||||
|
if (cy?.edges(`[source="${parentQuestionContentId}"]`).length === 0)
|
||||||
|
createFrontResult(quiz.backendId, parentQuestionContentId)
|
||||||
clearDataAfterRemoveNode({ targetQuestionContentId: targetNodeContentId, parentQuestionContentId })
|
clearDataAfterRemoveNode({ targetQuestionContentId: targetNodeContentId, parentQuestionContentId })
|
||||||
cy?.remove(cy?.$('#' + targetNodeContentId)).layout(lyopts).run()
|
cy?.remove(cy?.$('#' + targetNodeContentId)).layout(lyopts).run()
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,7 @@ export default function ButtonsOptionsAndPict({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: isMobile ? "wrap" : "nowrap",
|
flexWrap: isMobile ? "wrap" : "nowrap",
|
||||||
gap: "6px",
|
gap: "6px",
|
||||||
|
maxWidth: isMobile ? "200px" : undefined
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MiniButtonSetting
|
<MiniButtonSetting
|
||||||
|
@ -18,18 +18,28 @@ import RatingIcon from "@icons/questionsPage/rating";
|
|||||||
import Slider from "@icons/questionsPage/slider";
|
import Slider from "@icons/questionsPage/slider";
|
||||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
Paper,
|
Paper,
|
||||||
TextField,
|
TextField,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { copyQuestion, createUntypedQuestion, deleteQuestion, clearRuleForAll, toggleExpandQuestion, updateQuestion, updateUntypedQuestion, getQuestionByContentId, deleteQuestionWithTimeout } from "@root/questions/actions";
|
import {
|
||||||
|
copyQuestion,
|
||||||
|
createUntypedQuestion,
|
||||||
|
deleteQuestion,
|
||||||
|
clearRuleForAll,
|
||||||
|
toggleExpandQuestion,
|
||||||
|
updateQuestion,
|
||||||
|
updateUntypedQuestion,
|
||||||
|
getQuestionByContentId,
|
||||||
|
deleteQuestionWithTimeout,
|
||||||
|
} from "@root/questions/actions";
|
||||||
import { updateRootContentId } from "@root/quizes/actions";
|
import { updateRootContentId } from "@root/quizes/actions";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
|
import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
|
||||||
@ -44,445 +54,387 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
|
|||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
||||||
draggableProps: DraggableProvidedDragHandleProps | null | undefined;
|
draggableProps: DraggableProvidedDragHandleProps | null | undefined;
|
||||||
isDragging: boolean;
|
isDragging: boolean;
|
||||||
index: number;
|
index: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function QuestionsPageCard({ question, draggableProps, isDragging, index }: Props) {
|
export default function QuestionsPageCard({ question, draggableProps, isDragging, index }: Props) {
|
||||||
const { questions } = useQuestionsStore();
|
const { questions } = useQuestionsStore();
|
||||||
const [plusVisible, setPlusVisible] = useState<boolean>(false);
|
const [plusVisible, setPlusVisible] = useState<boolean>(false);
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||||
const anchorRef = useRef(null);
|
const anchorRef = useRef(null);
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
|
||||||
const setTitle = useDebouncedCallback((title) => {
|
const setTitle = useDebouncedCallback((title) => {
|
||||||
const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion;
|
const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion;
|
||||||
|
|
||||||
updateQuestionFn(question.id, question => {
|
updateQuestionFn(question.id, (question) => {
|
||||||
question.title = title;
|
question.title = title;
|
||||||
});
|
});
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Paper
|
<Paper
|
||||||
id={question.id}
|
id={question.id}
|
||||||
data-cy="quiz-question-card"
|
data-cy="quiz-question-card"
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "796px",
|
maxWidth: "796px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
borderRadius: "12px",
|
borderRadius: "12px",
|
||||||
backgroundColor: question.expanded ? "white" : "#EEE4FC",
|
backgroundColor: question.expanded ? "white" : "#EEE4FC",
|
||||||
border: question.expanded ? "none" : "1px solid #9A9AAF",
|
border: question.expanded ? "none" : "1px solid #9A9AAF",
|
||||||
boxShadow: "0px 10px 30px #e7e7e7",
|
boxShadow: "0px 10px 30px #e7e7e7",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
padding: isMobile ? "10px" : "20px 10px 20px 20px",
|
padding: isMobile ? "10px" : "20px 10px 20px 20px",
|
||||||
flexDirection: isMobile ? "column" : null,
|
flexDirection: isMobile ? "column" : null,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormControl
|
<FormControl
|
||||||
variant="standard"
|
variant="standard"
|
||||||
sx={{
|
sx={{
|
||||||
p: 0,
|
p: 0,
|
||||||
maxWidth: isTablet ? "549px" : "640px",
|
maxWidth: isTablet ? "549px" : "640px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
marginRight: isMobile ? "0px" : "16.1px",
|
marginRight: isMobile ? "0px" : "16.1px",
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
defaultValue={question.title}
|
||||||
|
placeholder={"Заголовок вопроса"}
|
||||||
|
onChange={({ target }: { target: HTMLInputElement }) => setTitle(target.value || " ")}
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<Box>
|
||||||
|
<InputAdornment
|
||||||
|
ref={anchorRef}
|
||||||
|
position="start"
|
||||||
|
sx={{ cursor: "pointer" }}
|
||||||
|
onClick={() => setOpen((isOpened) => !isOpened)}
|
||||||
>
|
>
|
||||||
<TextField
|
{IconAndrom(question.expanded, question.type)}
|
||||||
defaultValue={question.title}
|
</InputAdornment>
|
||||||
placeholder={"Заголовок вопроса"}
|
<ChooseAnswerModal
|
||||||
onChange={({ target }: { target: HTMLInputElement; }) => setTitle(target.value || " ")}
|
open={open}
|
||||||
InputProps={{
|
onClose={() => setOpen(false)}
|
||||||
startAdornment: (
|
anchorRef={anchorRef}
|
||||||
<Box>
|
question={question}
|
||||||
<InputAdornment
|
questionType={question.type}
|
||||||
ref={anchorRef}
|
|
||||||
position="start"
|
|
||||||
sx={{ cursor: "pointer" }}
|
|
||||||
onClick={() => setOpen((isOpened) => !isOpened)}
|
|
||||||
>
|
|
||||||
{IconAndrom(question.expanded, question.type)}
|
|
||||||
</InputAdornment>
|
|
||||||
<ChooseAnswerModal
|
|
||||||
open={open}
|
|
||||||
onClose={() => setOpen(false)}
|
|
||||||
anchorRef={anchorRef}
|
|
||||||
question={question}
|
|
||||||
questionType={question.type}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
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",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
width: isMobile ? "100%" : "auto",
|
|
||||||
position: "relative",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
sx={{ padding: "0", margin: "5px" }}
|
|
||||||
disableRipple
|
|
||||||
data-cy="expand-question"
|
|
||||||
onClick={() => toggleExpandQuestion(question.id)}
|
|
||||||
>
|
|
||||||
{question.expanded ? (
|
|
||||||
<ArrowDownIcon
|
|
||||||
style={{
|
|
||||||
width: "18px",
|
|
||||||
color: "#4D4D4D",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ExpandLessIcon
|
|
||||||
sx={{
|
|
||||||
boxSizing: "border-box",
|
|
||||||
fill: theme.palette.brightPurple.main,
|
|
||||||
background: "#FFF",
|
|
||||||
borderRadius: "6px",
|
|
||||||
height: "30px",
|
|
||||||
width: "30px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</IconButton>
|
|
||||||
{question.expanded ? (
|
|
||||||
<></>
|
|
||||||
) : (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
height: "30px",
|
|
||||||
borderRight: "solid 1px #4D4D4D",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
icon={
|
|
||||||
<HideIcon
|
|
||||||
style={{
|
|
||||||
boxSizing: "border-box",
|
|
||||||
color: "#7E2AEA",
|
|
||||||
background: "#FFF",
|
|
||||||
borderRadius: "6px",
|
|
||||||
height: "30px",
|
|
||||||
width: "30px",
|
|
||||||
padding: "3px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={<CrossedEyeIcon />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={""}
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.grey2.main,
|
|
||||||
ml: "-9px",
|
|
||||||
mr: 0,
|
|
||||||
userSelect: "none",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
sx={{ padding: "0" }}
|
|
||||||
onClick={() => copyQuestion(question.id, question.quizId)}
|
|
||||||
>
|
|
||||||
<CopyIcon
|
|
||||||
style={{ color: theme.palette.brightPurple.main }}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
borderRadius: "6px",
|
|
||||||
padding: "0",
|
|
||||||
margin: "0 5px 0 10px",
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
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"
|
|
||||||
>
|
|
||||||
<DeleteIcon
|
|
||||||
style={{ color: theme.palette.brightPurple.main }}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{question.type !== null &&
|
|
||||||
<Box
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
height: "30px",
|
|
||||||
width: "30px",
|
|
||||||
marginLeft: "3px",
|
|
||||||
borderRadius: "50%",
|
|
||||||
fontSize: "16px",
|
|
||||||
color: question.expanded
|
|
||||||
? theme.palette.brightPurple.main
|
|
||||||
: "#FFF",
|
|
||||||
background: question.expanded
|
|
||||||
? "#EEE4FC"
|
|
||||||
: theme.palette.brightPurple.main,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{question.page + 1}
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
<IconButton
|
|
||||||
disableRipple
|
|
||||||
sx={{
|
|
||||||
padding: isMobile ? "0" : "0 5px",
|
|
||||||
right: isMobile ? "0" : null,
|
|
||||||
bottom: isMobile ? "0" : null,
|
|
||||||
}}
|
|
||||||
{...draggableProps}
|
|
||||||
>
|
|
||||||
<PointsIcon style={{ color: "#4D4D4D", fontSize: "30px" }} />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
{question.expanded && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
padding: 0,
|
|
||||||
borderRadius: "12px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{question.type === null ? (
|
|
||||||
<TypeQuestions question={question} />
|
|
||||||
) : (
|
|
||||||
<SwitchQuestionsPage question={question} />
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Paper>
|
|
||||||
<Box
|
|
||||||
onMouseEnter={() => setPlusVisible(true)}
|
|
||||||
onMouseLeave={() => setPlusVisible(false)}
|
|
||||||
sx={{
|
|
||||||
maxWidth: "825px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "40px",
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
onClick={() => createUntypedQuestion(question.quizId, question.id)}
|
|
||||||
sx={{
|
|
||||||
display: plusVisible && !isDragging ? "flex" : "none",
|
|
||||||
width: "100%",
|
|
||||||
alignItems: "center",
|
|
||||||
columnGap: "10px",
|
|
||||||
}}
|
|
||||||
data-cy="create-question"
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
boxSizing: "border-box",
|
|
||||||
width: "100%",
|
|
||||||
height: "1px",
|
|
||||||
backgroundPosition: "bottom",
|
|
||||||
backgroundRepeat: "repeat-x",
|
|
||||||
backgroundSize: "20px 1px",
|
|
||||||
backgroundImage:
|
|
||||||
"radial-gradient(circle, #7E2AEA 6px, #F2F3F7 1px)",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<PlusIcon />
|
</Box>
|
||||||
</Box>
|
),
|
||||||
</Box>
|
}}
|
||||||
</>
|
sx={{
|
||||||
);
|
margin: isMobile ? "10px 0" : 0,
|
||||||
|
"& .MuiInputBase-root": {
|
||||||
|
color: "#000000",
|
||||||
|
backgroundColor: question.expanded ? theme.palette.background.default : "transparent",
|
||||||
|
height: "48px",
|
||||||
|
borderRadius: "10px",
|
||||||
|
".MuiOutlinedInput-notchedOutline": {
|
||||||
|
borderWidth: "1px !important",
|
||||||
|
border: !question.expanded ? "none" : null,
|
||||||
|
},
|
||||||
|
"& .MuiInputBase-input::placeholder": {
|
||||||
|
color: "#4D4D4D",
|
||||||
|
opacity: 0.8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
inputProps={{
|
||||||
|
sx: {
|
||||||
|
fontSize: "18px",
|
||||||
|
lineHeight: "21px",
|
||||||
|
py: 0,
|
||||||
|
paddingLeft: question.type === null ? 0 : "18px",
|
||||||
|
},
|
||||||
|
"data-cy": "quiz-question-title",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
width: isMobile ? "100%" : "auto",
|
||||||
|
position: "relative",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
sx={{ padding: "0", margin: "5px" }}
|
||||||
|
disableRipple
|
||||||
|
data-cy="expand-question"
|
||||||
|
onClick={() => toggleExpandQuestion(question.id)}
|
||||||
|
>
|
||||||
|
{question.expanded ? (
|
||||||
|
<ArrowDownIcon
|
||||||
|
style={{
|
||||||
|
width: "18px",
|
||||||
|
color: "#4D4D4D",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ExpandLessIcon
|
||||||
|
sx={{
|
||||||
|
boxSizing: "border-box",
|
||||||
|
fill: theme.palette.brightPurple.main,
|
||||||
|
background: "#FFF",
|
||||||
|
borderRadius: "6px",
|
||||||
|
height: "30px",
|
||||||
|
width: "30px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
{question.expanded ? (
|
||||||
|
<></>
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
height: "30px",
|
||||||
|
borderRight: "solid 1px #4D4D4D",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
icon={
|
||||||
|
<HideIcon
|
||||||
|
style={{
|
||||||
|
boxSizing: "border-box",
|
||||||
|
color: "#7E2AEA",
|
||||||
|
background: "#FFF",
|
||||||
|
borderRadius: "6px",
|
||||||
|
height: "30px",
|
||||||
|
width: "30px",
|
||||||
|
padding: "3px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
checkedIcon={<CrossedEyeIcon />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={""}
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.grey2.main,
|
||||||
|
ml: "-9px",
|
||||||
|
mr: 0,
|
||||||
|
userSelect: "none",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<IconButton sx={{ padding: "0" }} onClick={() => copyQuestion(question.id, question.quizId)}>
|
||||||
|
<CopyIcon style={{ color: theme.palette.brightPurple.main }} />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
borderRadius: "6px",
|
||||||
|
padding: "0",
|
||||||
|
margin: "0 5px 0 10px",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<DeleteIcon style={{ color: theme.palette.brightPurple.main }} />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{question.type !== null && (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
height: "30px",
|
||||||
|
width: "30px",
|
||||||
|
marginLeft: "3px",
|
||||||
|
borderRadius: "50%",
|
||||||
|
fontSize: "16px",
|
||||||
|
color: question.expanded ? theme.palette.brightPurple.main : "#FFF",
|
||||||
|
background: question.expanded ? "#EEE4FC" : theme.palette.brightPurple.main,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{question.page + 1}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
disableRipple
|
||||||
|
sx={{
|
||||||
|
padding: isMobile ? "0" : "0 5px",
|
||||||
|
right: isMobile ? "0" : null,
|
||||||
|
bottom: isMobile ? "0" : null,
|
||||||
|
}}
|
||||||
|
{...draggableProps}
|
||||||
|
>
|
||||||
|
<PointsIcon style={{ color: "#4D4D4D", fontSize: "30px" }} />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
{question.expanded && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
padding: 0,
|
||||||
|
borderRadius: "12px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{question.type === null ? (
|
||||||
|
<TypeQuestions question={question} />
|
||||||
|
) : (
|
||||||
|
<SwitchQuestionsPage question={question} />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Paper>
|
||||||
|
<Box
|
||||||
|
onMouseEnter={() => setPlusVisible(true)}
|
||||||
|
onMouseLeave={() => setPlusVisible(false)}
|
||||||
|
sx={{
|
||||||
|
maxWidth: "825px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "40px",
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
onClick={() => createUntypedQuestion(question.quizId, question.id)}
|
||||||
|
sx={{
|
||||||
|
display: plusVisible && !isDragging ? "flex" : "none",
|
||||||
|
width: "100%",
|
||||||
|
alignItems: "center",
|
||||||
|
columnGap: "10px",
|
||||||
|
}}
|
||||||
|
data-cy="create-question"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
boxSizing: "border-box",
|
||||||
|
width: "100%",
|
||||||
|
height: "1px",
|
||||||
|
backgroundPosition: "bottom",
|
||||||
|
backgroundRepeat: "repeat-x",
|
||||||
|
backgroundSize: "20px 1px",
|
||||||
|
backgroundImage: "radial-gradient(circle, #7E2AEA 6px, #F2F3F7 1px)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<PlusIcon />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const IconAndrom = (isExpanded: boolean, questionType: QuestionType | null) => {
|
const IconAndrom = (isExpanded: boolean, questionType: QuestionType | null) => {
|
||||||
switch (questionType) {
|
switch (questionType) {
|
||||||
case "variant":
|
case "variant":
|
||||||
return (
|
return <Answer color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
<Answer
|
case "images":
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
return <OptionsPict color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
sx={{ height: "22px", width: "20px" }}
|
case "varimg":
|
||||||
/>
|
return <OptionsAndPict color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
);
|
case "emoji":
|
||||||
case "images":
|
return <Emoji color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
return (
|
case "text":
|
||||||
<OptionsPict
|
return <Input color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
case "select":
|
||||||
sx={{ height: "22px", width: "20px" }}
|
return <DropDown color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
/>
|
case "date":
|
||||||
);
|
return <Date color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
case "varimg":
|
case "number":
|
||||||
return (
|
return <Slider color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
<OptionsAndPict
|
case "file":
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
return <Download color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
sx={{ height: "22px", width: "20px" }}
|
case "page":
|
||||||
/>
|
return <Page color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
);
|
case "rating":
|
||||||
case "emoji":
|
return <RatingIcon color={isExpanded ? "#9A9AAF" : "#7E2AEA"} sx={{ height: "22px", width: "20px" }} />;
|
||||||
return (
|
default:
|
||||||
<Emoji
|
return <></>;
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
}
|
||||||
sx={{ height: "22px", width: "20px" }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case "text":
|
|
||||||
return (
|
|
||||||
<Input
|
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
|
||||||
sx={{ height: "22px", width: "20px" }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case "select":
|
|
||||||
return (
|
|
||||||
<DropDown
|
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
|
||||||
sx={{ height: "22px", width: "20px" }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case "date":
|
|
||||||
return (
|
|
||||||
<Date
|
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
|
||||||
sx={{ height: "22px", width: "20px" }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case "number":
|
|
||||||
return (
|
|
||||||
<Slider
|
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
|
||||||
sx={{ height: "22px", width: "20px" }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case "file":
|
|
||||||
return (
|
|
||||||
<Download
|
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
|
||||||
sx={{ height: "22px", width: "20px" }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case "page":
|
|
||||||
return (
|
|
||||||
<Page
|
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
|
||||||
sx={{ height: "22px", width: "20px" }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case "rating":
|
|
||||||
return (
|
|
||||||
<RatingIcon
|
|
||||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
|
||||||
sx={{ height: "22px", width: "20px" }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -5,80 +5,77 @@ import { Draggable } from "react-beautiful-dnd";
|
|||||||
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "../../../../model/questionTypes/shared";
|
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "../../../../model/questionTypes/shared";
|
||||||
import QuestionsPageCard from "./QuestionPageCard";
|
import QuestionsPageCard from "./QuestionPageCard";
|
||||||
|
|
||||||
|
|
||||||
type FormDraggableListItemProps = {
|
type FormDraggableListItemProps = {
|
||||||
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
||||||
questionIndex: number;
|
questionIndex: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(
|
export default memo(({ question, questionIndex }: FormDraggableListItemProps) => {
|
||||||
({ question, questionIndex }: FormDraggableListItemProps) => {
|
const theme = useTheme();
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Draggable draggableId={String(questionIndex)} index={questionIndex}>
|
<Draggable draggableId={String(questionIndex)} index={questionIndex}>
|
||||||
{(provided) => (
|
{(provided) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
{...(questionIndex !== 0 ? provided.draggableProps : {})}
|
{...(questionIndex !== 0 ? provided.draggableProps : {})}
|
||||||
sx={{ userSelect: "none", padding: 0 }}
|
sx={{ userSelect: "none", padding: 0 }}
|
||||||
>
|
>
|
||||||
{question.deleted ? (
|
{question.deleted ? (
|
||||||
<Box
|
<Box
|
||||||
{...provided.dragHandleProps}
|
{...provided.dragHandleProps}
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
maxWidth: "800px",
|
maxWidth: "800px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
marginTop: "30px",
|
marginTop: "30px",
|
||||||
gap: "5px",
|
gap: "5px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
color: theme.palette.grey2.main,
|
color: theme.palette.grey2.main,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Вопрос удалён.
|
Вопрос удалён.
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
updateQuestion(question.id, question => {
|
updateQuestion(question.id, (question) => {
|
||||||
question.deleted = false;
|
question.deleted = false;
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
textDecoration: "underline",
|
textDecoration: "underline",
|
||||||
color: theme.palette.brightPurple.main,
|
color: theme.palette.brightPurple.main,
|
||||||
textDecorationColor: theme.palette.brightPurple.main,
|
textDecorationColor: theme.palette.brightPurple.main,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Восстановить?
|
Восстановить?
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
borderBottom: "1px solid rgba(0, 0, 0, 0.23)",
|
borderBottom: "1px solid rgba(0, 0, 0, 0.23)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<QuestionsPageCard
|
<QuestionsPageCard
|
||||||
key={questionIndex}
|
key={questionIndex}
|
||||||
question={question}
|
question={question}
|
||||||
questionIndex={questionIndex}
|
questionIndex={questionIndex}
|
||||||
draggableProps={provided.dragHandleProps}
|
draggableProps={provided.dragHandleProps}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
</Draggable>
|
</Draggable>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
@ -11,8 +11,8 @@ import OptionsPict from "@icons/questionsPage/options_pict";
|
|||||||
import Page from "@icons/questionsPage/page";
|
import Page from "@icons/questionsPage/page";
|
||||||
import RatingIcon from "@icons/questionsPage/rating";
|
import RatingIcon from "@icons/questionsPage/rating";
|
||||||
import Slider from "@icons/questionsPage/slider";
|
import Slider from "@icons/questionsPage/slider";
|
||||||
import { Box, InputAdornment, Paper } from "@mui/material";
|
import { Box, FormControlLabel, IconButton, InputAdornment, Paper, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import { updateQuestion, updateUntypedQuestion } from "@root/questions/actions";
|
import { toggleExpandQuestion, updateQuestion, updateUntypedQuestion } from "@root/questions/actions";
|
||||||
import CustomTextField from "@ui_kit/CustomTextField";
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
|
import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
|
||||||
@ -22,142 +22,206 @@ import SwitchQuestionsPage from "../../SwitchQuestionsPage";
|
|||||||
import { ChooseAnswerModal } from "./ChooseAnswerModal";
|
import { ChooseAnswerModal } from "./ChooseAnswerModal";
|
||||||
import FormTypeQuestions from "../FormTypeQuestions";
|
import FormTypeQuestions from "../FormTypeQuestions";
|
||||||
import { QuestionType } from "@model/question/question";
|
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 {
|
interface Props {
|
||||||
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
||||||
questionIndex: number;
|
questionIndex: number;
|
||||||
draggableProps: DraggableProvidedDragHandleProps | null | undefined;
|
draggableProps: DraggableProvidedDragHandleProps | null | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function QuestionsPageCard({
|
export default function QuestionsPageCard({ question, questionIndex, draggableProps }: Props) {
|
||||||
question,
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
questionIndex,
|
const anchorRef = useRef(null);
|
||||||
draggableProps,
|
const theme = useTheme();
|
||||||
}: Props) {
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||||
const anchorRef = useRef(null);
|
|
||||||
|
|
||||||
const setTitle = useDebouncedCallback((title) => {
|
const setTitle = useDebouncedCallback((title) => {
|
||||||
const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion;
|
const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion;
|
||||||
|
|
||||||
updateQuestionFn(question.id, question => {
|
updateQuestionFn(question.id, (question) => {
|
||||||
question.title = title;
|
question.title = title;
|
||||||
});
|
});
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Paper
|
<Paper
|
||||||
sx={{
|
sx={{
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
maxWidth: "796px",
|
maxWidth: "796px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
border: "none",
|
border: "none",
|
||||||
boxShadow: "none",
|
boxShadow: "none",
|
||||||
paddingBottom: "20px",
|
paddingBottom: "20px",
|
||||||
borderRadius: "0",
|
borderRadius: "0",
|
||||||
borderTopLeftRadius: "12px",
|
borderTopLeftRadius: "12px",
|
||||||
borderTopRightRadius: "12px",
|
borderTopRightRadius: "12px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
p: 0,
|
||||||
padding: 0,
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomTextField
|
<Box
|
||||||
placeholder={`Заголовок ${questionIndex + 1} вопроса`}
|
sx={{
|
||||||
text={question.title}
|
display: "flex",
|
||||||
onChange={({ target }) => setTitle(target.value)}
|
alignItems: "center",
|
||||||
sx={{ margin: "20px", width: "auto" }}
|
margin: "20px",
|
||||||
InputProps={{
|
gap: "18px",
|
||||||
startAdornment: (
|
flexDirection: isMobile ? "column-reverse" : null,
|
||||||
<Box>
|
}}
|
||||||
<InputAdornment
|
>
|
||||||
ref={anchorRef}
|
<CustomTextField
|
||||||
position="start"
|
placeholder={`Заголовок ${questionIndex + 1} вопроса`}
|
||||||
sx={{ cursor: "pointer" }}
|
text={question.title}
|
||||||
onClick={() => setOpen((isOpened) => !isOpened)}
|
onChange={({ target }) => setTitle(target.value)}
|
||||||
>
|
sx={{ width: "100%" }}
|
||||||
{IconAndrom(question.type)}
|
InputProps={{
|
||||||
</InputAdornment>
|
startAdornment: (
|
||||||
<ChooseAnswerModal
|
<Box>
|
||||||
open={open}
|
<InputAdornment
|
||||||
onClose={() => setOpen(false)}
|
ref={anchorRef}
|
||||||
anchorRef={anchorRef}
|
position="start"
|
||||||
question={question}
|
sx={{ cursor: "pointer" }}
|
||||||
questionType={question.type}
|
onClick={() => setOpen((isOpened) => !isOpened)}
|
||||||
/>
|
>
|
||||||
</Box>
|
{IconAndrom(question.type)}
|
||||||
),
|
</InputAdornment>
|
||||||
endAdornment: (
|
<ChooseAnswerModal
|
||||||
<Box {...draggableProps}>
|
open={open}
|
||||||
{questionIndex !== 0 && (
|
onClose={() => setOpen(false)}
|
||||||
<InputAdornment position="start">
|
anchorRef={anchorRef}
|
||||||
<PointsIcon
|
question={question}
|
||||||
style={{ color: "#9A9AAF", fontSize: "30px" }}
|
questionType={question.type}
|
||||||
/>
|
|
||||||
</InputAdornment>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
{question.type === null ? (
|
</Box>
|
||||||
<FormTypeQuestions question={question} />
|
),
|
||||||
) : (
|
}}
|
||||||
<SwitchQuestionsPage question={question} />
|
/>
|
||||||
)}
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: isMobile ? "100%" : "auto",
|
||||||
|
position: "relative",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
flexDirection: isMobile ? "row-reverse" : null,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "4px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
sx={{ padding: "0", margin: "5px" }}
|
||||||
|
disableRipple
|
||||||
|
data-cy="expand-question"
|
||||||
|
onClick={() => toggleExpandQuestion(question.id)}
|
||||||
|
>
|
||||||
|
{question.expanded ? (
|
||||||
|
<ArrowDownIcon
|
||||||
|
style={{
|
||||||
|
width: "18px",
|
||||||
|
color: "#4D4D4D",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ExpandLessIcon
|
||||||
|
sx={{
|
||||||
|
boxSizing: "border-box",
|
||||||
|
fill: theme.palette.brightPurple.main,
|
||||||
|
background: "#FFF",
|
||||||
|
borderRadius: "6px",
|
||||||
|
height: "30px",
|
||||||
|
width: "30px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
height: "30px",
|
||||||
|
width: "30px",
|
||||||
|
marginLeft: "3px",
|
||||||
|
borderRadius: "50%",
|
||||||
|
fontSize: "16px",
|
||||||
|
color: question.expanded ? theme.palette.brightPurple.main : "#FFF",
|
||||||
|
background: question.expanded ? "#EEE4FC" : theme.palette.brightPurple.main,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{questionIndex + 1}
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Box>
|
||||||
</>
|
|
||||||
);
|
<IconButton
|
||||||
|
disableRipple
|
||||||
|
sx={{
|
||||||
|
padding: isMobile ? "0" : "0 5px",
|
||||||
|
right: isMobile ? "0" : null,
|
||||||
|
bottom: isMobile ? "0" : null,
|
||||||
|
}}
|
||||||
|
{...draggableProps}
|
||||||
|
>
|
||||||
|
<PointsIcon style={{ color: "#4D4D4D", fontSize: "30px" }} />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{question.type === null ? (
|
||||||
|
<FormTypeQuestions question={question} />
|
||||||
|
) : (
|
||||||
|
<SwitchQuestionsPage question={question} />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const IconAndrom = (questionType: QuestionType | null) => {
|
const IconAndrom = (questionType: QuestionType | null) => {
|
||||||
switch (questionType) {
|
switch (questionType) {
|
||||||
case "variant":
|
case "variant":
|
||||||
return <Answer color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
return <Answer color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
case "images":
|
case "images":
|
||||||
return (
|
return <OptionsPict color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
<OptionsPict color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />
|
case "varimg":
|
||||||
);
|
return <OptionsAndPict color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
case "varimg":
|
case "emoji":
|
||||||
return (
|
return <Emoji color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
<OptionsAndPict
|
case "text":
|
||||||
color="#9A9AAF"
|
return <Input color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
sx={{ height: "22px", width: "20px" }}
|
case "select":
|
||||||
/>
|
return <DropDown color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
);
|
case "date":
|
||||||
case "emoji":
|
return <Date color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
return <Emoji color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
case "number":
|
||||||
case "text":
|
return <Slider color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
return <Input color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
case "file":
|
||||||
case "select":
|
return <Download color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
return (
|
case "page":
|
||||||
<DropDown color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />
|
return <Page color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
);
|
case "rating":
|
||||||
case "date":
|
return <RatingIcon color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
return <Date color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
default:
|
||||||
case "number":
|
return <AnswerGroup color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
||||||
return <Slider color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
}
|
||||||
case "file":
|
|
||||||
return (
|
|
||||||
<Download color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />
|
|
||||||
);
|
|
||||||
case "page":
|
|
||||||
return <Page color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />;
|
|
||||||
case "rating":
|
|
||||||
return (
|
|
||||||
<RatingIcon color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<AnswerGroup color="#9A9AAF" sx={{ height: "22px", width: "20px" }} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -5,31 +5,25 @@ import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
|||||||
import FormDraggableListItem from "./FormDraggableListItem";
|
import FormDraggableListItem from "./FormDraggableListItem";
|
||||||
import { useQuestions } from "@root/questions/hooks";
|
import { useQuestions } from "@root/questions/hooks";
|
||||||
|
|
||||||
|
|
||||||
export const FormDraggableList = () => {
|
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) => {
|
return (
|
||||||
if (destination) reorderQuestions(source.index, destination.index);
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
};
|
<Droppable droppableId="droppable-list">
|
||||||
|
{(provided) => (
|
||||||
return (
|
<Box ref={provided.innerRef} {...provided.droppableProps}>
|
||||||
<DragDropContext onDragEnd={onDragEnd}>
|
{questions?.map((question, index) => (
|
||||||
<Droppable droppableId="droppable-list">
|
<FormDraggableListItem key={question.id} question={question} questionIndex={index} />
|
||||||
{(provided) => (
|
))}
|
||||||
<Box ref={provided.innerRef} {...provided.droppableProps}>
|
{provided.placeholder}
|
||||||
{questions?.map((question, index) => (
|
</Box>
|
||||||
<FormDraggableListItem
|
)}
|
||||||
key={question.id}
|
</Droppable>
|
||||||
question={question}
|
</DragDropContext>
|
||||||
questionIndex={index}
|
);
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{provided.placeholder}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Droppable>
|
|
||||||
</DragDropContext>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -8,106 +8,103 @@ import { FormDraggableList } from "./FormDraggableList";
|
|||||||
import { collapseAllQuestions, createUntypedQuestion } from "@root/questions/actions";
|
import { collapseAllQuestions, createUntypedQuestion } from "@root/questions/actions";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
|
||||||
|
|
||||||
export default function FormQuestionsPage() {
|
export default function FormQuestionsPage() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
|
||||||
if (!quiz) return null;
|
if (!quiz) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "796px",
|
maxWidth: "796px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
margin: "60px 0 40px 0",
|
margin: "60px 0 40px 0",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant={"h5"}>Заголовок анкеты</Typography>
|
<Typography variant={"h5"}>Заголовок анкеты</Typography>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
lineHeight: "19px",
|
lineHeight: "19px",
|
||||||
padding: 0,
|
padding: 0,
|
||||||
textDecoration: "underline",
|
textDecoration: "underline",
|
||||||
color: theme.palette.brightPurple.main,
|
color: theme.palette.brightPurple.main,
|
||||||
textDecorationColor: theme.palette.brightPurple.main,
|
textDecorationColor: theme.palette.brightPurple.main,
|
||||||
}}
|
}}
|
||||||
onClick={collapseAllQuestions}
|
onClick={collapseAllQuestions}
|
||||||
>
|
>
|
||||||
Свернуть всё
|
Свернуть всё
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "796px",
|
maxWidth: "796px",
|
||||||
boxShadow: "0px 10px 30px #e7e7e7",
|
boxShadow: "0px 10px 30px #e7e7e7",
|
||||||
borderRadius: "12px",
|
borderRadius: "12px",
|
||||||
marginBottom: "30px",
|
marginBottom: "30px",
|
||||||
borderTop: "1px solid transparent",
|
borderTop: "1px solid transparent",
|
||||||
borderBottom: "1px solid transparent",
|
borderBottom: "1px solid transparent",
|
||||||
background: "#FFFFFF",
|
background: "#FFFFFF",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormDraggableList />
|
<FormDraggableList />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "15px",
|
gap: "15px",
|
||||||
padding: "4px",
|
padding: "4px",
|
||||||
margin: "15px",
|
margin: "15px",
|
||||||
border: "1px solid transparent",
|
border: "1px solid transparent",
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
border: "1px solid #9A9AAF",
|
border: "1px solid #9A9AAF",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
createUntypedQuestion(quiz.backendId);
|
createUntypedQuestion(quiz.backendId);
|
||||||
}}
|
}}
|
||||||
data-cy="create-question"
|
data-cy="create-question"
|
||||||
>
|
>
|
||||||
<AddAnswer color="#EEE4FC" />
|
<AddAnswer color="#EEE4FC" />
|
||||||
<Typography sx={{ color: "#9A9AAF" }}>
|
<Typography sx={{ color: "#9A9AAF" }}>Добавить еще один вопрос</Typography>
|
||||||
Добавить еще один вопрос
|
</Box>
|
||||||
</Typography>
|
</Box>
|
||||||
</Box>
|
<Box
|
||||||
</Box>
|
sx={{
|
||||||
<Box
|
display: "flex",
|
||||||
sx={{
|
justifyContent: "flex-end",
|
||||||
display: "flex",
|
gap: "8px",
|
||||||
justifyContent: "flex-end",
|
maxWidth: "796px",
|
||||||
gap: "8px",
|
}}
|
||||||
maxWidth: "796px",
|
>
|
||||||
}}
|
<Button
|
||||||
>
|
variant="outlined"
|
||||||
<Button
|
sx={{ padding: "10px 20px", borderRadius: "8px", height: "44px" }}
|
||||||
variant="outlined"
|
onClick={decrementCurrentStep}
|
||||||
sx={{ padding: "10px 20px", borderRadius: "8px", height: "44px" }}
|
>
|
||||||
onClick={decrementCurrentStep}
|
<ArrowLeft />
|
||||||
>
|
</Button>
|
||||||
<ArrowLeft />
|
<Button
|
||||||
</Button>
|
variant="contained"
|
||||||
<Button
|
sx={{
|
||||||
variant="contained"
|
height: "44px",
|
||||||
sx={{
|
padding: "10px 20px",
|
||||||
height: "44px",
|
borderRadius: "8px",
|
||||||
padding: "10px 20px",
|
background: theme.palette.brightPurple.main,
|
||||||
borderRadius: "8px",
|
fontSize: "18px",
|
||||||
background: theme.palette.brightPurple.main,
|
}}
|
||||||
fontSize: "18px",
|
onClick={incrementCurrentStep}
|
||||||
}}
|
>
|
||||||
onClick={incrementCurrentStep}
|
Следующий шаг
|
||||||
>
|
</Button>
|
||||||
Следующий шаг
|
</Box>
|
||||||
</Button>
|
{createPortal(<QuizPreview />, document.body)}
|
||||||
</Box>
|
</>
|
||||||
{createPortal(<QuizPreview />, document.body)}
|
);
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -12,80 +12,75 @@ import Slider from "../../../assets/icons/questionsPage/slider";
|
|||||||
|
|
||||||
import { QuestionType } from "@model/question/question";
|
import { QuestionType } from "@model/question/question";
|
||||||
import { createTypedQuestion } from "@root/questions/actions";
|
import { createTypedQuestion } from "@root/questions/actions";
|
||||||
import type {
|
import type { UntypedQuizQuestion } from "../../../model/questionTypes/shared";
|
||||||
UntypedQuizQuestion
|
|
||||||
} from "../../../model/questionTypes/shared";
|
|
||||||
|
|
||||||
|
|
||||||
type ButtonTypeQuestion = {
|
type ButtonTypeQuestion = {
|
||||||
icon: JSX.Element;
|
icon: JSX.Element;
|
||||||
title: string;
|
title: string;
|
||||||
value: QuestionType;
|
value: QuestionType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const BUTTON_TYPE_SHORT_QUESTIONS: ButtonTypeQuestion[] = [
|
const BUTTON_TYPE_SHORT_QUESTIONS: ButtonTypeQuestion[] = [
|
||||||
{
|
{
|
||||||
icon: <Answer color="#9A9AAF" />,
|
icon: <Answer color="#9A9AAF" />,
|
||||||
title: "Варианты ответов",
|
title: "Варианты ответов",
|
||||||
value: "variant",
|
value: "variant",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Input color="#9A9AAF" />,
|
icon: <Input color="#9A9AAF" />,
|
||||||
title: "Своё поле для ввода",
|
title: "Своё поле для ввода",
|
||||||
value: "text",
|
value: "text",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <DropDown color="#9A9AAF" />,
|
icon: <DropDown color="#9A9AAF" />,
|
||||||
title: "Выпадающий список",
|
title: "Выпадающий список",
|
||||||
value: "select",
|
value: "select",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Date color="#9A9AAF" />,
|
icon: <Date color="#9A9AAF" />,
|
||||||
title: "Дата",
|
title: "Дата",
|
||||||
value: "date",
|
value: "date",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Slider color="#9A9AAF" />,
|
icon: <Slider color="#9A9AAF" />,
|
||||||
title: "Ползунок",
|
title: "Ползунок",
|
||||||
value: "number",
|
value: "number",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Download color="#9A9AAF" />,
|
icon: <Download color="#9A9AAF" />,
|
||||||
title: "Загрузка файла",
|
title: "Загрузка файла",
|
||||||
value: "file",
|
value: "file",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: UntypedQuizQuestion;
|
question: UntypedQuizQuestion;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FormTypeQuestions({ question }: Props) {
|
export default function FormTypeQuestions({ question }: Props) {
|
||||||
|
return (
|
||||||
return (
|
<Box>
|
||||||
<Box>
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
sx={{
|
display: "flex",
|
||||||
display: "flex",
|
flexWrap: "wrap",
|
||||||
flexWrap: "wrap",
|
gap: "20px",
|
||||||
gap: "20px",
|
margin: "20px",
|
||||||
margin: "20px",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{("page" in question && question.page === 0 ? BUTTON_TYPE_QUESTIONS : BUTTON_TYPE_SHORT_QUESTIONS).map(
|
||||||
{(("page" in question) && question.page === 0
|
({ icon, title, value: questionType }) => (
|
||||||
? BUTTON_TYPE_QUESTIONS
|
<QuestionsMiniButton
|
||||||
: BUTTON_TYPE_SHORT_QUESTIONS
|
key={title}
|
||||||
).map(({ icon, title, value: questionType }) => (
|
onClick={() => {
|
||||||
<QuestionsMiniButton
|
createTypedQuestion(question.id, questionType);
|
||||||
key={title}
|
}}
|
||||||
onClick={() => {
|
icon={icon}
|
||||||
createTypedQuestion(question.id, questionType);
|
text={title}
|
||||||
}}
|
/>
|
||||||
icon={icon}
|
)
|
||||||
text={title}
|
)}
|
||||||
/>
|
</Box>
|
||||||
))}
|
</Box>
|
||||||
</Box>
|
);
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -151,181 +151,181 @@ export default function OptionsAndPicture({ question }: Props) {
|
|||||||
onClose={closeCropModal}
|
onClose={closeCropModal}
|
||||||
onSaveImageClick={handleCropModalSaveClick}
|
onSaveImageClick={handleCropModalSaveClick}
|
||||||
/>
|
/>
|
||||||
<Box
|
{/*<Box*/}
|
||||||
sx={{
|
{/* sx={{*/}
|
||||||
width: "100%",
|
{/* width: "100%",*/}
|
||||||
border: "1px solid #9A9AAF",
|
{/* border: "1px solid #9A9AAF",*/}
|
||||||
borderRadius: "8px",
|
{/* borderRadius: "8px",*/}
|
||||||
display: isTablet ? "block" : "none",
|
{/* display: isTablet ? "block" : "none",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
>
|
{/*>*/}
|
||||||
<TextField
|
{/* <TextField*/}
|
||||||
fullWidth
|
{/* fullWidth*/}
|
||||||
focused={false}
|
{/* focused={false}*/}
|
||||||
placeholder={"Добавьте ответ"}
|
{/* placeholder={"Добавьте ответ"}*/}
|
||||||
multiline={question.content.largeCheck}
|
{/* multiline={question.content.largeCheck}*/}
|
||||||
InputProps={{
|
{/* InputProps={{*/}
|
||||||
startAdornment: (
|
{/* startAdornment: (*/}
|
||||||
<>
|
{/* <>*/}
|
||||||
<InputAdornment position="start">
|
{/* <InputAdornment position="start">*/}
|
||||||
<PointsIcon
|
{/* <PointsIcon*/}
|
||||||
style={{ color: "#9A9AAF", fontSize: "30px" }}
|
{/* style={{ color: "#9A9AAF", fontSize: "30px" }}*/}
|
||||||
/>
|
{/* />*/}
|
||||||
</InputAdornment>
|
{/* </InputAdornment>*/}
|
||||||
{!isMobile && (
|
{/* {!isMobile && (*/}
|
||||||
<Box
|
{/* <Box*/}
|
||||||
sx={{
|
{/* sx={{*/}
|
||||||
width: "60px",
|
{/* width: "60px",*/}
|
||||||
height: "40px",
|
{/* height: "40px",*/}
|
||||||
background: "#EEE4FC",
|
{/* background: "#EEE4FC",*/}
|
||||||
display: "flex",
|
{/* display: "flex",*/}
|
||||||
justifyContent: "space-between",
|
{/* justifyContent: "space-between",*/}
|
||||||
marginRight: "20px",
|
{/* marginRight: "20px",*/}
|
||||||
marginLeft: "12px",
|
{/* marginLeft: "12px",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
>
|
{/* >*/}
|
||||||
<Box
|
{/* <Box*/}
|
||||||
sx={{
|
{/* sx={{*/}
|
||||||
display: "flex",
|
{/* display: "flex",*/}
|
||||||
alignItems: "center",
|
{/* alignItems: "center",*/}
|
||||||
justifyContent: "center",
|
{/* justifyContent: "center",*/}
|
||||||
width: "100%",
|
{/* width: "100%",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
>
|
{/* >*/}
|
||||||
<ImageAddIcons fontSize="22px" color="#7E2AEA" />
|
{/* <ImageAddIcons fontSize="22px" color="#7E2AEA" />*/}
|
||||||
</Box>
|
{/* </Box>*/}
|
||||||
<span
|
{/* <span*/}
|
||||||
style={{
|
{/* style={{*/}
|
||||||
display: "flex",
|
{/* display: "flex",*/}
|
||||||
alignItems: "center",
|
{/* alignItems: "center",*/}
|
||||||
justifyContent: "center",
|
{/* justifyContent: "center",*/}
|
||||||
background: "#7E2AEA",
|
{/* background: "#7E2AEA",*/}
|
||||||
height: "100%",
|
{/* height: "100%",*/}
|
||||||
width: "25px",
|
{/* width: "25px",*/}
|
||||||
color: "white",
|
{/* color: "white",*/}
|
||||||
fontSize: "15px",
|
{/* fontSize: "15px",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
>
|
{/* >*/}
|
||||||
+
|
{/* +*/}
|
||||||
</span>
|
{/* </span>*/}
|
||||||
</Box>
|
{/* </Box>*/}
|
||||||
)}
|
{/* )}*/}
|
||||||
</>
|
{/* </>*/}
|
||||||
),
|
{/* ),*/}
|
||||||
endAdornment: (
|
{/* endAdornment: (*/}
|
||||||
<InputAdornment position="end">
|
{/* <InputAdornment position="end">*/}
|
||||||
<IconButton
|
{/* <IconButton*/}
|
||||||
sx={{ padding: "0" }}
|
{/* sx={{ padding: "0" }}*/}
|
||||||
aria-describedby="my-popover-id"
|
{/* aria-describedby="my-popover-id"*/}
|
||||||
>
|
{/* >*/}
|
||||||
<MessageIcon
|
{/* <MessageIcon*/}
|
||||||
style={{
|
{/* style={{*/}
|
||||||
color: "#9A9AAF",
|
{/* color: "#9A9AAF",*/}
|
||||||
fontSize: "30px",
|
{/* fontSize: "30px",*/}
|
||||||
marginRight: "6.5px",
|
{/* marginRight: "6.5px",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
/>
|
{/* />*/}
|
||||||
</IconButton>
|
{/* </IconButton>*/}
|
||||||
<Popover
|
{/* <Popover*/}
|
||||||
id="my-popover-id"
|
{/* id="my-popover-id"*/}
|
||||||
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
{/* anchorOrigin={{ vertical: "bottom", horizontal: "left" }}*/}
|
||||||
open={false}
|
{/* open={false}*/}
|
||||||
>
|
{/* >*/}
|
||||||
<TextareaAutosize
|
{/* <TextareaAutosize*/}
|
||||||
style={{ margin: "10px" }}
|
{/* style={{ margin: "10px" }}*/}
|
||||||
placeholder="Подсказка для этого ответа"
|
{/* placeholder="Подсказка для этого ответа"*/}
|
||||||
/>
|
{/* />*/}
|
||||||
</Popover>
|
{/* </Popover>*/}
|
||||||
<IconButton sx={{ padding: "0" }}>
|
{/* <IconButton sx={{ padding: "0" }}>*/}
|
||||||
<DeleteIcon
|
{/* <DeleteIcon*/}
|
||||||
style={{
|
{/* style={{*/}
|
||||||
color: theme.palette.grey2.main,
|
{/* color: theme.palette.grey2.main,*/}
|
||||||
marginRight: "-1px",
|
{/* marginRight: "-1px",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
/>
|
{/* />*/}
|
||||||
</IconButton>
|
{/* </IconButton>*/}
|
||||||
</InputAdornment>
|
{/* </InputAdornment>*/}
|
||||||
),
|
{/* ),*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
sx={{
|
{/* sx={{*/}
|
||||||
"& .MuiInputBase-root": {
|
{/* "& .MuiInputBase-root": {*/}
|
||||||
padding: "13.5px",
|
{/* padding: "13.5px",*/}
|
||||||
borderRadius: "10px",
|
{/* borderRadius: "10px",*/}
|
||||||
background: "#ffffff",
|
{/* background: "#ffffff",*/}
|
||||||
height: "48px",
|
{/* height: "48px",*/}
|
||||||
},
|
{/* },*/}
|
||||||
"& .MuiOutlinedInput-notchedOutline": {
|
{/* "& .MuiOutlinedInput-notchedOutline": {*/}
|
||||||
border: "none",
|
{/* border: "none",*/}
|
||||||
},
|
{/* },*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
inputProps={{
|
{/* inputProps={{*/}
|
||||||
sx: { fontSize: "18px", lineHeight: "21px", py: 0 },
|
{/* sx: { fontSize: "18px", lineHeight: "21px", py: 0 },*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
/>
|
{/* />*/}
|
||||||
|
|
||||||
{isMobile && (
|
{/*{isMobile && (*/}
|
||||||
<Box
|
{/* <Box*/}
|
||||||
sx={{
|
{/* sx={{*/}
|
||||||
display: "flex",
|
{/* display: "flex",*/}
|
||||||
alignItems: "center",
|
{/* alignItems: "center",*/}
|
||||||
m: "8px",
|
{/* m: "8px",*/}
|
||||||
position: "relative",
|
{/* position: "relative",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
>
|
{/* >*/}
|
||||||
<Box
|
{/* <Box*/}
|
||||||
sx={{ width: "100%", background: "#EEE4FC", height: "40px" }}
|
{/* sx={{ width: "100%", background: "#EEE4FC", height: "40px" }}*/}
|
||||||
/>
|
{/* />*/}
|
||||||
<ImageAddIcons
|
{/* <ImageAddIcons*/}
|
||||||
style={{
|
{/* style={{*/}
|
||||||
position: "absolute",
|
{/* position: "absolute",*/}
|
||||||
color: "#7E2AEA",
|
{/* color: "#7E2AEA",*/}
|
||||||
fontSize: "20px",
|
{/* fontSize: "20px",*/}
|
||||||
left: "45%",
|
{/* left: "45%",*/}
|
||||||
right: "55%",
|
{/* right: "55%",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
/>
|
{/* />*/}
|
||||||
<Box
|
{/* <Box*/}
|
||||||
sx={{
|
{/* sx={{*/}
|
||||||
display: "flex",
|
{/* display: "flex",*/}
|
||||||
justifyContent: "center",
|
{/* justifyContent: "center",*/}
|
||||||
alignItems: "center",
|
{/* alignItems: "center",*/}
|
||||||
width: "20px",
|
{/* width: "20px",*/}
|
||||||
background: "#EEE4FC",
|
{/* background: "#EEE4FC",*/}
|
||||||
height: "40px",
|
{/* height: "40px",*/}
|
||||||
color: "white",
|
{/* color: "white",*/}
|
||||||
backgroundColor: "#7E2AEA",
|
{/* backgroundColor: "#7E2AEA",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
>
|
{/* >*/}
|
||||||
<Box
|
{/* <Box*/}
|
||||||
sx={{ width: "100%", background: "#EEE4FC", height: "40px" }}
|
{/* sx={{ width: "100%", background: "#EEE4FC", height: "40px" }}*/}
|
||||||
/>
|
{/* />*/}
|
||||||
<ImageAddIcons
|
{/* <ImageAddIcons*/}
|
||||||
style={{
|
{/* style={{*/}
|
||||||
position: "absolute",
|
{/* position: "absolute",*/}
|
||||||
color: "#7E2AEA",
|
{/* color: "#7E2AEA",*/}
|
||||||
fontSize: "20px",
|
{/* fontSize: "20px",*/}
|
||||||
left: "45%",
|
{/* left: "45%",*/}
|
||||||
right: "55%",
|
{/* right: "55%",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
/>
|
{/* />*/}
|
||||||
<Box
|
{/* <Box*/}
|
||||||
sx={{
|
{/* sx={{*/}
|
||||||
display: "flex",
|
{/* display: "flex",*/}
|
||||||
justifyContent: "center",
|
{/* justifyContent: "center",*/}
|
||||||
alignItems: "center",
|
{/* alignItems: "center",*/}
|
||||||
width: "20px",
|
{/* width: "20px",*/}
|
||||||
background: "#EEE4FC",
|
{/* background: "#EEE4FC",*/}
|
||||||
height: "40px",
|
{/* height: "40px",*/}
|
||||||
color: "white",
|
{/* color: "white",*/}
|
||||||
backgroundColor: "#7E2AEA",
|
{/* backgroundColor: "#7E2AEA",*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
>
|
{/* >*/}
|
||||||
+
|
{/* +*/}
|
||||||
</Box>
|
{/* </Box>*/}
|
||||||
</Box>
|
{/* </Box>*/}
|
||||||
</Box>
|
{/* </Box>*/}
|
||||||
)}
|
{/*)}*/}
|
||||||
</Box>
|
{/*</Box>*/}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
import { useState, useEffect, useLayoutEffect, useRef } from "react"
|
import { useState, useEffect, useLayoutEffect, useRef } from "react";
|
||||||
import {
|
import { Box, Button, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
IconButton,
|
|
||||||
Typography,
|
|
||||||
useMediaQuery,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { collapseAllQuestions, createUntypedQuestion } from "@root/questions/actions";
|
import { collapseAllQuestions, createUntypedQuestion } from "@root/questions/actions";
|
||||||
import { decrementCurrentStep, incrementCurrentStep } from "@root/quizes/actions";
|
import { decrementCurrentStep, incrementCurrentStep } from "@root/quizes/actions";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
@ -14,105 +7,102 @@ import QuizPreview from "@ui_kit/QuizPreview/QuizPreview";
|
|||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import AddPlus from "../../assets/icons/questionsPage/addPlus";
|
import AddPlus from "../../assets/icons/questionsPage/addPlus";
|
||||||
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
||||||
import BranchingQuestions from "./BranchingModal/BranchingQuestionsModal"
|
import BranchingQuestions from "./BranchingModal/BranchingQuestionsModal";
|
||||||
import { QuestionSwitchWindowTool } from "./QuestionSwitchWindowTool";
|
import { QuestionSwitchWindowTool } from "./QuestionSwitchWindowTool";
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { updateOpenBranchingPanel, updateEditSomeQuestion } from "@root/uiTools/actions";
|
import { updateOpenBranchingPanel, updateEditSomeQuestion } from "@root/uiTools/actions";
|
||||||
import { useUiTools } from "@root/uiTools/store";
|
import { useUiTools } from "@root/uiTools/store";
|
||||||
|
|
||||||
export default function QuestionsPage() {
|
export default function QuestionsPage() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { openedModalSettingsId, openBranchingPanel } = useUiTools();
|
const { openedModalSettingsId, openBranchingPanel } = useUiTools();
|
||||||
const isMobile = false//useMediaQuery(theme.breakpoints.down(660));
|
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
updateOpenBranchingPanel(false)
|
updateOpenBranchingPanel(false);
|
||||||
updateEditSomeQuestion()
|
updateEditSomeQuestion();
|
||||||
},[])
|
}, []);
|
||||||
|
|
||||||
const ref = useRef()
|
const ref = useRef();
|
||||||
if (!quiz) return null;
|
if (!quiz) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
ref={ref}
|
||||||
|
id="QuestionsPage"
|
||||||
|
sx={{
|
||||||
|
maxWidth: "796px",
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
margin: "60px 0 40px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant={"h5"}>{quiz.name ? quiz.name : "Заголовок квиза"}</Typography>
|
||||||
|
<Button
|
||||||
|
sx={{
|
||||||
|
display: openBranchingPanel ? "none" : "flex",
|
||||||
|
fontSize: "16px",
|
||||||
|
lineHeight: "19px",
|
||||||
|
padding: 0,
|
||||||
|
textDecoration: "underline",
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
textDecorationColor: theme.palette.brightPurple.main,
|
||||||
|
}}
|
||||||
|
onClick={collapseAllQuestions}
|
||||||
|
>
|
||||||
|
Свернуть всё
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<QuestionSwitchWindowTool />
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
maxWidth: "796px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
createUntypedQuestion(quiz.backendId);
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
position: "fixed",
|
||||||
|
left: isMobile ? "20px" : "250px",
|
||||||
|
bottom: isMobile ? "140px" : "20px",
|
||||||
|
}}
|
||||||
|
data-cy="create-question"
|
||||||
|
>
|
||||||
|
<AddPlus />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
return (
|
<Box sx={{ display: "flex", gap: "8px", marginLeft: "auto" }}>
|
||||||
<>
|
<Button
|
||||||
<Box
|
variant="outlined"
|
||||||
ref={ref}
|
sx={{ padding: "10px 20px", borderRadius: "8px", height: "44px" }}
|
||||||
id="QuestionsPage"
|
data-cy="back-button"
|
||||||
sx={{
|
onClick={decrementCurrentStep}
|
||||||
maxWidth: "796px",
|
>
|
||||||
width: "100%",
|
<ArrowLeft />
|
||||||
display: "flex",
|
</Button>
|
||||||
justifyContent: "space-between",
|
<Button
|
||||||
margin: "60px 0 40px 0",
|
variant="contained"
|
||||||
}}
|
sx={{
|
||||||
>
|
height: "44px",
|
||||||
<Typography variant={"h5"}>{
|
padding: "10px 20px",
|
||||||
quiz.name ? quiz.name : "Заголовок квиза" }</Typography>
|
borderRadius: "8px",
|
||||||
<Button
|
background: theme.palette.brightPurple.main,
|
||||||
sx={{
|
fontSize: "18px",
|
||||||
display: openBranchingPanel ? "none" : "flex",
|
}}
|
||||||
fontSize: "16px",
|
onClick={incrementCurrentStep}
|
||||||
lineHeight: "19px",
|
>
|
||||||
padding: 0,
|
Следующий шаг
|
||||||
textDecoration: "underline",
|
</Button>
|
||||||
color: theme.palette.brightPurple.main,
|
</Box>
|
||||||
textDecorationColor: theme.palette.brightPurple.main,
|
</Box>
|
||||||
}}
|
{createPortal(<QuizPreview />, document.body)}
|
||||||
onClick={collapseAllQuestions}
|
{openedModalSettingsId !== null && <BranchingQuestions />}
|
||||||
>
|
</>
|
||||||
Свернуть всё
|
);
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
<QuestionSwitchWindowTool/>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
maxWidth: "796px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
onClick={() => {
|
|
||||||
createUntypedQuestion(quiz.backendId);
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
position: "fixed",
|
|
||||||
left: isMobile ? "20px" : "250px",
|
|
||||||
bottom: "20px",
|
|
||||||
}}
|
|
||||||
data-cy="create-question"
|
|
||||||
>
|
|
||||||
<AddPlus />
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
|
|
||||||
<Box sx={{ display: "flex", gap: "8px", marginLeft: "auto" }}>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
sx={{ padding: "10px 20px", borderRadius: "8px", height: "44px" }}
|
|
||||||
data-cy="back-button"
|
|
||||||
onClick={decrementCurrentStep}
|
|
||||||
>
|
|
||||||
<ArrowLeft />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
sx={{
|
|
||||||
height: "44px",
|
|
||||||
padding: "10px 20px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
background: theme.palette.brightPurple.main,
|
|
||||||
fontSize: "18px",
|
|
||||||
}}
|
|
||||||
onClick={incrementCurrentStep}
|
|
||||||
>
|
|
||||||
Следующий шаг
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
{createPortal(<QuizPreview />, document.body)}
|
|
||||||
{openedModalSettingsId !== null && <BranchingQuestions/>}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -6,217 +6,216 @@ import SwitchSlider from "./switchSlider";
|
|||||||
import type { QuizQuestionNumber } from "../../../model/questionTypes/number";
|
import type { QuizQuestionNumber } from "../../../model/questionTypes/number";
|
||||||
import { updateQuestion } from "@root/questions/actions";
|
import { updateQuestion } from "@root/questions/actions";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: QuizQuestionNumber;
|
question: QuizQuestionNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SliderOptions({ question }: Props) {
|
export default function SliderOptions({ question }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(980));
|
const isTablet = useMediaQuery(theme.breakpoints.down(980));
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||||
const [switchState, setSwitchState] = useState("setting");
|
const [switchState, setSwitchState] = useState("setting");
|
||||||
const [stepError, setStepError] = useState("");
|
const [stepError, setStepError] = useState("");
|
||||||
|
|
||||||
const SSHC = (data: string) => {
|
const SSHC = (data: string) => {
|
||||||
setSwitchState(data);
|
setSwitchState(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: isTablet ? "auto" : "100%",
|
width: isTablet ? "auto" : "100%",
|
||||||
maxWidth: "720.8px",
|
maxWidth: "720.8px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
pl: "20px",
|
pl: "20px",
|
||||||
pr: isMobile ? "13px" : "20px",
|
pr: isMobile ? "13px" : "20px",
|
||||||
pb: isMobile ? "30px" : "20px",
|
pb: isMobile ? "30px" : "20px",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: isMobile ? "25px" : "20px",
|
gap: isMobile ? "25px" : "20px",
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
gap: isMobile ? "10px" : "14px",
|
||||||
|
mt: isMobile ? "25px" : "0px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
marginRight: isMobile ? "10px" : "0px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography sx={{ fontWeight: "500", fontSize: "18px", color: "#4D4D4D" }}>
|
||||||
|
Выбор значения из диапазона
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ width: "100%", display: "flex", alignItems: "center", gap: isMobile ? "9px" : "20px" }}>
|
||||||
|
<CustomNumberField
|
||||||
|
sx={{ maxWidth: "310px", width: "100%" }}
|
||||||
|
placeholder={"0"}
|
||||||
|
min={0}
|
||||||
|
max={99999999999}
|
||||||
|
value={question.content.range.split("—")[0]}
|
||||||
|
onChange={({ target }) => {
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography>—</Typography>
|
||||||
|
<CustomNumberField
|
||||||
|
sx={{ maxWidth: "310px", width: "100%" }}
|
||||||
|
placeholder={"100"}
|
||||||
|
min={0}
|
||||||
|
max={100000000000}
|
||||||
|
value={question.content.range.split("—")[1]}
|
||||||
|
onChange={({ target }) => {
|
||||||
|
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("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
flexDirection: isMobile ? "column-reverse" : "",
|
||||||
|
gap: isMobile ? "15px" : "50px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ width: "100%" }}>
|
||||||
|
<Typography sx={{ fontWeight: "500", fontSize: "18px", color: "#4D4D4D", mb: isMobile ? "10px" : "14px" }}>
|
||||||
|
Начальное значение
|
||||||
|
</Typography>
|
||||||
|
<CustomNumberField
|
||||||
|
sx={{ maxWidth: "310px", width: "100%" }}
|
||||||
|
placeholder={"50"}
|
||||||
|
min={Number(question.content.range.split("—")[0])}
|
||||||
|
max={Number(question.content.range.split("—")[1])}
|
||||||
|
value={String(question.content.start)}
|
||||||
|
onChange={({ target }) => {
|
||||||
|
updateQuestion(question.id, (question) => {
|
||||||
|
if (question.type !== "number") return;
|
||||||
|
|
||||||
|
question.content.start = Number(target.value);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ width: "100%" }}>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontWeight: "500",
|
||||||
|
fontSize: "18px",
|
||||||
|
color: "#4D4D4D",
|
||||||
|
mb: "10px",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
Шаг
|
||||||
sx={{
|
</Typography>
|
||||||
gap: isMobile ? "10px" : "14px",
|
<CustomNumberField
|
||||||
mt: isMobile ? "25px" : "0px",
|
sx={{ maxWidth: "310px", width: "100%" }}
|
||||||
display: "flex",
|
min={0}
|
||||||
flexDirection: "column",
|
max={100}
|
||||||
marginRight: isMobile ? "10px" : "0px",
|
placeholder={"1"}
|
||||||
}}
|
error={stepError}
|
||||||
>
|
value={String(question.content.step)}
|
||||||
<Typography sx={{ fontWeight: "500", fontSize: "18px", color: "#4D4D4D" }}>
|
onChange={({ target }) => {
|
||||||
Выбор значения из диапазона
|
updateQuestion(question.id, (question) => {
|
||||||
</Typography>
|
if (question.type !== "number") return;
|
||||||
<Box sx={{ width: "100%", display: "flex", alignItems: "center", gap: isMobile ? "9px" : "20px" }}>
|
|
||||||
<CustomNumberField
|
|
||||||
sx={{ maxWidth: "310px", width: "100%" }}
|
|
||||||
placeholder={"0"}
|
|
||||||
min={0}
|
|
||||||
max={99}
|
|
||||||
value={question.content.range.split("—")[0]}
|
|
||||||
onChange={({ target }) => {
|
|
||||||
updateQuestion(question.id, question => {
|
|
||||||
if (question.type !== "number") return;
|
|
||||||
|
|
||||||
question.content.range = `${target.value}—${question.content.range.split("—")[1]}`;
|
question.content.step = Number(target.value);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onBlur={({ target }) => {
|
onBlur={({ target }) => {
|
||||||
const start = question.content.start;
|
const min = Number(question.content.range.split("—")[0]);
|
||||||
const min = Number(target.value);
|
const max = Number(question.content.range.split("—")[1]);
|
||||||
const max = Number(question.content.range.split("—")[1]);
|
const range = max - min;
|
||||||
|
const step = Number(target.value);
|
||||||
|
|
||||||
if (min >= max) {
|
if (step > max) {
|
||||||
updateQuestion(question.id, question => {
|
updateQuestion(question.id, (question) => {
|
||||||
if (question.type !== "number") return;
|
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) {
|
if (range % step) {
|
||||||
updateQuestion(question.id, question => {
|
setStepError(`Шаг должен делить без остатка диапазон ${max} - ${min} = ${max - min}`);
|
||||||
if (question.type !== "number") return;
|
} else {
|
||||||
|
setStepError("");
|
||||||
question.content.start = min;
|
}
|
||||||
});
|
}}
|
||||||
}
|
/>
|
||||||
}}
|
</Box>
|
||||||
/>
|
</Box>
|
||||||
<Typography>—</Typography>
|
</Box>
|
||||||
<CustomNumberField
|
<ButtonsOptions switchState={switchState} SSHC={SSHC} question={question} />
|
||||||
sx={{ maxWidth: "310px", width: "100%" }}
|
<SwitchSlider switchState={switchState} question={question} />
|
||||||
placeholder={"100"}
|
</>
|
||||||
min={0}
|
);
|
||||||
max={100}
|
|
||||||
value={question.content.range.split("—")[1]}
|
|
||||||
onChange={({ target }) => {
|
|
||||||
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("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
flexDirection: isMobile ? "column-reverse" : "",
|
|
||||||
gap: isMobile ? "15px" : "50px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ width: "100%" }}>
|
|
||||||
<Typography sx={{ fontWeight: "500", fontSize: "18px", color: "#4D4D4D", mb: isMobile ? "10px" : "14px" }}>
|
|
||||||
Начальное значение
|
|
||||||
</Typography>
|
|
||||||
<CustomNumberField
|
|
||||||
sx={{ maxWidth: "310px", width: "100%" }}
|
|
||||||
placeholder={"50"}
|
|
||||||
min={Number(question.content.range.split("—")[0])}
|
|
||||||
max={Number(question.content.range.split("—")[1])}
|
|
||||||
value={String(question.content.start)}
|
|
||||||
onChange={({ target }) => {
|
|
||||||
updateQuestion(question.id, question => {
|
|
||||||
if (question.type !== "number") return;
|
|
||||||
|
|
||||||
question.content.start = Number(target.value);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ width: "100%" }}>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontWeight: "500",
|
|
||||||
fontSize: "18px",
|
|
||||||
color: "#4D4D4D",
|
|
||||||
mb: "10px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Шаг
|
|
||||||
</Typography>
|
|
||||||
<CustomNumberField
|
|
||||||
sx={{ maxWidth: "310px", width: "100%" }}
|
|
||||||
min={0}
|
|
||||||
max={100}
|
|
||||||
placeholder={"1"}
|
|
||||||
error={stepError}
|
|
||||||
value={String(question.content.step)}
|
|
||||||
onChange={({ target }) => {
|
|
||||||
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("");
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<ButtonsOptions switchState={switchState} SSHC={SSHC} question={question} />
|
|
||||||
<SwitchSlider switchState={switchState} question={question} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -12,48 +12,46 @@ import UploadFile from "./UploadFile/UploadFile";
|
|||||||
import AnswerOptions from "./answerOptions/AnswerOptions";
|
import AnswerOptions from "./answerOptions/AnswerOptions";
|
||||||
import { notReachable } from "../../utils/notReachable";
|
import { notReachable } from "../../utils/notReachable";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: AnyTypedQuizQuestion;
|
question: AnyTypedQuizQuestion;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SwitchQuestionsPage({ question }: Props) {
|
export default function SwitchQuestionsPage({ question }: Props) {
|
||||||
|
switch (question.type) {
|
||||||
|
case "variant":
|
||||||
|
return <AnswerOptions question={question} />;
|
||||||
|
|
||||||
switch (question.type) {
|
case "images":
|
||||||
case "variant":
|
return <OptionsPicture question={question} />;
|
||||||
return <AnswerOptions question={question} />;
|
|
||||||
|
|
||||||
case "images":
|
case "varimg":
|
||||||
return <OptionsPicture question={question} />;
|
return <OptionsAndPicture question={question} />;
|
||||||
|
|
||||||
case "varimg":
|
case "emoji":
|
||||||
return <OptionsAndPicture question={question} />;
|
return <Emoji question={question} />;
|
||||||
|
|
||||||
case "emoji":
|
case "text":
|
||||||
return <Emoji question={question} />;
|
return <OwnTextField question={question} />;
|
||||||
|
|
||||||
case "text":
|
case "select":
|
||||||
return <OwnTextField question={question} />;
|
return <DropDown question={question} />;
|
||||||
|
|
||||||
case "select":
|
case "date":
|
||||||
return <DropDown question={question} />;
|
return <DataOptions question={question} />;
|
||||||
|
|
||||||
case "date":
|
case "number":
|
||||||
return <DataOptions question={question} />;
|
return <SliderOptions question={question} />;
|
||||||
|
|
||||||
case "number":
|
case "file":
|
||||||
return <SliderOptions question={question} />;
|
return <UploadFile question={question} />;
|
||||||
|
|
||||||
case "file":
|
case "page":
|
||||||
return <UploadFile question={question} />;
|
return <PageOptions question={question} />;
|
||||||
|
|
||||||
case "page":
|
case "rating":
|
||||||
return <PageOptions question={question} />;
|
return <RatingOptions question={question} />;
|
||||||
|
|
||||||
case "rating":
|
default:
|
||||||
return <RatingOptions question={question} />;
|
notReachable(question);
|
||||||
|
}
|
||||||
default:
|
|
||||||
notReachable(question)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,172 +1,168 @@
|
|||||||
import {
|
import { Typography, Box, useTheme, ButtonBase, Modal, TextField, InputAdornment } from "@mui/material";
|
||||||
Typography,
|
|
||||||
Box,
|
|
||||||
useTheme,
|
|
||||||
ButtonBase,
|
|
||||||
Modal,
|
|
||||||
TextField,
|
|
||||||
InputAdornment,
|
|
||||||
} from "@mui/material";
|
|
||||||
import UploadIcon from "../../../assets/icons/UploadIcon";
|
import UploadIcon from "../../../assets/icons/UploadIcon";
|
||||||
import SearchIcon from "../../../assets/icons/SearchIcon";
|
import SearchIcon from "../../../assets/icons/SearchIcon";
|
||||||
import UnsplashIcon from "../../../assets/icons/Unsplash.svg";
|
import UnsplashIcon from "../../../assets/icons/Unsplash.svg";
|
||||||
import { useRef, useState, type DragEvent } from "react";
|
import { useRef, useState, type DragEvent } from "react";
|
||||||
|
|
||||||
|
type ImageFormat = "jpg" | "jpeg" | "png" | "gif";
|
||||||
|
|
||||||
interface ModalkaProps {
|
interface ModalkaProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
handleImageChange: (file: File) => void;
|
handleImageChange: (file: File) => void;
|
||||||
|
description?: string;
|
||||||
|
accept?: ImageFormat[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UploadImageModal: React.FC<ModalkaProps> = ({
|
export const UploadImageModal: React.FC<ModalkaProps> = ({
|
||||||
handleImageChange,
|
handleImageChange,
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
|
accept,
|
||||||
|
description,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const dropZone = useRef<HTMLDivElement>(null);
|
const dropZone = useRef<HTMLDivElement>(null);
|
||||||
const [ready, setReady] = useState(false);
|
const [ready, setReady] = useState(false);
|
||||||
|
|
||||||
const handleDragEnter = (event: DragEvent<HTMLDivElement>) => {
|
const handleDragEnter = (event: DragEvent<HTMLDivElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setReady(true);
|
setReady(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
|
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
const file = event.dataTransfer.files[0];
|
const file = event.dataTransfer.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
handleImageChange(file);
|
handleImageChange(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const acceptedFormats = accept ? accept.map((format) => "." + format).join(", ") : "";
|
||||||
<Modal
|
|
||||||
open={isOpen}
|
console.log(acceptedFormats);
|
||||||
onClose={onClose}
|
|
||||||
aria-labelledby="modal-modal-title"
|
return (
|
||||||
aria-describedby="modal-modal-description"
|
<Modal
|
||||||
|
open={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
aria-labelledby="modal-modal-title"
|
||||||
|
aria-describedby="modal-modal-description"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
maxWidth: "690px",
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
borderRadius: "12px",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 0,
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
padding: "20px",
|
||||||
|
background: theme.palette.background.default,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
|
<Typography sx={{ marginBottom: "20px", fontWeight: "bold", color: "#4D4D4D" }}>
|
||||||
|
Добавьте изображение
|
||||||
|
</Typography>
|
||||||
|
<ButtonBase component="label" sx={{ justifyContent: "flex-start" }}>
|
||||||
|
<input
|
||||||
|
onChange={(event) => event.target.files?.[0] && handleImageChange(event.target.files[0])}
|
||||||
|
hidden
|
||||||
|
accept={acceptedFormats || ".jpg, .jpeg, .png , .gif"}
|
||||||
|
multiple
|
||||||
|
type="file"
|
||||||
|
data-cy="upload-image-input"
|
||||||
|
/>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
onDragOver={(event: DragEvent<HTMLDivElement>) => event.preventDefault()}
|
||||||
position: "absolute",
|
onDrop={handleDrop}
|
||||||
top: "50%",
|
ref={dropZone}
|
||||||
left: "50%",
|
sx={{
|
||||||
transform: "translate(-50%, -50%)",
|
width: "580px",
|
||||||
maxWidth: "690px",
|
padding: "33px 10px 33px 55px",
|
||||||
bgcolor: "background.paper",
|
display: "flex",
|
||||||
borderRadius: "12px",
|
alignItems: "center",
|
||||||
boxShadow: 24,
|
backgroundColor: theme.palette.background.default,
|
||||||
p: 0,
|
border: `1px solid ${ready ? "red" : theme.palette.grey2.main}`,
|
||||||
overflow: "hidden",
|
borderRadius: "8px",
|
||||||
}}
|
gap: "55px",
|
||||||
|
}}
|
||||||
|
onDragEnter={handleDragEnter} // Применяем обработчик onDragEnter напрямую
|
||||||
>
|
>
|
||||||
<Box
|
<UploadIcon />
|
||||||
sx={{
|
<Box>
|
||||||
display: "flex",
|
<Typography sx={{ color: "#9A9AAF", fontWeight: "bold" }}>
|
||||||
flexDirection: "column",
|
Загрузите или перетяните сюда файл
|
||||||
padding: "20px",
|
</Typography>
|
||||||
background: theme.palette.background.default,
|
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>
|
||||||
}}
|
{description || "Принимает JPG, PNG, и GIF формат — максимум 5mb"}
|
||||||
>
|
</Typography>
|
||||||
<Typography
|
</Box>
|
||||||
sx={{ marginBottom: "20px", fontWeight: "bold", color: "#4D4D4D" }}
|
|
||||||
>
|
|
||||||
Добавьте изображение
|
|
||||||
</Typography>
|
|
||||||
<ButtonBase component="label" sx={{ justifyContent: "flex-start" }}>
|
|
||||||
<input
|
|
||||||
onChange={(event) => event.target.files?.[0] && handleImageChange(event.target.files[0])}
|
|
||||||
hidden
|
|
||||||
accept="image/*"
|
|
||||||
multiple
|
|
||||||
type="file"
|
|
||||||
data-cy="upload-image-input"
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
onDragOver={(event: DragEvent<HTMLDivElement>) =>
|
|
||||||
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 напрямую
|
|
||||||
>
|
|
||||||
<UploadIcon />
|
|
||||||
<Box>
|
|
||||||
<Typography sx={{ color: "#9A9AAF", fontWeight: "bold" }}>
|
|
||||||
Загрузите или перетяните сюда файл
|
|
||||||
</Typography>
|
|
||||||
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>
|
|
||||||
Принимает JPG, PNG, и GIF формат — максимум 5mb
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</ButtonBase>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
margin: "20px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#4D4D4D",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Или выберите на фотостоке
|
|
||||||
</Typography>
|
|
||||||
<img src={UnsplashIcon} alt="" />
|
|
||||||
</Box>
|
|
||||||
<TextField
|
|
||||||
id="search-in-unsplash"
|
|
||||||
placeholder="Ищите изображения на английском языка"
|
|
||||||
sx={{
|
|
||||||
"& .MuiInputBase-input": {
|
|
||||||
height: "48px",
|
|
||||||
padding: "0 10px 0 0",
|
|
||||||
},
|
|
||||||
"& .MuiOutlinedInput-notchedOutline": {
|
|
||||||
borderRadius: "8px",
|
|
||||||
},
|
|
||||||
"& .Mui-focused .MuiOutlinedInput-notchedOutline": {
|
|
||||||
border: "1px solid rgba(0, 0, 0, 0.23)",
|
|
||||||
},
|
|
||||||
"& .MuiInputBase-root.MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline":
|
|
||||||
{
|
|
||||||
borderColor: "rgba(0, 0, 0, 0.23)",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
startAdornment: (
|
|
||||||
<InputAdornment
|
|
||||||
position="start"
|
|
||||||
sx={{
|
|
||||||
outline: "none",
|
|
||||||
"& svg > path": { stroke: "#9A9AAF" },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SearchIcon />
|
|
||||||
</InputAdornment>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Modal>
|
</ButtonBase>
|
||||||
);
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
margin: "20px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontWeight: "bold",
|
||||||
|
color: "#4D4D4D",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Или выберите на фотостоке
|
||||||
|
</Typography>
|
||||||
|
<img src={UnsplashIcon} alt="" />
|
||||||
|
</Box>
|
||||||
|
<TextField
|
||||||
|
id="search-in-unsplash"
|
||||||
|
placeholder="Ищите изображения на английском языка"
|
||||||
|
sx={{
|
||||||
|
"& .MuiInputBase-input": {
|
||||||
|
height: "48px",
|
||||||
|
padding: "0 10px 0 0",
|
||||||
|
},
|
||||||
|
"& .MuiOutlinedInput-notchedOutline": {
|
||||||
|
borderRadius: "8px",
|
||||||
|
},
|
||||||
|
"& .Mui-focused .MuiOutlinedInput-notchedOutline": {
|
||||||
|
border: "1px solid rgba(0, 0, 0, 0.23)",
|
||||||
|
},
|
||||||
|
"& .MuiInputBase-root.MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline": {
|
||||||
|
borderColor: "rgba(0, 0, 0, 0.23)",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment
|
||||||
|
position="start"
|
||||||
|
sx={{
|
||||||
|
outline: "none",
|
||||||
|
"& svg > path": { stroke: "#9A9AAF" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SearchIcon />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
|
import { ResultSettings } from "./ResultSettings";
|
||||||
import { ResultSettings } from "./ResultSettings"
|
|
||||||
import { createFrontResult } from "@root/questions/actions";
|
import { createFrontResult } from "@root/questions/actions";
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
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 image from "../../assets/Rectangle 110.png";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
|
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
||||||
|
import { decrementCurrentStep } from "@root/quizes/actions";
|
||||||
|
|
||||||
export const FirstEntry = () => {
|
export const FirstEntry = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -17,16 +18,20 @@ export const FirstEntry = () => {
|
|||||||
const create = () => {
|
const create = () => {
|
||||||
if (quiz?.config.haveRoot) {
|
if (quiz?.config.haveRoot) {
|
||||||
questions
|
questions
|
||||||
.filter((question:AnyTypedQuizQuestion) => {
|
.filter((question: AnyTypedQuizQuestion) => {
|
||||||
return question.type !== null && question.content.rule.parentId.length !== 0 && question.content.rule.children.length === 0
|
return (
|
||||||
})
|
question.type !== null &&
|
||||||
.forEach(question => {
|
question.content.rule.parentId.length !== 0 &&
|
||||||
createFrontResult(quiz.id, question.content.id)
|
question.content.rule.children.length === 0
|
||||||
})
|
);
|
||||||
|
})
|
||||||
|
.forEach((question) => {
|
||||||
|
createFrontResult(quiz.backendId, question.content.id);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
createFrontResult(quiz.id, "line")
|
createFrontResult(quiz.backendId, "line");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -53,7 +58,7 @@ export const FirstEntry = () => {
|
|||||||
mr: !isSmallMonitor ? "104px" : 0,
|
mr: !isSmallMonitor ? "104px" : 0,
|
||||||
marginBottom: isSmallMonitor ? "20px" : 0,
|
marginBottom: isSmallMonitor ? "20px" : 0,
|
||||||
position: "relative",
|
position: "relative",
|
||||||
height: "100%"
|
height: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h5" sx={{ marginBottom: "20px" }}>
|
<Typography variant="h5" sx={{ marginBottom: "20px" }}>
|
||||||
@ -69,7 +74,10 @@ export const FirstEntry = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{ color: "#4D4D4D", width: "95%" }}>
|
<Typography sx={{ color: "#4D4D4D", width: "95%" }}>
|
||||||
Вы можете показывать разные результаты квиза (добавьте описание, изображение, стоимость и т.п.) разным пользователям, нужно только их создать и проставить условия. Таким образом ваш квиз получится максимально индивидуальным для каждого клиента. Показывайте картинку/видео вместо результата или переадресовывайте пользователя по нужной ссылке.
|
Вы можете показывать разные результаты квиза (добавьте описание, изображение, стоимость и т.п.) разным
|
||||||
|
пользователям, нужно только их создать и проставить условия. Таким образом ваш квиз получится максимально
|
||||||
|
индивидуальным для каждого клиента. Показывайте картинку/видео вместо результата или переадресовывайте
|
||||||
|
пользователя по нужной ссылке.
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
@ -91,21 +99,35 @@ export const FirstEntry = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Button
|
|
||||||
onClick={create}
|
<Box sx={{ display: "flex", justifyContent: "flex-start", alignItems: "center", gap: "8px", mt: "30px" }}>
|
||||||
variant="contained"
|
<Button
|
||||||
sx={{
|
variant="outlined"
|
||||||
backgroundColor: "#7E2AEA",
|
sx={{
|
||||||
fontSize: "18px",
|
padding: "10px 20px",
|
||||||
lineHeight: "18px",
|
borderRadius: "8px",
|
||||||
width: "216px",
|
height: "44px",
|
||||||
height: "44px",
|
}}
|
||||||
mt: "30px",
|
onClick={decrementCurrentStep}
|
||||||
p: "10px 20px"
|
>
|
||||||
}}
|
<ArrowLeft />
|
||||||
>
|
</Button>
|
||||||
Создать результаты
|
<Button
|
||||||
</Button>
|
onClick={create}
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#7E2AEA",
|
||||||
|
fontSize: "18px",
|
||||||
|
lineHeight: "18px",
|
||||||
|
width: "216px",
|
||||||
|
height: "44px",
|
||||||
|
|
||||||
|
p: "10px 20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Создать результаты
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
33
src/pages/ViewPublicationPage/ContactForm.tsx
Normal file
33
src/pages/ViewPublicationPage/ContactForm.tsx
Normal file
@ -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 (
|
||||||
|
<Box>
|
||||||
|
<Typography>Форма контактов</Typography>
|
||||||
|
{!showResultForm && quiz?.config.resultInfo.when === "after" && (
|
||||||
|
<Button variant="contained" onClick={followNextForm}>
|
||||||
|
Показать результат
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
@ -2,6 +2,8 @@ import { useState, useEffect } from "react";
|
|||||||
import { Box, Button, useTheme } from "@mui/material";
|
import { Box, Button, useTheme } from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore } from "@root/quizView";
|
import { useQuizViewStore } from "@root/quizView";
|
||||||
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AnyTypedQuizQuestion,
|
AnyTypedQuizQuestion,
|
||||||
@ -11,20 +13,26 @@ import { getQuestionByContentId } from "@root/questions/actions";
|
|||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
|
|
||||||
type FooterProps = {
|
type FooterProps = {
|
||||||
questions: AnyTypedQuizQuestion[];
|
|
||||||
setCurrentQuestion: (step: AnyTypedQuizQuestion) => void;
|
setCurrentQuestion: (step: AnyTypedQuizQuestion) => void;
|
||||||
question: AnyTypedQuizQuestion;
|
question: AnyTypedQuizQuestion;
|
||||||
|
setShowContactForm: (show: boolean) => void;
|
||||||
|
setShowResultForm: (show: boolean) => void;
|
||||||
|
setResultQuestion: (id: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Footer = ({
|
export const Footer = ({
|
||||||
setCurrentQuestion,
|
setCurrentQuestion,
|
||||||
questions,
|
|
||||||
question,
|
question,
|
||||||
|
setShowContactForm,
|
||||||
|
setShowResultForm,
|
||||||
|
setResultQuestion,
|
||||||
}: FooterProps) => {
|
}: FooterProps) => {
|
||||||
const [disablePreviousButton, setDisablePreviousButton] =
|
const [disablePreviousButton, setDisablePreviousButton] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const [disableNextButton, setDisableNextButton] = useState<boolean>(false);
|
const [disableNextButton, setDisableNextButton] = useState<boolean>(false);
|
||||||
|
const quiz = useCurrentQuiz();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
|
const questions = useQuestionsStore().questions as AnyTypedQuizQuestion[];
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const linear = !questions.find(
|
const linear = !questions.find(
|
||||||
({ content }) => content.rule.parentId === "root"
|
({ content }) => content.rule.parentId === "root"
|
||||||
@ -72,32 +80,42 @@ export const Footer = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (linear) {
|
if (linear) {
|
||||||
const questionIndex = questions.findIndex(({ id }) => id === question.id);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const nextQuestion = questions[questionIndex + 1];
|
const nextQuestionId = getNextQuestionId();
|
||||||
|
if (nextQuestionId) {
|
||||||
if (nextQuestion) {
|
setDisableNextButton(false);
|
||||||
setDisableNextButton(false);
|
|
||||||
} else {
|
|
||||||
setDisableNextButton(true);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const nextQuestionId = getNextQuestionId();
|
const nextQuestion = getQuestionByContentId(
|
||||||
if (nextQuestionId) {
|
question.content.rule.default
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nextQuestion?.type) {
|
||||||
setDisableNextButton(false);
|
setDisableNextButton(false);
|
||||||
} else {
|
|
||||||
const nextQuestion = getQuestionByContentId(
|
|
||||||
question.content.rule.default
|
|
||||||
);
|
|
||||||
if (nextQuestion?.type) {
|
|
||||||
setDisableNextButton(false);
|
|
||||||
} else {
|
|
||||||
setDisableNextButton(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [question, answers]);
|
}, [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 = () => {
|
const getNextQuestionId = () => {
|
||||||
if (answers.length) {
|
if (answers.length) {
|
||||||
const answer = answers.find(
|
const answer = answers.find(
|
||||||
@ -171,11 +189,12 @@ export const Footer = ({
|
|||||||
const followNextStep = () => {
|
const followNextStep = () => {
|
||||||
if (linear) {
|
if (linear) {
|
||||||
const questionIndex = questions.findIndex(({ id }) => id === question.id);
|
const questionIndex = questions.findIndex(({ id }) => id === question.id);
|
||||||
|
|
||||||
const nextQuestion = questions[questionIndex + 1];
|
const nextQuestion = questions[questionIndex + 1];
|
||||||
|
|
||||||
if (nextQuestion) {
|
if (nextQuestion && nextQuestion.type !== "result") {
|
||||||
setCurrentQuestion(nextQuestion);
|
setCurrentQuestion(nextQuestion);
|
||||||
|
} else {
|
||||||
|
showResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -186,7 +205,7 @@ export const Footer = ({
|
|||||||
if (nextQuestionId) {
|
if (nextQuestionId) {
|
||||||
const nextQuestion = getQuestionByContentId(nextQuestionId);
|
const nextQuestion = getQuestionByContentId(nextQuestionId);
|
||||||
|
|
||||||
if (nextQuestion?.type) {
|
if (nextQuestion?.type && nextQuestion.type !== "result") {
|
||||||
setCurrentQuestion(nextQuestion);
|
setCurrentQuestion(nextQuestion);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@ -196,10 +215,10 @@ export const Footer = ({
|
|||||||
const nextQuestion = getQuestionByContentId(
|
const nextQuestion = getQuestionByContentId(
|
||||||
question.content.rule.default
|
question.content.rule.default
|
||||||
);
|
);
|
||||||
if (nextQuestion?.type) {
|
if (nextQuestion?.type && nextQuestion.type !== "result") {
|
||||||
setCurrentQuestion(nextQuestion);
|
setCurrentQuestion(nextQuestion);
|
||||||
} else {
|
} else {
|
||||||
enqueueSnackbar("не могу получить последующий вопрос (дефолтный)");
|
showResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
import { getQuestionByContentId } from "@root/questions/actions";
|
||||||
|
|
||||||
import { Variant } from "./questions/Variant";
|
import { Variant } from "./questions/Variant";
|
||||||
import { Images } from "./questions/Images";
|
import { Images } from "./questions/Images";
|
||||||
import { Varimg } from "./questions/Varimg";
|
import { Varimg } from "./questions/Varimg";
|
||||||
@ -12,12 +16,12 @@ import { File } from "./questions/File";
|
|||||||
import { Page } from "./questions/Page";
|
import { Page } from "./questions/Page";
|
||||||
import { Rating } from "./questions/Rating";
|
import { Rating } from "./questions/Rating";
|
||||||
import { Footer } from "./Footer";
|
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 { QuestionType } from "../../model/question/question";
|
||||||
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
|
||||||
import { getQuestionByContentId } from "@root/questions/actions";
|
|
||||||
|
|
||||||
type QuestionProps = {
|
type QuestionProps = {
|
||||||
questions: AnyTypedQuizQuestion[];
|
questions: AnyTypedQuizQuestion[];
|
||||||
@ -41,6 +45,9 @@ export const Question = ({ questions }: QuestionProps) => {
|
|||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const [currentQuestion, setCurrentQuestion] =
|
const [currentQuestion, setCurrentQuestion] =
|
||||||
useState<AnyTypedQuizQuestion>();
|
useState<AnyTypedQuizQuestion>();
|
||||||
|
const [showContactForm, setShowContactForm] = useState<boolean>(false);
|
||||||
|
const [showResultForm, setShowResultForm] = useState<boolean>(false);
|
||||||
|
const [resultQuestion, setResultQuestion] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const nextQuestion = getQuestionByContentId(quiz?.config.haveRoot || "");
|
const nextQuestion = getQuestionByContentId(quiz?.config.haveRoot || "");
|
||||||
@ -70,13 +77,41 @@ export const Question = ({ questions }: QuestionProps) => {
|
|||||||
margin: "0 auto",
|
margin: "0 auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<QuestionComponent currentQuestion={currentQuestion} />
|
{!showContactForm && !showResultForm && !resultQuestion && (
|
||||||
|
<QuestionComponent currentQuestion={currentQuestion} />
|
||||||
|
)}
|
||||||
|
{resultQuestion && (
|
||||||
|
<ResultQuestion
|
||||||
|
resultQuestion={resultQuestion}
|
||||||
|
setResultQuestion={setResultQuestion}
|
||||||
|
setShowContactForm={setShowContactForm}
|
||||||
|
setShowResultForm={setShowResultForm}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showContactForm && (
|
||||||
|
<ContactForm
|
||||||
|
showResultForm={showResultForm}
|
||||||
|
setShowContactForm={setShowContactForm}
|
||||||
|
setShowResultForm={setShowResultForm}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showResultForm && (
|
||||||
|
<ResultForm
|
||||||
|
showContactForm={showContactForm}
|
||||||
|
setShowContactForm={setShowContactForm}
|
||||||
|
setShowResultForm={setShowResultForm}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Footer
|
{!showContactForm && !showResultForm && !resultQuestion && (
|
||||||
questions={questions}
|
<Footer
|
||||||
question={currentQuestion}
|
question={currentQuestion}
|
||||||
setCurrentQuestion={setCurrentQuestion}
|
setCurrentQuestion={setCurrentQuestion}
|
||||||
/>
|
setShowContactForm={setShowContactForm}
|
||||||
|
setShowResultForm={setShowResultForm}
|
||||||
|
setResultQuestion={setResultQuestion}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
33
src/pages/ViewPublicationPage/ResultForm.tsx
Normal file
33
src/pages/ViewPublicationPage/ResultForm.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { Box, Typography, Button } from "@mui/material";
|
||||||
|
|
||||||
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
|
||||||
|
type ResultFormProps = {
|
||||||
|
showContactForm: boolean;
|
||||||
|
setShowContactForm: (show: boolean) => void;
|
||||||
|
setShowResultForm: (show: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ResultForm = ({
|
||||||
|
showContactForm,
|
||||||
|
setShowContactForm,
|
||||||
|
setShowResultForm,
|
||||||
|
}: ResultFormProps) => {
|
||||||
|
const quiz = useCurrentQuiz();
|
||||||
|
|
||||||
|
const followNextForm = () => {
|
||||||
|
setShowResultForm(false);
|
||||||
|
setShowContactForm(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Typography>Форма результатов</Typography>
|
||||||
|
{!showContactForm && quiz?.config.resultInfo.when !== "after" && (
|
||||||
|
<Button variant="contained" onClick={followNextForm}>
|
||||||
|
Показать форму контактов
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
43
src/pages/ViewPublicationPage/ResultQuestion.tsx
Normal file
43
src/pages/ViewPublicationPage/ResultQuestion.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { Box, Typography, Button } from "@mui/material";
|
||||||
|
|
||||||
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
|
|
||||||
|
type ResultQuestionProps = {
|
||||||
|
resultQuestion: string;
|
||||||
|
setResultQuestion: (id: string) => void;
|
||||||
|
setShowContactForm: (show: boolean) => void;
|
||||||
|
setShowResultForm: (show: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ResultQuestion = ({
|
||||||
|
resultQuestion,
|
||||||
|
setResultQuestion,
|
||||||
|
setShowContactForm,
|
||||||
|
setShowResultForm,
|
||||||
|
}: ResultQuestionProps) => {
|
||||||
|
const quiz = useCurrentQuiz();
|
||||||
|
const { questions } = useQuestionsStore();
|
||||||
|
|
||||||
|
const followNextForm = () => {
|
||||||
|
setResultQuestion("");
|
||||||
|
|
||||||
|
if (quiz?.config.resultInfo.when === "after") {
|
||||||
|
setShowContactForm(true);
|
||||||
|
} else {
|
||||||
|
setShowResultForm(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Typography>Вопрос результат</Typography>
|
||||||
|
<Typography>
|
||||||
|
{JSON.stringify(questions.find(({ id }) => id === resultQuestion))}
|
||||||
|
</Typography>
|
||||||
|
<Button variant="contained" onClick={followNextForm}>
|
||||||
|
Далее
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
@ -25,18 +25,17 @@ export const ViewPage = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const quizes = await quizApi.getList()
|
const quizes = await quizApi.getList();
|
||||||
setQuizes(quizes)
|
setQuizes(quizes);
|
||||||
|
|
||||||
const questions = await questionApi.getList({ quiz_id: editQuizId })
|
const questions = await questionApi.getList({ quiz_id: editQuizId });
|
||||||
setQuestions(questions)
|
setQuestions(questions);
|
||||||
}
|
};
|
||||||
getData()
|
getData();
|
||||||
}, [])
|
}, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setVisualStartPage(quiz?.config.noStartPage)
|
setVisualStartPage(quiz?.config.noStartPage);
|
||||||
}, [questions])
|
}, [questions]);
|
||||||
|
|
||||||
|
|
||||||
const [visualStartPage, setVisualStartPage] = useState<boolean>();
|
const [visualStartPage, setVisualStartPage] = useState<boolean>();
|
||||||
|
|
||||||
@ -52,8 +51,8 @@ export const ViewPage = () => {
|
|||||||
questions.filter(({ type }) => type) as AnyTypedQuizQuestion[]
|
questions.filter(({ type }) => type) as AnyTypedQuizQuestion[]
|
||||||
).sort((previousItem, item) => previousItem.page - item.page);
|
).sort((previousItem, item) => previousItem.page - item.page);
|
||||||
|
|
||||||
console.log("visualStartPage ", visualStartPage)
|
console.log("visualStartPage ", visualStartPage);
|
||||||
if (visualStartPage === undefined) return <></>
|
if (visualStartPage === undefined) return <></>;
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{!visualStartPage ? (
|
{!visualStartPage ? (
|
||||||
|
@ -15,32 +15,24 @@ type NumberProps = {
|
|||||||
|
|
||||||
export const Number = ({ currentQuestion }: NumberProps) => {
|
export const Number = ({ currentQuestion }: NumberProps) => {
|
||||||
const [minRange, setMinRange] = useState<string>("0");
|
const [minRange, setMinRange] = useState<string>("0");
|
||||||
const [maxRange, setMaxRange] = useState<string>("100");
|
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const updateMinRangeDebounced = useDebouncedCallback(
|
const updateMinRangeDebounced = useDebouncedCallback((value, crowded = false) => {
|
||||||
(value, crowded = false) => {
|
if (crowded) {
|
||||||
if (crowded) {
|
setMinRange(maxRange);
|
||||||
setMinRange(maxRange);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
updateAnswer(currentQuestion.content.id, value);
|
updateAnswer(currentQuestion.content.id, value);
|
||||||
},
|
}, 1000);
|
||||||
1000
|
const updateMaxRangeDebounced = useDebouncedCallback((value, crowded = false) => {
|
||||||
);
|
if (crowded) {
|
||||||
const updateMaxRangeDebounced = useDebouncedCallback(
|
setMaxRange(minRange);
|
||||||
(value, crowded = false) => {
|
}
|
||||||
if (crowded) {
|
|
||||||
setMaxRange(minRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateAnswer(currentQuestion.content.id, value);
|
updateAnswer(currentQuestion.content.id, value);
|
||||||
},
|
}, 1000);
|
||||||
1000
|
const answer = answers.find(({ questionId }) => questionId === currentQuestion.content.id)?.answer as string;
|
||||||
);
|
|
||||||
const answer = answers.find(
|
|
||||||
({ questionId }) => questionId === currentQuestion.content.id
|
|
||||||
)?.answer as string;
|
|
||||||
|
|
||||||
const min = window.Number(currentQuestion.content.range.split("—")[0]);
|
const min = window.Number(currentQuestion.content.range.split("—")[0]);
|
||||||
const max = window.Number(currentQuestion.content.range.split("—")[1]);
|
const max = window.Number(currentQuestion.content.range.split("—")[1]);
|
||||||
|
@ -1,12 +1,4 @@
|
|||||||
import {
|
import { Box, Button, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
SxProps,
|
|
||||||
Theme,
|
|
||||||
Typography,
|
|
||||||
useMediaQuery,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { createQuiz } from "@root/quizes/actions";
|
import { createQuiz } from "@root/quizes/actions";
|
||||||
import { useQuizes } from "@root/quizes/hooks";
|
import { useQuizes } from "@root/quizes/hooks";
|
||||||
import SectionWrapper from "@ui_kit/SectionWrapper";
|
import SectionWrapper from "@ui_kit/SectionWrapper";
|
||||||
@ -16,72 +8,62 @@ import ComplexNavText from "./ComplexNavText";
|
|||||||
import FirstQuiz from "./FirstQuiz";
|
import FirstQuiz from "./FirstQuiz";
|
||||||
import QuizCard from "./QuizCard";
|
import QuizCard from "./QuizCard";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
outerContainerSx?: SxProps<Theme>;
|
outerContainerSx?: SxProps<Theme>;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MyQuizzesFull({
|
export default function MyQuizzesFull({ outerContainerSx: sx, children }: Props) {
|
||||||
outerContainerSx: sx,
|
const { quizes } = useQuizes();
|
||||||
children,
|
const navigate = useNavigate();
|
||||||
}: Props) {
|
const theme = useTheme();
|
||||||
const { quizes } = useQuizes();
|
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
||||||
const navigate = useNavigate();
|
|
||||||
const theme = useTheme();
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{quizes.length === 0 ? (
|
{quizes.length === 0 ? (
|
||||||
<FirstQuiz />
|
<FirstQuiz />
|
||||||
) : (
|
) : (
|
||||||
<SectionWrapper maxWidth="lg">
|
<SectionWrapper maxWidth="lg">
|
||||||
<ComplexNavText text1="Кабинет квизов" />
|
<ComplexNavText text1="Кабинет квизов" />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
mt: "20px",
|
mt: "20px",
|
||||||
mb: "30px",
|
mb: "30px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h4">Мои квизы</Typography>
|
<Typography variant="h4">Мои квизы</Typography>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile ? "10px" : "10px 47px",
|
padding: isMobile ? "10px" : "10px 47px",
|
||||||
minWidth: "44px",
|
minWidth: "44px",
|
||||||
}}
|
}}
|
||||||
onClick={() => createQuiz(navigate)}
|
onClick={() => createQuiz(navigate)}
|
||||||
data-cy="create-quiz"
|
data-cy="create-quiz"
|
||||||
>
|
>
|
||||||
{isMobile ? "+" : "Создать +"}
|
{isMobile ? "+" : "Создать +"}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
py: "10px",
|
py: "10px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
gap: "40px",
|
gap: "40px",
|
||||||
mb: "60px",
|
mb: "60px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{quizes.map(quiz => (
|
{quizes.map((quiz) => (
|
||||||
<QuizCard
|
<QuizCard key={quiz.id} quiz={quiz} openCount={0} applicationCount={0} conversionPercent={0} />
|
||||||
key={quiz.id}
|
))}
|
||||||
quiz={quiz}
|
</Box>
|
||||||
openCount={0}
|
{children}
|
||||||
applicationCount={0}
|
</SectionWrapper>
|
||||||
conversionPercent={0}
|
)}
|
||||||
/>
|
</>
|
||||||
))}
|
);
|
||||||
</Box>
|
|
||||||
{children}
|
|
||||||
</SectionWrapper>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,9 @@ import { useEffect, useState } from "react";
|
|||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { SidebarMobile } from "./Sidebar/SidebarMobile";
|
import { SidebarMobile } from "./Sidebar/SidebarMobile";
|
||||||
import { cleanQuestions } from "@root/questions/actions";
|
import { cleanQuestions, setQuestions } from "@root/questions/actions";
|
||||||
import { updateOpenBranchingPanel } from "@root/uiTools/actions";
|
import { updateOpenBranchingPanel } from "@root/uiTools/actions";
|
||||||
import { BranchingPanel } from "../Questions/BranchingPanel";
|
import { BranchingPanel } from "../Questions/BranchingPanel";
|
||||||
import { setQuestions } from "@root/questions/actions";
|
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { useQuizes } from "@root/quizes/hooks";
|
import { useQuizes } from "@root/quizes/hooks";
|
||||||
import { questionApi } from "@api/question";
|
import { questionApi } from "@api/question";
|
||||||
@ -52,6 +51,7 @@ export default function EditPage() {
|
|||||||
|
|
||||||
const questions = await questionApi.getList({ quiz_id: editQuizId })
|
const questions = await questionApi.getList({ quiz_id: editQuizId })
|
||||||
setQuestions(questions)
|
setQuestions(questions)
|
||||||
|
|
||||||
}
|
}
|
||||||
getData()
|
getData()
|
||||||
}, [])
|
}, [])
|
||||||
@ -245,7 +245,7 @@ export default function EditPage() {
|
|||||||
left: 0,
|
left: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
padding: "20px 40px",
|
padding: isMobile ? "20px 16px" : "20px 40px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "flex-end",
|
justifyContent: "flex-end",
|
||||||
gap: "15px",
|
gap: "15px",
|
||||||
@ -320,16 +320,20 @@ export default function EditPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Button
|
<a href={`/view`} target="_blank" rel="noreferrer" style={{ textDecoration: "none" }}>
|
||||||
variant="contained"
|
<Button
|
||||||
sx={{
|
variant="contained"
|
||||||
fontSize: "14px",
|
sx={{
|
||||||
lineHeight: "18px",
|
fontSize: "14px",
|
||||||
height: "34px",
|
lineHeight: "18px",
|
||||||
}}
|
height: "34px",
|
||||||
>
|
minWidth: "130px"
|
||||||
Опубликовать
|
}}
|
||||||
</Button>
|
>
|
||||||
|
Опубликовать
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,120 +6,126 @@ import { useState } from "react";
|
|||||||
import { UploadImageModal } from "../../pages/Questions/UploadImage/UploadImageModal";
|
import { UploadImageModal } from "../../pages/Questions/UploadImage/UploadImageModal";
|
||||||
import { useDisclosure } from "../../utils/useDisclosure";
|
import { useDisclosure } from "../../utils/useDisclosure";
|
||||||
|
|
||||||
|
|
||||||
const allowedFileTypes = ["image/png", "image/jpeg", "image/gif"];
|
const allowedFileTypes = ["image/png", "image/jpeg", "image/gif"];
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
imageUrl: string | null;
|
imageUrl: string | null;
|
||||||
onImageUploadClick: (image: Blob) => void;
|
onImageUploadClick: (image: Blob) => void;
|
||||||
onDeleteClick: () => void;
|
onDeleteClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FaviconDropZone({ imageUrl, onImageUploadClick, onDeleteClick }: Props) {
|
export default function FaviconDropZone({ imageUrl, onImageUploadClick, onDeleteClick }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const [isDropReady, setIsDropReady] = useState<boolean>(false);
|
const [isDropReady, setIsDropReady] = useState<boolean>(false);
|
||||||
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
||||||
|
|
||||||
if (!quiz) return null;
|
if (!quiz) return null;
|
||||||
|
|
||||||
async function handleImageUpload(file: File) {
|
async function handleImageUpload(file: File) {
|
||||||
if (file.size > 5 * 2 ** 20) return enqueueSnackbar("Размер картинки слишком велик");
|
if (file.size > 5 * 2 ** 20) return enqueueSnackbar("Размер картинки слишком велик");
|
||||||
if (!allowedFileTypes.includes(file.type)) return enqueueSnackbar("Допустимые форматы изображений: png, jpeg, gif");
|
if (!allowedFileTypes.includes(file.type)) return enqueueSnackbar("Допустимые форматы изображений: png, jpeg, gif");
|
||||||
|
|
||||||
onImageUploadClick(file);
|
onImageUploadClick(file);
|
||||||
closeImageUploadModal();
|
closeImageUploadModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
const onDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setIsDropReady(false);
|
setIsDropReady(false);
|
||||||
|
|
||||||
const file = event.dataTransfer.files[0];
|
const file = event.dataTransfer.files[0];
|
||||||
if (!file || imageUrl) return;
|
if (!file || imageUrl) return;
|
||||||
|
|
||||||
handleImageUpload(file);
|
handleImageUpload(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<UploadImageModal
|
||||||
|
isOpen={isImageUploadOpen}
|
||||||
|
onClose={closeImageUploadModal}
|
||||||
|
handleImageChange={handleImageUpload}
|
||||||
|
description="Принимает JPG, PNG — максимум 5mb"
|
||||||
|
accept={["jpeg", "jpg", "png"]}
|
||||||
|
/>
|
||||||
|
<Box
|
||||||
|
onDragEnter={() => !imageUrl && setIsDropReady(true)}
|
||||||
|
onDragExit={() => setIsDropReady(false)}
|
||||||
|
onDragOver={(e) => e.preventDefault()}
|
||||||
|
onDrop={onDrop}
|
||||||
|
sx={{
|
||||||
|
width: "48px",
|
||||||
|
height: "48px",
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
border: `1px solid ${isDropReady ? "red" : theme.palette.grey2.main}`,
|
||||||
|
borderRadius: "8px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ButtonBase
|
||||||
|
onClick={imageUrl ? undefined : openImageUploadModal}
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
gap: "10px",
|
justifyContent: "center",
|
||||||
}}>
|
alignItems: "center",
|
||||||
<UploadImageModal
|
borderRadius: "8px",
|
||||||
isOpen={isImageUploadOpen}
|
overflow: "hidden",
|
||||||
onClose={closeImageUploadModal}
|
}}
|
||||||
handleImageChange={handleImageUpload}
|
>
|
||||||
|
{imageUrl ? (
|
||||||
|
<img
|
||||||
|
src={imageUrl}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
objectFit: "scale-down",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Box
|
) : (
|
||||||
onDragEnter={() => !imageUrl && setIsDropReady(true)}
|
<UploadIcon />
|
||||||
onDragExit={() => setIsDropReady(false)}
|
)}
|
||||||
onDragOver={e => e.preventDefault()}
|
</ButtonBase>
|
||||||
onDrop={onDrop}
|
</Box>
|
||||||
sx={{
|
<Box
|
||||||
width: "48px",
|
sx={{
|
||||||
height: "48px",
|
display: "flex",
|
||||||
backgroundColor: theme.palette.background.default,
|
flexDirection: "column",
|
||||||
border: `1px solid ${isDropReady ? "red" : theme.palette.grey2.main}`,
|
alignItems: "start",
|
||||||
borderRadius: "8px",
|
}}
|
||||||
}}>
|
>
|
||||||
<ButtonBase
|
{imageUrl && (
|
||||||
onClick={imageUrl ? undefined : openImageUploadModal}
|
<ButtonBase onClick={onDeleteClick}>
|
||||||
sx={{
|
<Typography
|
||||||
width: "100%",
|
sx={{
|
||||||
height: "100%",
|
color: theme.palette.orange.main,
|
||||||
display: "flex",
|
fontSize: "16px",
|
||||||
justifyContent: "center",
|
lineHeight: "19px",
|
||||||
alignItems: "center",
|
textDecoration: "underline",
|
||||||
borderRadius: "8px",
|
}}
|
||||||
overflow: "hidden",
|
>
|
||||||
}}
|
Удалить
|
||||||
>
|
</Typography>
|
||||||
{imageUrl ?
|
</ButtonBase>
|
||||||
<img
|
)}
|
||||||
src={imageUrl}
|
<Typography
|
||||||
style={{
|
sx={{
|
||||||
width: "100%",
|
color: theme.palette.orange.main,
|
||||||
height: "100%",
|
fontSize: "16px",
|
||||||
objectFit: "scale-down",
|
lineHeight: "19px",
|
||||||
}}
|
textDecoration: "underline",
|
||||||
/>
|
mt: "auto",
|
||||||
:
|
}}
|
||||||
<UploadIcon />
|
>
|
||||||
}
|
5 MB максимум
|
||||||
</ButtonBase>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{
|
</Box>
|
||||||
display: "flex",
|
);
|
||||||
flexDirection: "column",
|
}
|
||||||
alignItems: "start",
|
|
||||||
}}>
|
|
||||||
{imageUrl &&
|
|
||||||
<ButtonBase onClick={onDeleteClick}>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.orange.main,
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "19px",
|
|
||||||
textDecoration: "underline",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Удалить
|
|
||||||
</Typography>
|
|
||||||
</ButtonBase>
|
|
||||||
}
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.orange.main,
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "19px",
|
|
||||||
textDecoration: "underline",
|
|
||||||
mt: "auto",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
5 MB максимум
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
@ -2,6 +2,7 @@ import AlignCenterIcon from "@icons/AlignCenterIcon";
|
|||||||
import AlignLeftIcon from "@icons/AlignLeftIcon";
|
import AlignLeftIcon from "@icons/AlignLeftIcon";
|
||||||
import AlignRightIcon from "@icons/AlignRightIcon";
|
import AlignRightIcon from "@icons/AlignRightIcon";
|
||||||
import ArrowDown from "@icons/ArrowDownIcon";
|
import ArrowDown from "@icons/ArrowDownIcon";
|
||||||
|
import ArrowLeft from "@icons/ArrowLeftSP";
|
||||||
import LayoutCenteredIcon from "@icons/LayoutCenteredIcon";
|
import LayoutCenteredIcon from "@icons/LayoutCenteredIcon";
|
||||||
import LayoutExpandedIcon from "@icons/LayoutExpandedIcon";
|
import LayoutExpandedIcon from "@icons/LayoutExpandedIcon";
|
||||||
import LayoutStandartIcon from "@icons/LayoutStandartIcon";
|
import LayoutStandartIcon from "@icons/LayoutStandartIcon";
|
||||||
@ -652,7 +653,7 @@ export default function StartPageSettings() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={() => setFormState("design")}
|
onClick={() => setFormState("design")}
|
||||||
sx={{
|
sx={{
|
||||||
display: "block",
|
display: "flex",
|
||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
padding: "0",
|
padding: "0",
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
@ -662,14 +663,14 @@ export default function StartPageSettings() {
|
|||||||
textUnderlineOffset: "2px",
|
textUnderlineOffset: "2px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
🡰 Вернуться к дизайну
|
<ArrowLeft right={false}/> Вернуться к дизайну
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{formState === "design" && (
|
{formState === "design" && (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setFormState("content")}
|
onClick={() => setFormState("content")}
|
||||||
sx={{
|
sx={{
|
||||||
display: "block",
|
display: "flex",
|
||||||
marginLeft: "auto",
|
marginLeft: "auto",
|
||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
padding: "0",
|
padding: "0",
|
||||||
@ -680,7 +681,7 @@ export default function StartPageSettings() {
|
|||||||
textUnderlineOffset: "2px",
|
textUnderlineOffset: "2px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Далее заполнить контент 🡲
|
Далее заполнить контент <ArrowLeft right={true}/>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -297,6 +297,7 @@ export const uploadQuestionImage = async (
|
|||||||
if (!question || !quizQid) return;
|
if (!question || !quizQid) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log(question.quizId)
|
||||||
const response = await quizApi.addImages(question.quizId, blob);
|
const response = await quizApi.addImages(question.quizId, blob);
|
||||||
|
|
||||||
const values = Object.values(response);
|
const values = Object.values(response);
|
||||||
@ -553,3 +554,4 @@ export const createBackResult = async (
|
|||||||
enqueueSnackbar("Не удалось создать вопрос");
|
enqueueSnackbar("Не удалось создать вопрос");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,74 +1,77 @@
|
|||||||
import {Slider, useTheme} from "@mui/material";
|
import { Slider, useTheme } from "@mui/material";
|
||||||
|
|
||||||
type CustomSliderProps = {
|
type CustomSliderProps = {
|
||||||
defaultValue?: number;
|
defaultValue?: number;
|
||||||
value?: number | number[];
|
value?: number | number[];
|
||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
step?: number;
|
step?: number;
|
||||||
onChange?: (_: Event, value: number | number[]) => void;
|
onChange?: (_: Event, value: number | number[]) => void;
|
||||||
onChangeCommitted?: (_: React.SyntheticEvent | Event, value: number | number[]) => void;
|
onChangeCommitted?: (_: React.SyntheticEvent | Event, value: number | number[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CustomSlider = ({
|
export const CustomSlider = ({
|
||||||
defaultValue,
|
defaultValue,
|
||||||
value,
|
value,
|
||||||
min = 0,
|
min = 0,
|
||||||
max = 100,
|
max = 100,
|
||||||
step,
|
step,
|
||||||
onChange,
|
onChange,
|
||||||
onChangeCommitted
|
onChangeCommitted,
|
||||||
}: CustomSliderProps) => {
|
}: CustomSliderProps) => {
|
||||||
// const handleChange = ({ type }: Event, newValue: number | number[]) => {
|
// const handleChange = ({ type }: Event, newValue: number | number[]) => {
|
||||||
// // Для корректной работы слайдера в FireFox
|
// // Для корректной работы слайдера в FireFox
|
||||||
// if (type !== "change") {
|
// if (type !== "change") {
|
||||||
// onChange?.(e, newValue);
|
// onChange?.(e, newValue);
|
||||||
// }
|
// }
|
||||||
// };
|
// };
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slider
|
<Slider
|
||||||
value={value}
|
value={value}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
min={min}
|
min={min}
|
||||||
max={max}
|
max={max}
|
||||||
step={step}
|
step={step}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
valueLabelDisplay="on"
|
valueLabelDisplay="on"
|
||||||
onChangeCommitted={onChangeCommitted}
|
onChangeCommitted={onChangeCommitted}
|
||||||
onMouseDown={(e) => e.stopPropagation()}
|
onMouseDown={(e) => e.stopPropagation()}
|
||||||
data-cy="slider"
|
data-cy="slider"
|
||||||
sx={{
|
sx={{
|
||||||
color: theme.palette.brightPurple.main,
|
color: theme.palette.brightPurple.main,
|
||||||
padding: "0",
|
padding: "0",
|
||||||
marginTop: "75px",
|
marginTop: "75px",
|
||||||
"& .MuiSlider-valueLabel":{
|
"& .MuiSlider-valueLabel": {
|
||||||
background: theme.palette.brightPurple.main,
|
background: theme.palette.brightPurple.main,
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
width: "60px",
|
minWidth: "60px",
|
||||||
height: "36px"
|
width: "auto",
|
||||||
},
|
whiteSpace: "nowrap",
|
||||||
"& .MuiSlider-valueLabel::before": {
|
textAlign: "center",
|
||||||
width: "6px",
|
height: "36px",
|
||||||
height: "2px",
|
},
|
||||||
transform: "translate(-50%, 50%) rotate(90deg)",
|
"& .MuiSlider-valueLabel::before": {
|
||||||
bottom: "-5px"
|
width: "6px",
|
||||||
},
|
height: "2px",
|
||||||
"& .MuiSlider-rail": {
|
transform: "translate(-50%, 50%) rotate(90deg)",
|
||||||
backgroundColor: "#F2F3F7",
|
bottom: "-5px",
|
||||||
border: `1px solid #9A9AAF`,
|
},
|
||||||
height: "12px"
|
"& .MuiSlider-rail": {
|
||||||
},
|
backgroundColor: "#F2F3F7",
|
||||||
"& .MuiSlider-thumb": {
|
border: `1px solid #9A9AAF`,
|
||||||
border: "3px #f2f3f7 solid",
|
height: "12px",
|
||||||
height: "23px",
|
},
|
||||||
width: "23px"
|
"& .MuiSlider-thumb": {
|
||||||
},
|
border: "3px #f2f3f7 solid",
|
||||||
"& .MuiSlider-track": {
|
height: "23px",
|
||||||
height: "12px"
|
width: "23px",
|
||||||
}
|
},
|
||||||
}}
|
"& .MuiSlider-track": {
|
||||||
/>
|
height: "12px",
|
||||||
);
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
import {
|
import { FormControl, TextField, useTheme, SxProps, Theme } from "@mui/material";
|
||||||
FormControl,
|
|
||||||
TextField,
|
|
||||||
useTheme,
|
|
||||||
SxProps,
|
|
||||||
Theme,
|
|
||||||
} from "@mui/material";
|
|
||||||
|
|
||||||
import type { ChangeEvent, KeyboardEvent, FocusEvent } from "react";
|
import type { ChangeEvent, KeyboardEvent, FocusEvent } from "react";
|
||||||
import type { InputProps } from "@mui/material";
|
import type { InputProps } from "@mui/material";
|
||||||
|
@ -5,6 +5,7 @@ import { decrementCurrentStep } from "@root/quizes/actions";
|
|||||||
import PenaLogo from "../PenaLogo";
|
import PenaLogo from "../PenaLogo";
|
||||||
import CustomAvatar from "./Avatar";
|
import CustomAvatar from "./Avatar";
|
||||||
import NavMenuItem from "./NavMenuItem";
|
import NavMenuItem from "./NavMenuItem";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -25,7 +26,9 @@ export default function Header() {
|
|||||||
zIndex: theme.zIndex.drawer + 1,
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PenaLogo width={124} />
|
<Link to="/">
|
||||||
|
<PenaLogo width={124} />
|
||||||
|
</Link>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -1,11 +1,4 @@
|
|||||||
import {
|
import { Box, Container, IconButton, Typography, useTheme, useMediaQuery } from "@mui/material";
|
||||||
Box,
|
|
||||||
Container,
|
|
||||||
IconButton,
|
|
||||||
Typography,
|
|
||||||
useTheme,
|
|
||||||
useMediaQuery,
|
|
||||||
} from "@mui/material";
|
|
||||||
import NavMenuItem from "./NavMenuItem";
|
import NavMenuItem from "./NavMenuItem";
|
||||||
import PenaLogo from "../PenaLogo";
|
import PenaLogo from "../PenaLogo";
|
||||||
import WalletIcon from "@icons/WalletIcon";
|
import WalletIcon from "@icons/WalletIcon";
|
||||||
@ -13,7 +6,7 @@ import CustomAvatar from "./Avatar";
|
|||||||
import { Burger } from "@icons/Burger";
|
import { Burger } from "@icons/Burger";
|
||||||
import { clearAuthToken } from "@frontend/kitui";
|
import { clearAuthToken } from "@frontend/kitui";
|
||||||
import { logout } from "@api/auth";
|
import { logout } from "@api/auth";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { clearUserData } from "@root/user";
|
import { clearUserData } from "@root/user";
|
||||||
import { LogoutButton } from "@ui_kit/LogoutButton";
|
import { LogoutButton } from "@ui_kit/LogoutButton";
|
||||||
@ -59,7 +52,9 @@ export default function HeaderFull() {
|
|||||||
style={{ fontSize: "30px", color: "#000000", cursor: "pointer" }}
|
style={{ fontSize: "30px", color: "#000000", cursor: "pointer" }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<PenaLogo width={124} />
|
<Link to="/">
|
||||||
|
<PenaLogo width={124} />
|
||||||
|
</Link>
|
||||||
{!isTablet && (
|
{!isTablet && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -93,10 +88,7 @@ export default function HeaderFull() {
|
|||||||
>
|
>
|
||||||
Мой баланс
|
Мой баланс
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography variant="body2" color={theme.palette.brightPurple.main}>
|
||||||
variant="body2"
|
|
||||||
color={theme.palette.brightPurple.main}
|
|
||||||
>
|
|
||||||
00.00 руб.
|
00.00 руб.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -112,12 +104,12 @@ export default function HeaderFull() {
|
|||||||
width: "36px",
|
width: "36px",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<LogoutButton
|
<LogoutButton
|
||||||
onClick={handleLogoutClick}
|
onClick={handleLogoutClick}
|
||||||
sx={{
|
sx={{
|
||||||
ml: "20px",
|
ml: "20px",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -1,330 +1,344 @@
|
|||||||
import { CropIcon } from "@icons/CropIcon";
|
import { CropIcon } from "@icons/CropIcon";
|
||||||
import { ResetIcon } from "@icons/ResetIcon";
|
import { ResetIcon } from "@icons/ResetIcon";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
IconButton,
|
IconButton,
|
||||||
Modal,
|
Modal,
|
||||||
Slider,
|
Slider,
|
||||||
SxProps,
|
SxProps,
|
||||||
Theme,
|
Theme,
|
||||||
Typography,
|
Typography,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { FC, useMemo, useRef, useState } from "react";
|
import { FC, useMemo, useRef, useState } from "react";
|
||||||
import ReactCrop, { Crop, PixelCrop } from "react-image-crop";
|
import ReactCrop, { Crop, PixelCrop } from "react-image-crop";
|
||||||
import "react-image-crop/dist/ReactCrop.css";
|
import "react-image-crop/dist/ReactCrop.css";
|
||||||
import { canvasPreview } from "./utils/canvasPreview";
|
import { canvasPreview } from "./utils/canvasPreview";
|
||||||
|
|
||||||
|
|
||||||
const styleSlider: SxProps<Theme> = {
|
const styleSlider: SxProps<Theme> = {
|
||||||
color: "#7E2AEA",
|
color: "#7E2AEA",
|
||||||
height: "12px",
|
height: "12px",
|
||||||
"& .MuiSlider-track": {
|
"& .MuiSlider-track": {
|
||||||
border: "none",
|
border: "none",
|
||||||
},
|
},
|
||||||
"& .MuiSlider-rail": {
|
"& .MuiSlider-rail": {
|
||||||
backgroundColor: "#F2F3F7",
|
backgroundColor: "#F2F3F7",
|
||||||
border: `1px solid #9A9AAF`,
|
border: `1px solid #9A9AAF`,
|
||||||
},
|
},
|
||||||
"& .MuiSlider-thumb": {
|
"& .MuiSlider-thumb": {
|
||||||
height: 26,
|
height: 26,
|
||||||
width: 26,
|
width: 26,
|
||||||
border: `6px solid #7E2AEA`,
|
border: `6px solid #7E2AEA`,
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
boxShadow: `0px 0px 0px 3px white,
|
boxShadow: `0px 0px 0px 3px white,
|
||||||
0px 4px 4px 3px #C3C8DD`,
|
0px 4px 4px 3px #C3C8DD`,
|
||||||
"&:focus, &:hover, &.Mui-active, &.Mui-focusVisible": {
|
"&:focus, &:hover, &.Mui-active, &.Mui-focusVisible": {
|
||||||
boxShadow: `0px 0px 0px 3px white,
|
boxShadow: `0px 0px 0px 3px white,
|
||||||
0px 4px 4px 3px #C3C8DD`,
|
0px 4px 4px 3px #C3C8DD`,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
imageBlob: Blob | null;
|
imageBlob: Blob | null;
|
||||||
originalImageUrl: string | null;
|
originalImageUrl: string | null;
|
||||||
setCropModalImageBlob: (imageBlob: Blob) => void;
|
setCropModalImageBlob: (imageBlob: Blob) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSaveImageClick: (imageBlob: Blob) => void;
|
onSaveImageClick: (imageBlob: Blob) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CropModal: FC<Props> = ({ isOpen, imageBlob, originalImageUrl, setCropModalImageBlob, onSaveImageClick, onClose }) => {
|
export const CropModal: FC<Props> = ({
|
||||||
const theme = useTheme();
|
isOpen,
|
||||||
const [crop, setCrop] = useState<Crop>();
|
imageBlob,
|
||||||
const [completedCrop, setCompletedCrop] = useState<PixelCrop>();
|
originalImageUrl,
|
||||||
const [darken, setDarken] = useState(0);
|
setCropModalImageBlob,
|
||||||
const [rotate, setRotate] = useState(0);
|
onSaveImageClick,
|
||||||
const [width, setWidth] = useState<number>(240);
|
onClose,
|
||||||
const cropImageElementRef = useRef<HTMLImageElement>(null);
|
}) => {
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(786));
|
const theme = useTheme();
|
||||||
|
const [crop, setCrop] = useState<Crop>();
|
||||||
|
const [completedCrop, setCompletedCrop] = useState<PixelCrop>();
|
||||||
|
const [darken, setDarken] = useState(0);
|
||||||
|
const [rotate, setRotate] = useState(0);
|
||||||
|
const [width, setWidth] = useState<number>(240);
|
||||||
|
const cropImageElementRef = useRef<HTMLImageElement>(null);
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(786));
|
||||||
|
|
||||||
const imageUrl = useMemo(() => imageBlob && URL.createObjectURL(imageBlob), [imageBlob]);
|
const imageUrl = useMemo(() => imageBlob && URL.createObjectURL(imageBlob), [imageBlob]);
|
||||||
|
|
||||||
const handleCropClick = async () => {
|
const handleCropClick = async () => {
|
||||||
if (!completedCrop) throw new Error("No completed crop");
|
if (!completedCrop) throw new Error("No completed crop");
|
||||||
if (!cropImageElementRef.current) throw new Error("No image");
|
if (!cropImageElementRef.current) throw new Error("No image");
|
||||||
|
|
||||||
const canvasCopy = document.createElement("canvas");
|
const canvasCopy = document.createElement("canvas");
|
||||||
const ctx = canvasCopy.getContext("2d");
|
const ctx = canvasCopy.getContext("2d");
|
||||||
if (!ctx) throw new Error("No 2d context");
|
if (!ctx) throw new Error("No 2d context");
|
||||||
|
|
||||||
canvasCopy.width = completedCrop.width;
|
canvasCopy.width = completedCrop.width;
|
||||||
canvasCopy.height = completedCrop.height;
|
canvasCopy.height = completedCrop.height;
|
||||||
ctx.filter = `brightness(${100 - darken}%)`;
|
ctx.filter = `brightness(${100 - darken}%)`;
|
||||||
|
|
||||||
await canvasPreview(cropImageElementRef.current, canvasCopy, completedCrop, rotate);
|
await canvasPreview(cropImageElementRef.current, canvasCopy, completedCrop, rotate);
|
||||||
|
|
||||||
canvasCopy.toBlob((blob) => {
|
canvasCopy.toBlob((blob) => {
|
||||||
if (!blob) throw new Error("Failed to create blob");
|
if (!blob) throw new Error("Failed to create blob");
|
||||||
|
|
||||||
setCropModalImageBlob(blob);
|
setCropModalImageBlob(blob);
|
||||||
setCrop(undefined);
|
setCrop(undefined);
|
||||||
setCompletedCrop(undefined);
|
setCompletedCrop(undefined);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleSaveClick() {
|
function handleSaveClick() {
|
||||||
if (imageBlob) onSaveImageClick?.(imageBlob);
|
if (imageBlob) onSaveImageClick?.(imageBlob);
|
||||||
setCrop(undefined);
|
setCrop(undefined);
|
||||||
setCompletedCrop(undefined);
|
setCompletedCrop(undefined);
|
||||||
onClose();
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleLoadOriginalImage() {
|
||||||
|
if (!originalImageUrl) return;
|
||||||
|
|
||||||
|
const response = await fetch(originalImageUrl);
|
||||||
|
const blob = await response.blob();
|
||||||
|
|
||||||
|
setCropModalImageBlob(blob);
|
||||||
|
setCrop(undefined);
|
||||||
|
setCompletedCrop(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getImageSize = () => {
|
||||||
|
if (cropImageElementRef.current) {
|
||||||
|
const imageWidth = cropImageElementRef.current.naturalWidth;
|
||||||
|
const imageHeight = cropImageElementRef.current.naturalHeight;
|
||||||
|
|
||||||
|
const aspect = imageWidth / imageHeight;
|
||||||
|
|
||||||
|
if (aspect <= 1.333) {
|
||||||
|
setWidth(240);
|
||||||
|
}
|
||||||
|
if (aspect >= 1.5) {
|
||||||
|
setWidth(580);
|
||||||
|
}
|
||||||
|
if (aspect >= 1.778) {
|
||||||
|
setWidth(580);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async function handleLoadOriginalImage() {
|
return (
|
||||||
if (!originalImageUrl) return;
|
<Modal
|
||||||
|
open={isOpen}
|
||||||
const response = await fetch(originalImageUrl);
|
onClose={onClose}
|
||||||
const blob = await response.blob();
|
aria-labelledby="modal-modal-title"
|
||||||
|
aria-describedby="modal-modal-description"
|
||||||
setCropModalImageBlob(blob);
|
>
|
||||||
setCrop(undefined);
|
<Box
|
||||||
setCompletedCrop(undefined);
|
sx={{
|
||||||
}
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
const getImageSize = () => {
|
left: "50%",
|
||||||
if (cropImageElementRef.current) {
|
transform: "translate(-50%, -50%)",
|
||||||
const imageWidth = cropImageElementRef.current.naturalWidth;
|
bgcolor: "background.paper",
|
||||||
const imageHeight = cropImageElementRef.current.naturalHeight;
|
boxShadow: 24,
|
||||||
|
padding: "20px",
|
||||||
const aspect = imageWidth / imageHeight;
|
borderRadius: "8px",
|
||||||
|
width: isMobile ? "343px" : "620px",
|
||||||
if (aspect <= 1.333) {
|
}}
|
||||||
setWidth(240);
|
>
|
||||||
}
|
<Box
|
||||||
if (aspect >= 1.5) {
|
sx={{
|
||||||
setWidth(580);
|
height: "320px",
|
||||||
}
|
padding: "10px",
|
||||||
if (aspect >= 1.778) {
|
backgroundSize: "cover",
|
||||||
setWidth(580);
|
backgroundRepeat: "no-repeat",
|
||||||
}
|
display: "flex",
|
||||||
}
|
alignItems: "center",
|
||||||
};
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
open={isOpen}
|
|
||||||
onClose={onClose}
|
|
||||||
aria-labelledby="modal-modal-title"
|
|
||||||
aria-describedby="modal-modal-description"
|
|
||||||
>
|
>
|
||||||
<Box sx={{
|
{imageUrl && (
|
||||||
position: "absolute",
|
<ReactCrop
|
||||||
top: "50%",
|
crop={crop}
|
||||||
left: "50%",
|
onChange={(_, percentCrop) => setCrop(percentCrop)}
|
||||||
transform: "translate(-50%, -50%)",
|
onComplete={(c) => setCompletedCrop(c)}
|
||||||
bgcolor: "background.paper",
|
maxWidth={500}
|
||||||
boxShadow: 24,
|
minWidth={50}
|
||||||
padding: "20px",
|
maxHeight={320}
|
||||||
borderRadius: "8px",
|
minHeight={50}
|
||||||
width: isMobile ? "343px" : "620px",
|
>
|
||||||
}}>
|
<img
|
||||||
<Box
|
onLoad={getImageSize}
|
||||||
sx={{
|
ref={cropImageElementRef}
|
||||||
height: "320px",
|
alt="Crop me"
|
||||||
padding: "10px",
|
src={imageUrl}
|
||||||
backgroundSize: "cover",
|
style={{
|
||||||
backgroundRepeat: "no-repeat",
|
filter: `brightness(${100 - darken}%)`,
|
||||||
display: "flex",
|
transform: ` rotate(${rotate}deg)`,
|
||||||
alignItems: "center",
|
maxWidth: "580px",
|
||||||
justifyContent: "center",
|
maxHeight: "320px",
|
||||||
}}
|
}}
|
||||||
>
|
width={width}
|
||||||
{imageUrl && (
|
/>
|
||||||
<ReactCrop
|
</ReactCrop>
|
||||||
crop={crop}
|
)}
|
||||||
onChange={(_, percentCrop) => setCrop(percentCrop)}
|
</Box>
|
||||||
onComplete={(c) => setCompletedCrop(c)}
|
<Box
|
||||||
maxWidth={500}
|
sx={{
|
||||||
minWidth={50}
|
color: "#7E2AEA",
|
||||||
maxHeight={320}
|
display: "flex",
|
||||||
minHeight={50}
|
alignItems: "center",
|
||||||
>
|
justifyContent: "center",
|
||||||
<img
|
fontSize: "16xp",
|
||||||
onLoad={getImageSize}
|
fontWeight: "600",
|
||||||
ref={cropImageElementRef}
|
marginBottom: "50px",
|
||||||
alt="Crop me"
|
}}
|
||||||
src={imageUrl}
|
>
|
||||||
style={{
|
<Typography sx={{ color: "#7E2AEA", lineHeight: "0px" }}>
|
||||||
filter: `brightness(${100 - darken}%)`,
|
{crop?.width ? Math.round(crop.width) + "px" : ""}
|
||||||
transform: ` rotate(${rotate}deg)`,
|
</Typography>
|
||||||
maxWidth: "580px",
|
<Typography sx={{ color: "#7E2AEA", lineHeight: "0px" }}>
|
||||||
maxHeight: "320px",
|
{crop?.height ? Math.round(crop.height) + "px" : ""}
|
||||||
}}
|
</Typography>
|
||||||
width={width}
|
</Box>
|
||||||
/>
|
|
||||||
</ReactCrop>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
color: "#7E2AEA",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
fontSize: "16xp",
|
|
||||||
fontWeight: "600",
|
|
||||||
marginBottom: "50px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography sx={{ color: "#7E2AEA", lineHeight: "0px" }}>
|
|
||||||
{crop?.width ? Math.round(crop.width) + "px" : ""}
|
|
||||||
</Typography>
|
|
||||||
<Typography sx={{ color: "#7E2AEA", lineHeight: "0px" }}>
|
|
||||||
{crop?.height ? Math.round(crop.height) + "px" : ""}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: isMobile ? "block" : "flex",
|
display: isMobile ? "block" : "flex",
|
||||||
alignItems: "end",
|
alignItems: "end",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconButton onClick={() => setRotate(r => (r + 90) % 360)}>
|
<IconButton onClick={() => setRotate((r) => (r + 90) % 360)}>
|
||||||
<ResetIcon />
|
<ResetIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>
|
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>Размер</Typography>
|
||||||
Размер
|
<Slider
|
||||||
</Typography>
|
sx={[
|
||||||
<Slider
|
styleSlider,
|
||||||
sx={[styleSlider, {
|
{
|
||||||
width: isMobile ? "350px" : "250px",
|
width: isMobile ? "350px" : "250px",
|
||||||
}]}
|
},
|
||||||
value={width}
|
]}
|
||||||
min={50}
|
value={width}
|
||||||
max={580}
|
min={50}
|
||||||
step={1}
|
max={580}
|
||||||
onChange={(_, newValue) => {
|
step={1}
|
||||||
setWidth(newValue as number);
|
onChange={(_, newValue) => {
|
||||||
}}
|
setWidth(newValue as number);
|
||||||
/>
|
}}
|
||||||
</Box>
|
/>
|
||||||
<Box>
|
</Box>
|
||||||
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>
|
<Box>
|
||||||
Затемнение
|
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>Затемнение</Typography>
|
||||||
</Typography>
|
<Slider
|
||||||
<Slider
|
sx={[
|
||||||
sx={[styleSlider, {
|
styleSlider,
|
||||||
width: isMobile ? "350px" : "250px",
|
{
|
||||||
}]}
|
width: isMobile ? "350px" : "250px",
|
||||||
value={darken}
|
},
|
||||||
min={0}
|
]}
|
||||||
max={100}
|
value={darken}
|
||||||
step={1}
|
min={0}
|
||||||
onChange={(_, newValue) => setDarken(newValue as number)}
|
max={100}
|
||||||
/>
|
step={1}
|
||||||
</Box>
|
onChange={(_, newValue) => setDarken(newValue as number)}
|
||||||
</Box>
|
/>
|
||||||
<Box
|
</Box>
|
||||||
sx={{
|
</Box>
|
||||||
marginTop: "40px",
|
<Box
|
||||||
width: "100%",
|
sx={{
|
||||||
display: "flex",
|
marginTop: "40px",
|
||||||
}}
|
width: "100%",
|
||||||
>
|
display: "flex",
|
||||||
<Button
|
gap: "10px",
|
||||||
onClick={handleSaveClick}
|
}}
|
||||||
disableRipple
|
>
|
||||||
data-cy="crop-modal-save-button"
|
<Button
|
||||||
sx={{
|
onClick={handleCropClick}
|
||||||
height: "48px",
|
disableRipple
|
||||||
color: "#7E2AEA",
|
disabled={!completedCrop}
|
||||||
borderRadius: "8px",
|
sx={{
|
||||||
border: "1px solid #7E2AEA",
|
padding: "10px 20px",
|
||||||
marginRight: "10px",
|
borderRadius: "8px",
|
||||||
px: "20px",
|
background: theme.palette.brightPurple.main,
|
||||||
}}
|
fontSize: "18px",
|
||||||
>Сохранить</Button>
|
color: "#7E2AEA",
|
||||||
<Button
|
border: `1px solid ${!completedCrop ? "rgba(0, 0, 0, 0.26)" : "#7E2AEA"}`,
|
||||||
onClick={handleLoadOriginalImage}
|
backgroundColor: "transparent",
|
||||||
disableRipple
|
}}
|
||||||
disabled={!originalImageUrl}
|
>
|
||||||
sx={{
|
<CropIcon color={!completedCrop ? "rgba(0, 0, 0, 0.26)" : "#7E2AEA"} />
|
||||||
width: "215px",
|
Обрезать
|
||||||
height: "48px",
|
</Button>
|
||||||
color: "#7E2AEA",
|
<Button
|
||||||
borderRadius: "8px",
|
onClick={handleLoadOriginalImage}
|
||||||
border: "1px solid #7E2AEA",
|
disableRipple
|
||||||
marginRight: "10px",
|
disabled={!originalImageUrl}
|
||||||
ml: "auto",
|
sx={{
|
||||||
}}
|
width: "215px",
|
||||||
>
|
height: "48px",
|
||||||
Загрузить оригинал
|
color: "#7E2AEA",
|
||||||
</Button>
|
borderRadius: "8px",
|
||||||
<Button
|
border: "1px solid #7E2AEA",
|
||||||
onClick={handleCropClick}
|
}}
|
||||||
disableRipple
|
>
|
||||||
variant="contained"
|
Загрузить оригинал
|
||||||
disabled={!completedCrop}
|
</Button>
|
||||||
sx={{
|
<Button
|
||||||
padding: "10px 20px",
|
onClick={handleSaveClick}
|
||||||
borderRadius: "8px",
|
disableRipple
|
||||||
background: theme.palette.brightPurple.main,
|
variant="contained"
|
||||||
fontSize: "18px",
|
data-cy="crop-modal-save-button"
|
||||||
}}
|
sx={{
|
||||||
>
|
height: "48px",
|
||||||
<CropIcon />
|
borderRadius: "8px",
|
||||||
Обрезать
|
border: "1px solid #7E2AEA",
|
||||||
</Button>
|
marginRight: "10px",
|
||||||
</Box>
|
px: "20px",
|
||||||
</Box>
|
ml: "auto",
|
||||||
</Modal>
|
}}
|
||||||
);
|
>
|
||||||
|
Сохранить
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useCropModalState(initialOpenState = false) {
|
export function useCropModalState(initialOpenState = false) {
|
||||||
const [isCropModalOpen, setOpened] = useState(initialOpenState);
|
const [isCropModalOpen, setOpened] = useState(initialOpenState);
|
||||||
const [imageBlob, setCropModalImageBlob] = useState<Blob | null>(null);
|
const [imageBlob, setCropModalImageBlob] = useState<Blob | null>(null);
|
||||||
const [originalImageUrl, setOriginalImageUrl] = useState<string | null>(null);
|
const [originalImageUrl, setOriginalImageUrl] = useState<string | null>(null);
|
||||||
|
|
||||||
const closeCropModal = () => {
|
const closeCropModal = () => {
|
||||||
setOpened(false);
|
setOpened(false);
|
||||||
setCropModalImageBlob(null);
|
setCropModalImageBlob(null);
|
||||||
setOriginalImageUrl(null);
|
setOriginalImageUrl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function openCropModal(image: Blob | string, originalImageUrl: string | null | undefined = null) {
|
async function openCropModal(image: Blob | string, originalImageUrl: string | null | undefined = null) {
|
||||||
if (typeof image === "string") {
|
if (typeof image === "string") {
|
||||||
const response = await fetch(image);
|
const response = await fetch(image);
|
||||||
image = await response.blob();
|
image = await response.blob();
|
||||||
}
|
|
||||||
|
|
||||||
setCropModalImageBlob(image);
|
|
||||||
setOriginalImageUrl(originalImageUrl);
|
|
||||||
setOpened(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
setCropModalImageBlob(image);
|
||||||
isCropModalOpen,
|
setOriginalImageUrl(originalImageUrl);
|
||||||
openCropModal,
|
setOpened(true);
|
||||||
closeCropModal,
|
}
|
||||||
imageBlob,
|
|
||||||
setCropModalImageBlob,
|
return {
|
||||||
originalImageUrl,
|
isCropModalOpen,
|
||||||
} as const;
|
openCropModal,
|
||||||
|
closeCropModal,
|
||||||
|
imageBlob,
|
||||||
|
setCropModalImageBlob,
|
||||||
|
originalImageUrl,
|
||||||
|
} as const;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,9 @@ export default function Number({ question }: Props) {
|
|||||||
gap: 1,
|
gap: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h6" data-cy="question-title">{question.title}</Typography>
|
<Typography variant="h6" data-cy="question-title">
|
||||||
|
{question.title}
|
||||||
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
px: 2,
|
px: 2,
|
||||||
|
@ -32,7 +32,7 @@ export default function SwitchStepPages({
|
|||||||
}
|
}
|
||||||
case 1: return quizType === "form" ? <FormQuestionsPage /> : <QuestionsPage />;
|
case 1: return quizType === "form" ? <FormQuestionsPage /> : <QuestionsPage />;
|
||||||
case 2: return <ResultPage />;
|
case 2: return <ResultPage />;
|
||||||
case 3: return <QuestionsMap />;
|
case 3: return <ContactFormPage />;
|
||||||
case 4: return <ContactFormPage />;
|
case 4: return <ContactFormPage />;
|
||||||
case 5: return <InstallQuiz />;
|
case 5: return <InstallQuiz />;
|
||||||
case 6: return <>Реклама</>;
|
case 6: return <>Реклама</>;
|
||||||
|
13
yarn.lock
13
yarn.lock
@ -4114,10 +4114,15 @@ csstype@^3.0.2, csstype@^3.1.2:
|
|||||||
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz"
|
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz"
|
||||||
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
|
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
|
||||||
|
|
||||||
cypress@^13.4.0:
|
cypress-file-upload@^5.0.8:
|
||||||
version "13.4.0"
|
version "5.0.8"
|
||||||
resolved "https://registry.npmjs.org/cypress/-/cypress-13.4.0.tgz"
|
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1"
|
||||||
integrity sha512-KeWNC9xSHG/ewZURVbaQsBQg2mOKw4XhjJZFKjWbEjgZCdxpPXLpJnfq5Jns1Gvnjp6AlnIfpZfWFlDgVKXdWQ==
|
integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==
|
||||||
|
|
||||||
|
cypress@^13.6.1:
|
||||||
|
version "13.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.1.tgz#c5f714f08551666ed3ac1fa95718eabb23a416df"
|
||||||
|
integrity sha512-k1Wl5PQcA/4UoTffYKKaxA0FJKwg8yenYNYRzLt11CUR0Kln+h7Udne6mdU1cUIdXBDTVZWtmiUjzqGs7/pEpw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cypress/request" "^3.0.0"
|
"@cypress/request" "^3.0.0"
|
||||||
"@cypress/xvfb" "^1.2.4"
|
"@cypress/xvfb" "^1.2.4"
|
||||||
|
Loading…
Reference in New Issue
Block a user