use backend somewhere & refactor
This commit is contained in:
parent
f7f1ce13ab
commit
0c6198dd79
@ -41,6 +41,7 @@
|
|||||||
"react-rnd": "^10.4.1",
|
"react-rnd": "^10.4.1",
|
||||||
"react-router-dom": "^6.6.2",
|
"react-router-dom": "^6.6.2",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"swr": "^2.2.4",
|
||||||
"typescript": "^4.4.2",
|
"typescript": "^4.4.2",
|
||||||
"use-debounce": "^9.0.4",
|
"use-debounce": "^9.0.4",
|
||||||
"web-vitals": "^2.1.0",
|
"web-vitals": "^2.1.0",
|
||||||
|
|||||||
@ -5,40 +5,44 @@ import { DeleteQuizRequest, DeleteQuizResponse } from "model/quiz/delete";
|
|||||||
import { EditQuizRequest, EditQuizResponse } from "model/quiz/edit";
|
import { EditQuizRequest, EditQuizResponse } from "model/quiz/edit";
|
||||||
import { GetQuizRequest, GetQuizResponse } from "model/quiz/get";
|
import { GetQuizRequest, GetQuizResponse } from "model/quiz/get";
|
||||||
import { GetQuizListRequest, GetQuizListResponse } from "model/quiz/getList";
|
import { GetQuizListRequest, GetQuizListResponse } from "model/quiz/getList";
|
||||||
import { BackendQuiz } from "model/quiz/quiz";
|
import { RawQuiz } from "model/quiz/quiz";
|
||||||
|
|
||||||
|
|
||||||
const baseUrl = process.env.NODE_ENV === "production" ? "/squiz" : "https://squiz.pena.digital/squiz";
|
const baseUrl = process.env.NODE_ENV === "production" ? "/squiz" : "https://squiz.pena.digital/squiz";
|
||||||
|
|
||||||
function createQuiz(body: CreateQuizRequest = defaultCreateQuizBody) {
|
function createQuiz(body?: Partial<CreateQuizRequest>) {
|
||||||
return makeRequest<CreateQuizRequest, BackendQuiz>({
|
return makeRequest<CreateQuizRequest, RawQuiz>({
|
||||||
url: `${baseUrl}/quiz/create`,
|
url: `${baseUrl}/quiz/create`,
|
||||||
body,
|
body: { ...defaultCreateQuizBody, ...body },
|
||||||
method: "POST",
|
method: "POST",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getQuizList(body: GetQuizListRequest = defaultGetQuizListBody) {
|
async function getQuizList(body?: Partial<GetQuizListRequest>) {
|
||||||
return makeRequest<GetQuizListRequest, GetQuizListResponse>({
|
const response = await makeRequest<GetQuizListRequest, GetQuizListResponse>({
|
||||||
url: `${baseUrl}/quiz/getList`,
|
url: `${baseUrl}/quiz/getList`,
|
||||||
body,
|
body: { ...defaultGetQuizListBody, ...body },
|
||||||
method: "GET",
|
method: "POST",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return response.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getQuiz(body: GetQuizRequest = defaultGetQuizBody) {
|
function getQuiz(body?: Partial<GetQuizRequest>) {
|
||||||
return makeRequest<GetQuizRequest, GetQuizResponse>({
|
return makeRequest<GetQuizRequest, GetQuizResponse>({
|
||||||
url: `${baseUrl}/quiz/get`,
|
url: `${baseUrl}/quiz/get`,
|
||||||
body,
|
body: { ...defaultGetQuizBody, ...body },
|
||||||
method: "GET",
|
method: "GET",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function editQuiz(body: EditQuizRequest = defaultEditQuizBody) {
|
async function editQuiz(body: EditQuizRequest, signal?: AbortSignal) {
|
||||||
|
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
return makeRequest<EditQuizRequest, EditQuizResponse>({
|
return makeRequest<EditQuizRequest, EditQuizResponse>({
|
||||||
url: `${baseUrl}/quiz/edit`,
|
url: `${baseUrl}/quiz/edit`,
|
||||||
body,
|
body,
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
signal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,18 +90,18 @@ const defaultCreateQuizBody: CreateQuizRequest = {
|
|||||||
"fingerprinting": true,
|
"fingerprinting": true,
|
||||||
"repeatable": true,
|
"repeatable": true,
|
||||||
"note_prevented": true,
|
"note_prevented": true,
|
||||||
"mail_notifications": true,
|
"mail_notifications": false,
|
||||||
"unique_answers": true,
|
"unique_answers": true,
|
||||||
"name": "string",
|
"name": "string",
|
||||||
"description": "string",
|
"description": "string",
|
||||||
"config": "string",
|
"config": "string",
|
||||||
"status": "string",
|
"status": "stop",
|
||||||
"limit": 0,
|
"limit": 0,
|
||||||
"due_to": 0,
|
"due_to": 0,
|
||||||
"time_of_passing": 0,
|
"time_of_passing": 0,
|
||||||
"pausable": true,
|
"pausable": false,
|
||||||
"question_cnt": 0,
|
"question_cnt": 0,
|
||||||
"super": true,
|
"super": false,
|
||||||
"group_id": 0,
|
"group_id": 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -129,14 +133,6 @@ const defaultGetQuizBody: GetQuizRequest = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const defaultGetQuizListBody: GetQuizListRequest = {
|
const defaultGetQuizListBody: GetQuizListRequest = {
|
||||||
"limit": 0,
|
"limit": 100,
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
"from": 0,
|
|
||||||
"to": 0,
|
|
||||||
"search": "string",
|
|
||||||
"status": "string",
|
|
||||||
"deleted": true,
|
|
||||||
"archived": true,
|
|
||||||
"super": true,
|
|
||||||
"group_id": 0,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { HTML5Backend } from "react-dnd-html5-backend";
|
|||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import lightTheme from "./utils/themes/light";
|
import lightTheme from "./utils/themes/light";
|
||||||
|
import { SWRConfig } from "swr";
|
||||||
|
|
||||||
|
|
||||||
dayjs.locale("ru");
|
dayjs.locale("ru");
|
||||||
@ -20,14 +21,16 @@ const localeText = ruRU.components.MuiLocalizationProvider.defaultProps.localeTe
|
|||||||
const root = createRoot(document.getElementById("root")!);
|
const root = createRoot(document.getElementById("root")!);
|
||||||
|
|
||||||
root.render(
|
root.render(
|
||||||
<DndProvider backend={HTML5Backend}>
|
<SWRConfig value={{ revalidateOnFocus: false, shouldRetryOnError: false }}>
|
||||||
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale="ru" localeText={localeText}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
<ThemeProvider theme={lightTheme}>
|
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale="ru" localeText={localeText}>
|
||||||
<SnackbarProvider>
|
<ThemeProvider theme={lightTheme}>
|
||||||
<CssBaseline />
|
<SnackbarProvider>
|
||||||
<App />
|
<CssBaseline />
|
||||||
</SnackbarProvider>
|
<App />
|
||||||
</ThemeProvider>
|
</SnackbarProvider>
|
||||||
</LocalizationProvider>
|
</ThemeProvider>
|
||||||
</DndProvider>
|
</LocalizationProvider>
|
||||||
|
</DndProvider>
|
||||||
|
</SWRConfig>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,18 +1,34 @@
|
|||||||
export interface CreateQuizRequest {
|
export interface CreateQuizRequest {
|
||||||
fingerprinting: boolean;
|
/** set true for save deviceId */
|
||||||
repeatable: boolean;
|
fingerprinting: boolean;
|
||||||
note_prevented: boolean;
|
/** set true for allow user to repeat quiz */
|
||||||
|
repeatable: boolean;
|
||||||
|
/** set true for save statistic of incomplete quiz passing */
|
||||||
|
note_prevented: boolean;
|
||||||
|
/** set true for mail notification for each quiz passing */
|
||||||
mail_notifications: boolean;
|
mail_notifications: boolean;
|
||||||
unique_answers: boolean;
|
/** set true for save statistics only for unique quiz passing */
|
||||||
name: string;
|
unique_answers: boolean;
|
||||||
description: string;
|
/** name of quiz. max 280 length */
|
||||||
config: string;
|
name: string;
|
||||||
status: string;
|
/** description of quiz */
|
||||||
limit: number;
|
description: string;
|
||||||
due_to: number;
|
/** config of quiz. serialized json for rules of quiz flow */
|
||||||
time_of_passing: number;
|
config: string;
|
||||||
pausable: boolean;
|
/** status of quiz. allow only '', 'draft', 'template', 'stop', 'start' */
|
||||||
question_cnt: number;
|
status: "draft" | "template" | "stop" | "start";
|
||||||
super: boolean;
|
/** limit is count of max quiz passing */
|
||||||
group_id: number;
|
limit: number;
|
||||||
|
/** last time when quiz is valid. timestamp in seconds */
|
||||||
|
due_to: number;
|
||||||
|
/** seconds to pass quiz */
|
||||||
|
time_of_passing: number;
|
||||||
|
/** true if it is allowed for pause quiz */
|
||||||
|
pausable: boolean;
|
||||||
|
/** count of questions */
|
||||||
|
question_cnt: number;
|
||||||
|
/** set true if squiz realize group functionality */
|
||||||
|
super: boolean;
|
||||||
|
/** group of new quiz */
|
||||||
|
group_id: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,66 @@
|
|||||||
|
import { Quiz } from "./quiz";
|
||||||
|
|
||||||
|
|
||||||
export interface EditQuizRequest {
|
export interface EditQuizRequest {
|
||||||
|
/** id of question for update */
|
||||||
id: number;
|
id: number;
|
||||||
|
/** set true for storing fingerprints */
|
||||||
fp: boolean;
|
fp: boolean;
|
||||||
|
/** set true for allow to repeat quiz after passing */
|
||||||
rep: boolean;
|
rep: boolean;
|
||||||
|
/** set true for store unfinished passing */
|
||||||
note_prevented: boolean;
|
note_prevented: boolean;
|
||||||
|
/** set true if we should send passing result on every passing */
|
||||||
mailing: boolean;
|
mailing: boolean;
|
||||||
|
/** set true if we allow only one user quiz passing */
|
||||||
uniq: boolean;
|
uniq: boolean;
|
||||||
name: string;
|
/** new name of the quiz */
|
||||||
desc: string;
|
name?: string;
|
||||||
conf: string;
|
/** new descriptions of the quiz */
|
||||||
status: string;
|
desc?: string;
|
||||||
|
/** new config of the quiz */
|
||||||
|
conf?: string;
|
||||||
|
/** new status. only draft,template,stop,start allowed */
|
||||||
|
status?: string;
|
||||||
|
/** max amount of quiz passing */
|
||||||
limit: number;
|
limit: number;
|
||||||
|
/** max time of quiz passing */
|
||||||
due_to: number;
|
due_to: number;
|
||||||
|
/** max time to pass quiz */
|
||||||
time_of_passing: number;
|
time_of_passing: number;
|
||||||
|
/** allow to pause quiz to user */
|
||||||
pausable: boolean;
|
pausable: boolean;
|
||||||
question_cnt: number;
|
/** count of questions */
|
||||||
super: boolean;
|
question_cnt?: number;
|
||||||
group_id: number;
|
/** set true if squiz realize group functionality */
|
||||||
|
super?: boolean;
|
||||||
|
/** group of new quiz */
|
||||||
|
group_id?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EditQuizResponse {
|
export interface EditQuizResponse {
|
||||||
|
/** id of new version of question */
|
||||||
updated: number;
|
updated: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function quizToEditQuizRequest(quiz: Quiz): EditQuizRequest {
|
||||||
|
return {
|
||||||
|
id: quiz.id,
|
||||||
|
fp: quiz.fingerprinting,
|
||||||
|
rep: quiz.repeatable,
|
||||||
|
note_prevented: quiz.note_prevented,
|
||||||
|
mailing: quiz.mail_notifications,
|
||||||
|
uniq: quiz.unique_answers,
|
||||||
|
name: quiz.name,
|
||||||
|
desc: quiz.description,
|
||||||
|
conf: JSON.stringify(quiz.config),
|
||||||
|
status: quiz.status,
|
||||||
|
limit: quiz.limit,
|
||||||
|
due_to: quiz.due_to,
|
||||||
|
time_of_passing: quiz.time_of_passing,
|
||||||
|
pausable: quiz.pausable,
|
||||||
|
question_cnt: quiz.question_cnt,
|
||||||
|
super: quiz.super,
|
||||||
|
group_id: quiz.group_id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -1,17 +1,29 @@
|
|||||||
|
import { RawQuiz } from "./quiz";
|
||||||
|
|
||||||
export interface GetQuizListRequest {
|
export interface GetQuizListRequest {
|
||||||
limit: number;
|
/** max items on page */
|
||||||
offset: number;
|
limit?: number;
|
||||||
from: number;
|
/** page number */
|
||||||
to: number;
|
offset?: number;
|
||||||
search: string;
|
/** start time of time period. timestamp in seconds */
|
||||||
status: string;
|
from?: number;
|
||||||
deleted: boolean;
|
/** end time of time period. timestamp in seconds */
|
||||||
archived: boolean;
|
to?: number;
|
||||||
super: boolean;
|
/** string for fulltext search in titles of quizes */
|
||||||
group_id: number;
|
search?: string;
|
||||||
|
/** allow only - draft, template, timeout, stop, start, offlimit */
|
||||||
|
status?: "" | "draft" | "template" | "timeout" | "stop" | "start" | "offlimit";
|
||||||
|
/** get deleted quizes */
|
||||||
|
deleted?: boolean;
|
||||||
|
/** get archived quizes */
|
||||||
|
archived?: boolean;
|
||||||
|
/** set true if squiz realize group functionality */
|
||||||
|
super?: boolean;
|
||||||
|
/** group of new quiz */
|
||||||
|
group_id?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetQuizListResponse {
|
export interface GetQuizListResponse {
|
||||||
count: number;
|
count: number;
|
||||||
items: unknown[]; // TODO
|
items: RawQuiz[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,29 +1,134 @@
|
|||||||
export interface BackendQuiz {
|
import { QuizConfig, defaultQuizConfig } from "@model/quizSettings";
|
||||||
|
|
||||||
|
|
||||||
|
export interface Quiz {
|
||||||
|
/** Id of created quiz */
|
||||||
id: number;
|
id: number;
|
||||||
|
/** string id for customers */
|
||||||
qid: string;
|
qid: string;
|
||||||
|
/** true if quiz deleted */
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
|
/** true if quiz archived */
|
||||||
archived: boolean;
|
archived: boolean;
|
||||||
|
/** set true for save deviceId */
|
||||||
fingerprinting: boolean;
|
fingerprinting: boolean;
|
||||||
|
/** set true for allow user to repeat quiz */
|
||||||
repeatable: boolean;
|
repeatable: boolean;
|
||||||
|
/** set true for save statistic of incomplete quiz passing */
|
||||||
note_prevented: boolean;
|
note_prevented: boolean;
|
||||||
|
/** set true for mail notification for each quiz passing */
|
||||||
mail_notifications: boolean;
|
mail_notifications: boolean;
|
||||||
|
/** set true for save statistics only for unique quiz passing */
|
||||||
unique_answers: boolean;
|
unique_answers: boolean;
|
||||||
|
/** name of quiz. max 280 length */
|
||||||
name: string;
|
name: string;
|
||||||
|
/** description of quiz */
|
||||||
description: string;
|
description: string;
|
||||||
config: string;
|
/** quiz config*/
|
||||||
|
config: QuizConfig;
|
||||||
|
/** status of quiz. allow only '', 'draft', 'template', 'stop', 'start' */
|
||||||
status: string;
|
status: string;
|
||||||
|
/** limit is count of max quiz passing */
|
||||||
limit: number;
|
limit: number;
|
||||||
|
/** last time when quiz is valid. timestamp in seconds */
|
||||||
due_to: number;
|
due_to: number;
|
||||||
|
/** seconds to pass quiz */
|
||||||
time_of_passing: number;
|
time_of_passing: number;
|
||||||
|
/** true if it is allowed for pause quiz */
|
||||||
pausable: boolean;
|
pausable: boolean;
|
||||||
|
/** version of quiz */
|
||||||
version: number;
|
version: number;
|
||||||
|
/** version comment to version of quiz */
|
||||||
version_comment: string;
|
version_comment: string;
|
||||||
|
/** array of previous versions of quiz */
|
||||||
parent_ids: number[];
|
parent_ids: number[];
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
|
/** count of questions */
|
||||||
question_cnt: number;
|
question_cnt: number;
|
||||||
|
/** count passings */
|
||||||
passed_count: number;
|
passed_count: number;
|
||||||
|
/** average time of passing */
|
||||||
average_time: number;
|
average_time: number;
|
||||||
|
/** set true if squiz realize group functionality */
|
||||||
super: boolean;
|
super: boolean;
|
||||||
|
/** group of new quiz */
|
||||||
group_id: number;
|
group_id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RawQuiz {
|
||||||
|
/** Id of created quiz */
|
||||||
|
id: number;
|
||||||
|
/** string id for customers */
|
||||||
|
qid: string;
|
||||||
|
/** true if quiz deleted */
|
||||||
|
deleted: boolean;
|
||||||
|
/** true if quiz archived */
|
||||||
|
archived: boolean;
|
||||||
|
/** set true for save deviceId */
|
||||||
|
fingerprinting: boolean;
|
||||||
|
/** set true for allow user to repeat quiz */
|
||||||
|
repeatable: boolean;
|
||||||
|
/** set true for save statistic of incomplete quiz passing */
|
||||||
|
note_prevented: boolean;
|
||||||
|
/** set true for mail notification for each quiz passing */
|
||||||
|
mail_notifications: boolean;
|
||||||
|
/** set true for save statistics only for unique quiz passing */
|
||||||
|
unique_answers: boolean;
|
||||||
|
/** name of quiz. max 280 length */
|
||||||
|
name: string;
|
||||||
|
/** description of quiz */
|
||||||
|
description: string;
|
||||||
|
/** config of quiz. serialized json for rules of quiz flow */
|
||||||
|
config: string;
|
||||||
|
/** status of quiz. allow only '', 'draft', 'template', 'stop', 'start' */
|
||||||
|
status: string;
|
||||||
|
/** limit is count of max quiz passing */
|
||||||
|
limit: number;
|
||||||
|
/** last time when quiz is valid. timestamp in seconds */
|
||||||
|
due_to: number;
|
||||||
|
/** seconds to pass quiz */
|
||||||
|
time_of_passing: number;
|
||||||
|
/** true if it is allowed for pause quiz */
|
||||||
|
pausable: boolean;
|
||||||
|
/** version of quiz */
|
||||||
|
version: number;
|
||||||
|
/** version comment to version of quiz */
|
||||||
|
version_comment: string;
|
||||||
|
/** array of previous versions of quiz */
|
||||||
|
parent_ids: number[];
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
/** count of questions */
|
||||||
|
question_cnt: number;
|
||||||
|
/** count passings */
|
||||||
|
passed_count: number;
|
||||||
|
/** average time of passing */
|
||||||
|
average_time: number;
|
||||||
|
/** set true if squiz realize group functionality */
|
||||||
|
super: boolean;
|
||||||
|
/** group of new quiz */
|
||||||
|
group_id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function quizToRawQuiz(quiz: Quiz): RawQuiz {
|
||||||
|
return {
|
||||||
|
...quiz,
|
||||||
|
config: JSON.stringify(quiz.config),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rawQuizToQuiz(rawQuiz: RawQuiz): Quiz {
|
||||||
|
let config = defaultQuizConfig;
|
||||||
|
|
||||||
|
try {
|
||||||
|
config = JSON.parse(rawQuiz.config);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Cannot parse quiz config from string, using default config", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rawQuiz,
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
72
src/model/quizSettings.ts
Normal file
72
src/model/quizSettings.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
export const quizSetupSteps = {
|
||||||
|
1: { displayStep: 1, text: "Настройка стартовой страницы" },
|
||||||
|
2: { displayStep: 1, text: "Настройка стартовой страницы" },
|
||||||
|
3: { displayStep: 1, text: "Настройка стартовой страницы" },
|
||||||
|
4: { displayStep: 2, text: "Задайте вопросы" },
|
||||||
|
5: { displayStep: 3, text: "Настройте авторезультаты" },
|
||||||
|
6: { displayStep: 3, text: "Настройте авторезультаты" },
|
||||||
|
7: { displayStep: 4, text: "Оценка графа карты вопросов" },
|
||||||
|
8: { displayStep: 5, text: "Настройте форму контактов" },
|
||||||
|
9: { displayStep: 6, text: "Установите квиз" },
|
||||||
|
10: { displayStep: 7, text: "Запустите рекламу" },
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const maxQuizSetupSteps = Math.max(...Object.keys(quizSetupSteps).map(parseInt));
|
||||||
|
|
||||||
|
export const maxDisplayQuizSetupSteps = Math.max(...Object.values(quizSetupSteps).map(v => v.displayStep));
|
||||||
|
|
||||||
|
export type QuizSetupStep = keyof typeof quizSetupSteps;
|
||||||
|
|
||||||
|
export interface QuizConfig {
|
||||||
|
type: "quiz" | "form";
|
||||||
|
logo: string;
|
||||||
|
noStartPage: boolean;
|
||||||
|
startpageType: "standard" | "expanded" | "centered";
|
||||||
|
startpage: {
|
||||||
|
description: string;
|
||||||
|
button: string;
|
||||||
|
position: string;
|
||||||
|
background: {
|
||||||
|
type: string;
|
||||||
|
desktop: string;
|
||||||
|
mobile: string;
|
||||||
|
video: string;
|
||||||
|
cycle: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
info: {
|
||||||
|
phonenumber: string;
|
||||||
|
clickable: boolean;
|
||||||
|
orgname: string;
|
||||||
|
site: string;
|
||||||
|
law?: string;
|
||||||
|
};
|
||||||
|
meta: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultQuizConfig: QuizConfig = {
|
||||||
|
type: "quiz",
|
||||||
|
logo: "",
|
||||||
|
noStartPage: false,
|
||||||
|
startpageType: "standard",
|
||||||
|
startpage: {
|
||||||
|
description: "",
|
||||||
|
button: "",
|
||||||
|
position: "left",
|
||||||
|
background: {
|
||||||
|
type: "none",
|
||||||
|
desktop: "",
|
||||||
|
mobile: "",
|
||||||
|
video: "",
|
||||||
|
cycle: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
phonenumber: "",
|
||||||
|
clickable: false,
|
||||||
|
orgname: "",
|
||||||
|
site: "",
|
||||||
|
law: "",
|
||||||
|
},
|
||||||
|
meta: "",
|
||||||
|
};
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { Button, Typography } from "@mui/material";
|
import { Button, Typography } from "@mui/material";
|
||||||
import { createQuiz } from "@root/quizesV2";
|
|
||||||
import SectionWrapper from "@ui_kit/SectionWrapper";
|
import SectionWrapper from "@ui_kit/SectionWrapper";
|
||||||
import ComplexNavText from "./ComplexNavText";
|
import ComplexNavText from "./ComplexNavText";
|
||||||
import { useNavigate } from "react-router";
|
import { createQuiz } from "@root/quizes/actions";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
|
||||||
export default function FirstQuiz() {
|
export default function FirstQuiz() {
|
||||||
|
|||||||
@ -1,88 +1,100 @@
|
|||||||
|
import { quizApi } from "@api/quiz";
|
||||||
|
import { devlog } from "@frontend/kitui";
|
||||||
import {
|
import {
|
||||||
Typography,
|
Box,
|
||||||
Box,
|
Button,
|
||||||
Button,
|
SxProps,
|
||||||
SxProps,
|
Theme,
|
||||||
Theme,
|
Typography,
|
||||||
useTheme,
|
useMediaQuery,
|
||||||
useMediaQuery,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import ComplexNavText from "./ComplexNavText";
|
|
||||||
import QuizCard from "./QuizCard";
|
|
||||||
import SectionWrapper from "@ui_kit/SectionWrapper";
|
import SectionWrapper from "@ui_kit/SectionWrapper";
|
||||||
|
import { isAxiosError } from "axios";
|
||||||
|
import { enqueueSnackbar } from "notistack";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { quizStore } from "@root/quizes";
|
|
||||||
import FirstQuiz from "./FirstQuiz";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { createQuiz } from "@root/quizesV2";
|
import useSWR from "swr";
|
||||||
|
import ComplexNavText from "./ComplexNavText";
|
||||||
|
import FirstQuiz from "./FirstQuiz";
|
||||||
|
import QuizCard from "./QuizCard";
|
||||||
|
import { setQuizes, createQuiz } from "@root/quizes/actions";
|
||||||
|
import { useQuizArray } from "@root/quizes/hooks";
|
||||||
|
|
||||||
|
|
||||||
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,
|
outerContainerSx: sx,
|
||||||
children,
|
children,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { listQuizes, updateQuizesList, removeQuiz, createBlank } = quizStore();
|
useSWR("quizes", () => quizApi.getList(), {
|
||||||
const navigate = useNavigate();
|
onSuccess: setQuizes,
|
||||||
const theme = useTheme();
|
onError: error => {
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
const message = isAxiosError<string>(error) ? (error.response?.data ?? "") : "";
|
||||||
|
|
||||||
return (
|
devlog("Error creating quiz", error);
|
||||||
<>
|
enqueueSnackbar(`Не удалось получить квизы. ${message}`);
|
||||||
{Object.keys(listQuizes).length === 0 ? (
|
},
|
||||||
<FirstQuiz />
|
});
|
||||||
) : (
|
const quizArray = useQuizArray();
|
||||||
<SectionWrapper maxWidth="lg">
|
const navigate = useNavigate();
|
||||||
<ComplexNavText text1="Кабинет квизов" />
|
const theme = useTheme();
|
||||||
<Box
|
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
||||||
sx={{
|
|
||||||
display: "flex",
|
return (
|
||||||
justifyContent: "space-between",
|
<>
|
||||||
alignItems: "center",
|
{quizArray.length === 0 ? (
|
||||||
mt: "20px",
|
<FirstQuiz />
|
||||||
mb: "30px",
|
) : (
|
||||||
}}
|
<SectionWrapper maxWidth="lg">
|
||||||
>
|
<ComplexNavText text1="Кабинет квизов" />
|
||||||
<Typography variant="h4">Мои квизы</Typography>
|
<Box
|
||||||
<Button
|
sx={{
|
||||||
variant="contained"
|
display: "flex",
|
||||||
sx={{
|
justifyContent: "space-between",
|
||||||
padding: isMobile ? "10px" : "10px 47px",
|
alignItems: "center",
|
||||||
minWidth: "44px",
|
mt: "20px",
|
||||||
}}
|
mb: "30px",
|
||||||
onClick={() => createQuiz(navigate)}
|
}}
|
||||||
>
|
>
|
||||||
{isMobile ? "+" : "Создать +"}
|
<Typography variant="h4">Мои квизы</Typography>
|
||||||
</Button>
|
<Button
|
||||||
</Box>
|
variant="contained"
|
||||||
<Box
|
sx={{
|
||||||
sx={{
|
padding: isMobile ? "10px" : "10px 47px",
|
||||||
py: "10px",
|
minWidth: "44px",
|
||||||
display: "flex",
|
}}
|
||||||
flexWrap: "wrap",
|
onClick={() => createQuiz(navigate)}
|
||||||
gap: "40px",
|
>
|
||||||
mb: "60px",
|
{isMobile ? "+" : "Создать +"}
|
||||||
}}
|
</Button>
|
||||||
>
|
</Box>
|
||||||
{Object.values(listQuizes).map(({ id, name }) => (
|
<Box
|
||||||
<QuizCard
|
sx={{
|
||||||
key={id}
|
py: "10px",
|
||||||
name={name}
|
display: "flex",
|
||||||
openCount={0}
|
flexWrap: "wrap",
|
||||||
applicationCount={0}
|
gap: "40px",
|
||||||
conversionPercent={0}
|
mb: "60px",
|
||||||
onClickDelete={() => {
|
}}
|
||||||
removeQuiz(id);
|
>
|
||||||
}}
|
{quizArray.map(quiz => (
|
||||||
onClickEdit={() => navigate(`/setting/${id}`)}
|
<QuizCard
|
||||||
/>
|
key={quiz.id}
|
||||||
))}
|
quiz={quiz}
|
||||||
</Box>
|
openCount={0}
|
||||||
{children}
|
applicationCount={0}
|
||||||
</SectionWrapper>
|
conversionPercent={0}
|
||||||
)}
|
/>
|
||||||
</>
|
))}
|
||||||
);
|
</Box>
|
||||||
|
{children}
|
||||||
|
</SectionWrapper>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,142 +1,147 @@
|
|||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
IconButton,
|
|
||||||
Typography,
|
|
||||||
useTheme,
|
|
||||||
useMediaQuery,
|
|
||||||
} from "@mui/material";
|
|
||||||
import ChartIcon from "@icons/ChartIcon";
|
import ChartIcon from "@icons/ChartIcon";
|
||||||
import LinkIcon from "@icons/LinkIcon";
|
import LinkIcon from "@icons/LinkIcon";
|
||||||
import PencilIcon from "@icons/PencilIcon";
|
import PencilIcon from "@icons/PencilIcon";
|
||||||
|
import { Quiz } from "@model/quiz/quiz";
|
||||||
import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
|
import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
IconButton,
|
||||||
|
Typography,
|
||||||
|
useMediaQuery,
|
||||||
|
useTheme,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { deleteQuiz } from "@root/quizes/actions";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
name: string;
|
quiz: Quiz;
|
||||||
openCount?: number;
|
openCount?: number;
|
||||||
applicationCount?: number;
|
applicationCount?: number;
|
||||||
conversionPercent?: number;
|
conversionPercent?: number;
|
||||||
onClickDelete?: () => void;
|
|
||||||
onClickEdit?: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function QuizCard({
|
export default function QuizCard({
|
||||||
name,
|
quiz,
|
||||||
openCount = 0,
|
openCount = 0,
|
||||||
applicationCount = 0,
|
applicationCount = 0,
|
||||||
conversionPercent = 0,
|
conversionPercent = 0,
|
||||||
onClickDelete,
|
|
||||||
onClickEdit,
|
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
function handleEditClick() {
|
||||||
<Box
|
navigate(`/setting/${quiz.id}`);
|
||||||
sx={{
|
}
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
return (
|
||||||
backgroundColor: "white",
|
<Box
|
||||||
width: "560px",
|
sx={{
|
||||||
height: "280px",
|
display: "flex",
|
||||||
p: "20px",
|
flexDirection: "column",
|
||||||
borderRadius: "12px",
|
backgroundColor: "white",
|
||||||
boxSizing: "border-box",
|
width: "560px",
|
||||||
boxShadow: `0px 100px 309px rgba(210, 208, 225, 0.24),
|
height: "280px",
|
||||||
|
p: "20px",
|
||||||
|
borderRadius: "12px",
|
||||||
|
boxSizing: "border-box",
|
||||||
|
boxShadow: `0px 100px 309px rgba(210, 208, 225, 0.24),
|
||||||
0px 41.7776px 129.093px rgba(210, 208, 225, 0.172525),
|
0px 41.7776px 129.093px rgba(210, 208, 225, 0.172525),
|
||||||
0px 22.3363px 69.0192px rgba(210, 208, 225, 0.143066),
|
0px 22.3363px 69.0192px rgba(210, 208, 225, 0.143066),
|
||||||
0px 12.5216px 38.6916px rgba(210, 208, 225, 0.12),
|
0px 12.5216px 38.6916px rgba(210, 208, 225, 0.12),
|
||||||
0px 6.6501px 20.5488px rgba(210, 208, 225, 0.0969343),
|
0px 6.6501px 20.5488px rgba(210, 208, 225, 0.0969343),
|
||||||
0px 2.76726px 8.55082px rgba(210, 208, 225, 0.0674749)`,
|
0px 2.76726px 8.55082px rgba(210, 208, 225, 0.0674749)`,
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<Typography variant="h5">{name}</Typography>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
mt: "10px",
|
|
||||||
gap: "10px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<LinkIcon bgcolor="#EEE4FC" color={theme.palette.brightPurple.main} />
|
|
||||||
<Typography color={theme.palette.grey3.main}>
|
|
||||||
быстрая ссылка ...
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
mt: "32px",
|
|
||||||
mr: "22px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ flex: "1 1 0" }}>
|
|
||||||
<Typography variant="h5">{openCount}</Typography>
|
|
||||||
<Typography color={theme.palette.grey3.main}>Открытий</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ flex: "1 1 0" }}>
|
|
||||||
<Typography variant="h5">{openCount}</Typography>
|
|
||||||
<Typography color={theme.palette.grey3.main}>Заявок</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ flex: "1 1 0" }}>
|
|
||||||
<Typography variant="h5">{openCount} %</Typography>
|
|
||||||
<Typography color={theme.palette.grey3.main}>Конверсия</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
mt: "auto",
|
|
||||||
display: "flex",
|
|
||||||
gap: isMobile ? "10px" : "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
sx={{
|
|
||||||
padding: "10px 39px",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Заявки
|
<Typography variant="h5">{quiz.name}</Typography>
|
||||||
</Button>
|
<Box
|
||||||
<Button
|
sx={{
|
||||||
variant="outlined"
|
display: "flex",
|
||||||
startIcon={<PencilIcon />}
|
alignItems: "center",
|
||||||
onClick={onClickEdit}
|
mt: "10px",
|
||||||
sx={{
|
gap: "10px",
|
||||||
padding: isMobile ? "10px" : "10px 20px",
|
}}
|
||||||
minWidth: "unset",
|
>
|
||||||
color: theme.palette.brightPurple.main,
|
<LinkIcon bgcolor="#EEE4FC" color={theme.palette.brightPurple.main} />
|
||||||
"& .MuiButton-startIcon": {
|
<Typography color={theme.palette.grey3.main}>
|
||||||
marginRight: isMobile ? 0 : "4px",
|
быстрая ссылка ...
|
||||||
marginLeft: 0,
|
</Typography>
|
||||||
},
|
</Box>
|
||||||
}}
|
<Box
|
||||||
>
|
sx={{
|
||||||
{isMobile ? "" : "Редактировать"}
|
display: "flex",
|
||||||
</Button>
|
mt: "32px",
|
||||||
<Button
|
mr: "22px",
|
||||||
variant="outlined"
|
}}
|
||||||
startIcon={<ChartIcon />}
|
>
|
||||||
sx={{
|
<Box sx={{ flex: "1 1 0" }}>
|
||||||
minWidth: "46px",
|
<Typography variant="h5">{openCount}</Typography>
|
||||||
padding: "10px 10px",
|
<Typography color={theme.palette.grey3.main}>Открытий</Typography>
|
||||||
"& .MuiButton-startIcon": {
|
</Box>
|
||||||
mr: 0,
|
<Box sx={{ flex: "1 1 0" }}>
|
||||||
ml: 0,
|
<Typography variant="h5">{applicationCount}</Typography>
|
||||||
},
|
<Typography color={theme.palette.grey3.main}>Заявок</Typography>
|
||||||
}}
|
</Box>
|
||||||
/>
|
<Box sx={{ flex: "1 1 0" }}>
|
||||||
<IconButton
|
<Typography variant="h5">{conversionPercent} %</Typography>
|
||||||
sx={{
|
<Typography color={theme.palette.grey3.main}>Конверсия</Typography>
|
||||||
color: theme.palette.brightPurple.main,
|
</Box>
|
||||||
ml: "auto",
|
</Box>
|
||||||
}}
|
<Box
|
||||||
onClick={onClickDelete}
|
sx={{
|
||||||
>
|
mt: "auto",
|
||||||
<MoreHorizIcon sx={{ transform: "scale(1.75)" }} />
|
display: "flex",
|
||||||
</IconButton>
|
gap: isMobile ? "10px" : "20px",
|
||||||
</Box>
|
}}
|
||||||
</Box>
|
>
|
||||||
);
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
padding: "10px 39px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Заявки
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
startIcon={<PencilIcon />}
|
||||||
|
onClick={handleEditClick}
|
||||||
|
sx={{
|
||||||
|
padding: isMobile ? "10px" : "10px 20px",
|
||||||
|
minWidth: "unset",
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
"& .MuiButton-startIcon": {
|
||||||
|
marginRight: isMobile ? 0 : "4px",
|
||||||
|
marginLeft: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isMobile ? "" : "Редактировать"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
startIcon={<ChartIcon />}
|
||||||
|
sx={{
|
||||||
|
minWidth: "46px",
|
||||||
|
padding: "10px 10px",
|
||||||
|
"& .MuiButton-startIcon": {
|
||||||
|
mr: 0,
|
||||||
|
ml: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
ml: "auto",
|
||||||
|
}}
|
||||||
|
onClick={() => deleteQuiz(quiz.id)}
|
||||||
|
>
|
||||||
|
<MoreHorizIcon sx={{ transform: "scale(1.75)" }} />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,270 +1,270 @@
|
|||||||
|
import { quizApi } from "@api/quiz";
|
||||||
|
import { devlog } from "@frontend/kitui";
|
||||||
|
import BackArrowIcon from "@icons/BackArrowIcon";
|
||||||
|
import { Burger } from "@icons/Burger";
|
||||||
|
import EyeIcon from "@icons/EyeIcon";
|
||||||
|
import { PenaLogoIcon } from "@icons/PenaLogoIcon";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Container,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
TextField,
|
||||||
|
useMediaQuery,
|
||||||
|
useTheme,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { decrementCurrentStep, setQuizes } from "@root/quizes/actions";
|
||||||
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
import { useQuizStore } from "@root/quizes/store";
|
||||||
|
import CustomAvatar from "@ui_kit/Header/Avatar";
|
||||||
|
import NavMenuItem from "@ui_kit/Header/NavMenuItem";
|
||||||
|
import PenaLogo from "@ui_kit/PenaLogo";
|
||||||
|
import Sidebar from "@ui_kit/Sidebar";
|
||||||
import Stepper from "@ui_kit/Stepper";
|
import Stepper from "@ui_kit/Stepper";
|
||||||
import SwitchStepPages from "@ui_kit/switchStepPages";
|
import SwitchStepPages from "@ui_kit/switchStepPages";
|
||||||
import React, { useState } from "react";
|
import { isAxiosError } from "axios";
|
||||||
import PenaLogo from "@ui_kit/PenaLogo";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import {
|
import { useState } from "react";
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Container,
|
|
||||||
FormControl,
|
|
||||||
IconButton,
|
|
||||||
TextField,
|
|
||||||
useMediaQuery,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import BackArrowIcon from "@icons/BackArrowIcon";
|
import useSWR from "swr";
|
||||||
import NavMenuItem from "@ui_kit/Header/NavMenuItem";
|
|
||||||
import EyeIcon from "@icons/EyeIcon";
|
|
||||||
import CustomAvatar from "@ui_kit/Header/Avatar";
|
|
||||||
import Sidebar from "@ui_kit/Sidebar";
|
|
||||||
import { quizStore } from "@root/quizes";
|
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import { Burger } from "@icons/Burger";
|
|
||||||
import { PenaLogoIcon } from "@icons/PenaLogoIcon";
|
|
||||||
import { SidebarMobile } from "./Sidebar/SidebarMobile";
|
import { SidebarMobile } from "./Sidebar/SidebarMobile";
|
||||||
|
|
||||||
const DESCRIPTIONS = [
|
|
||||||
"Настройка стартовой страницы",
|
|
||||||
"Задайте вопросы",
|
|
||||||
"Настройте авторезультаты",
|
|
||||||
"Оценка графа карты вопросов",
|
|
||||||
"Настройте форму контактов",
|
|
||||||
"Установите квиз",
|
|
||||||
"Запустите рекламу",
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export default function StartPage() {
|
export default function StartPage() {
|
||||||
const { listQuizes, updateQuizesList, removeQuiz, createBlank } = quizStore();
|
useSWR("quizes", () => quizApi.getList(), {
|
||||||
const params = Number(useParams().quizId);
|
onSuccess: setQuizes,
|
||||||
const activeStep = listQuizes[params].step;
|
onError: error => {
|
||||||
const theme = useTheme();
|
const message = isAxiosError<string>(error) ? (error.response?.data ?? "") : "";
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
|
||||||
|
|
||||||
const [mobileSidebar, setMobileSidebar] = useState<boolean>(false);
|
devlog("Error creating quiz", error);
|
||||||
|
enqueueSnackbar(`Не удалось получить квизы. ${message}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const theme = useTheme();
|
||||||
|
const quiz = useCurrentQuiz();
|
||||||
|
const currentStep = useQuizStore(state => state.currentStep);
|
||||||
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
||||||
|
const [mobileSidebar, setMobileSidebar] = useState<boolean>(false);
|
||||||
|
|
||||||
const handleBack = () => {
|
const quizConfig = quiz?.config;
|
||||||
let result = listQuizes[params].step - 1;
|
|
||||||
updateQuizesList(params, { step: result ? result : 1 });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/*хедер*/}
|
{/*хедер*/}
|
||||||
<Container
|
<Container
|
||||||
component="nav"
|
component="nav"
|
||||||
maxWidth={false}
|
maxWidth={false}
|
||||||
disableGutters
|
disableGutters
|
||||||
sx={{
|
|
||||||
px: "16px",
|
|
||||||
display: "flex",
|
|
||||||
height: isMobile ? "51px" : "80px",
|
|
||||||
alignItems: "center",
|
|
||||||
bgcolor: isMobile ? "#333647" : "white",
|
|
||||||
borderBottom: "1px solid #E3E3E3",
|
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Link to="/" style={{ display: "flex" }}>
|
|
||||||
{isMobile ? (
|
|
||||||
<PenaLogoIcon style={{ fontSize: "39px", color: "white" }} />
|
|
||||||
) : (
|
|
||||||
<PenaLogo width={124} />
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: isMobile ? "none" : "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
ml: "37px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconButton sx={{ p: "6px" }} onClick={() => handleBack()}>
|
|
||||||
<BackArrowIcon />
|
|
||||||
</IconButton>
|
|
||||||
<FormControl fullWidth variant="standard">
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
id="project-name"
|
|
||||||
placeholder="Название проекта окно"
|
|
||||||
sx={{
|
|
||||||
width: "270px",
|
|
||||||
"& .MuiInputBase-root": {
|
|
||||||
height: "34px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
p: 0,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
inputProps={{
|
|
||||||
sx: {
|
|
||||||
height: "20px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "20px",
|
|
||||||
p: "7px",
|
|
||||||
color: "black",
|
|
||||||
"&::placeholder": {
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</Box>
|
|
||||||
{isTablet ? (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
ml: "auto",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isMobile ? (
|
|
||||||
<Burger
|
|
||||||
onClick={() => setMobileSidebar(!mobileSidebar)}
|
|
||||||
style={{ fontSize: "30px", color: "white", cursor: "pointer" }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<CustomAvatar
|
|
||||||
sx={{
|
sx={{
|
||||||
ml: "11px",
|
px: "16px",
|
||||||
backgroundColor: theme.palette.orange.main,
|
display: "flex",
|
||||||
height: "36px",
|
height: isMobile ? "51px" : "80px",
|
||||||
width: "36px",
|
alignItems: "center",
|
||||||
|
bgcolor: isMobile ? "#333647" : "white",
|
||||||
|
borderBottom: "1px solid #E3E3E3",
|
||||||
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
)}
|
<Link to="/" style={{ display: "flex" }}>
|
||||||
</Box>
|
{isMobile ? (
|
||||||
) : (
|
<PenaLogoIcon style={{ fontSize: "39px", color: "white" }} />
|
||||||
<>
|
) : (
|
||||||
|
<PenaLogo width={124} />
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: isMobile ? "none" : "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
ml: "37px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton sx={{ p: "6px" }} onClick={decrementCurrentStep}>
|
||||||
|
<BackArrowIcon />
|
||||||
|
</IconButton>
|
||||||
|
<FormControl fullWidth variant="standard">
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
id="project-name"
|
||||||
|
placeholder="Название проекта окно"
|
||||||
|
sx={{
|
||||||
|
width: "270px",
|
||||||
|
"& .MuiInputBase-root": {
|
||||||
|
height: "34px",
|
||||||
|
borderRadius: "8px",
|
||||||
|
p: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
inputProps={{
|
||||||
|
sx: {
|
||||||
|
height: "20px",
|
||||||
|
borderRadius: "8px",
|
||||||
|
fontSize: "16px",
|
||||||
|
lineHeight: "20px",
|
||||||
|
p: "7px",
|
||||||
|
color: "black",
|
||||||
|
"&::placeholder": {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
|
{isTablet ? (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
ml: "auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isMobile ? (
|
||||||
|
<Burger
|
||||||
|
onClick={() => setMobileSidebar(!mobileSidebar)}
|
||||||
|
style={{ fontSize: "30px", color: "white", cursor: "pointer" }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CustomAvatar
|
||||||
|
sx={{
|
||||||
|
ml: "11px",
|
||||||
|
backgroundColor: theme.palette.orange.main,
|
||||||
|
height: "36px",
|
||||||
|
width: "36px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "30px",
|
||||||
|
overflow: "hidden",
|
||||||
|
ml: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NavMenuItem text="Редактировать" isActive />
|
||||||
|
<NavMenuItem text="Заявки" />
|
||||||
|
<NavMenuItem text="Аналитика" />
|
||||||
|
<NavMenuItem text="История" />
|
||||||
|
<NavMenuItem text="Помощь" />
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
ml: "auto",
|
||||||
|
gap: "15px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
startIcon={<EyeIcon />}
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
fontSize: "14px",
|
||||||
|
lineHeight: "18px",
|
||||||
|
height: "34px",
|
||||||
|
"& .MuiButton-startIcon": {
|
||||||
|
mr: "3px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Предпросмотр
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
fontSize: "14px",
|
||||||
|
lineHeight: "18px",
|
||||||
|
height: "34px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Опубликовать
|
||||||
|
</Button>
|
||||||
|
<CustomAvatar
|
||||||
|
sx={{
|
||||||
|
ml: "11px",
|
||||||
|
backgroundColor: theme.palette.orange.main,
|
||||||
|
height: "36px",
|
||||||
|
width: "36px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: isMobile ? "block" : "flex",
|
||||||
gap: "30px",
|
}}
|
||||||
overflow: "hidden",
|
|
||||||
ml: "20px",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<NavMenuItem text="Редактировать" isActive />
|
{isMobile ? <SidebarMobile open={mobileSidebar} /> : <Sidebar />}
|
||||||
<NavMenuItem text="Заявки" />
|
<Box
|
||||||
<NavMenuItem text="Аналитика" />
|
sx={{
|
||||||
<NavMenuItem text="История" />
|
background: theme.palette.background.default,
|
||||||
<NavMenuItem text="Помощь" />
|
width: "100%",
|
||||||
|
padding: isMobile ? "16px" : "25px",
|
||||||
|
height: "calc(100vh - 80px)",
|
||||||
|
overflow: "auto",
|
||||||
|
boxSizing: "border-box",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{quizConfig &&
|
||||||
|
<>
|
||||||
|
<Stepper activeStep={currentStep} />
|
||||||
|
<SwitchStepPages
|
||||||
|
activeStep={currentStep}
|
||||||
|
quizType={quizConfig.type}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
{isTablet && [1, 2, 3].includes(currentStep) && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
left: isMobile ? 0 : "230px",
|
||||||
|
bottom: 0,
|
||||||
|
width: isMobile ? "100%" : "calc(100% - 230px)",
|
||||||
|
padding: "20px 40px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
gap: "15px",
|
||||||
|
background: "#FFF",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
startIcon={<EyeIcon />}
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
fontSize: "14px",
|
||||||
|
lineHeight: "18px",
|
||||||
|
height: "34px",
|
||||||
|
"& .MuiButton-startIcon": {
|
||||||
|
mr: "3px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Предпросмотр
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
fontSize: "14px",
|
||||||
|
lineHeight: "18px",
|
||||||
|
height: "34px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Опубликовать
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
</>
|
||||||
sx={{
|
);
|
||||||
display: "flex",
|
|
||||||
ml: "auto",
|
|
||||||
gap: "15px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
startIcon={<EyeIcon />}
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.brightPurple.main,
|
|
||||||
fontSize: "14px",
|
|
||||||
lineHeight: "18px",
|
|
||||||
height: "34px",
|
|
||||||
"& .MuiButton-startIcon": {
|
|
||||||
mr: "3px",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Предпросмотр
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
sx={{
|
|
||||||
fontSize: "14px",
|
|
||||||
lineHeight: "18px",
|
|
||||||
height: "34px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Опубликовать
|
|
||||||
</Button>
|
|
||||||
<CustomAvatar
|
|
||||||
sx={{
|
|
||||||
ml: "11px",
|
|
||||||
backgroundColor: theme.palette.orange.main,
|
|
||||||
height: "36px",
|
|
||||||
width: "36px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: isMobile ? "block" : "flex",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isMobile ? <SidebarMobile open={mobileSidebar} /> : <Sidebar />}
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
background: theme.palette.background.default,
|
|
||||||
width: "100%",
|
|
||||||
padding: isMobile ? "16px" : "25px",
|
|
||||||
height: "calc(100vh - 80px)",
|
|
||||||
overflow: "auto",
|
|
||||||
boxSizing: "border-box",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Stepper
|
|
||||||
activeStep={activeStep}
|
|
||||||
desc={DESCRIPTIONS[activeStep - 1]}
|
|
||||||
/>
|
|
||||||
<SwitchStepPages
|
|
||||||
activeStep={activeStep}
|
|
||||||
quizType={listQuizes[params].config.type}
|
|
||||||
startpage={listQuizes[params].startpage}
|
|
||||||
createResult={listQuizes[params].createResult}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
{isTablet && activeStep === 1 && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
left: isMobile ? 0 : "230px",
|
|
||||||
bottom: 0,
|
|
||||||
width: isMobile ? "100%" : "calc(100% - 230px)",
|
|
||||||
padding: "20px 40px",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
gap: "15px",
|
|
||||||
background: "#FFF",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
startIcon={<EyeIcon />}
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.brightPurple.main,
|
|
||||||
fontSize: "14px",
|
|
||||||
lineHeight: "18px",
|
|
||||||
height: "34px",
|
|
||||||
"& .MuiButton-startIcon": {
|
|
||||||
mr: "3px",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Предпросмотр
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
sx={{
|
|
||||||
fontSize: "14px",
|
|
||||||
lineHeight: "18px",
|
|
||||||
height: "34px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Опубликовать
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,71 +1,71 @@
|
|||||||
import { Box, Button, useTheme } from "@mui/material";
|
import { Box, Button } from "@mui/material";
|
||||||
import CreationCard from "@ui_kit/CreationCard";
|
import CreationCard from "@ui_kit/CreationCard";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
import quizCreationImage1 from "../../assets/quiz-creation-1.png";
|
import quizCreationImage1 from "../../assets/quiz-creation-1.png";
|
||||||
import quizCreationImage2 from "../../assets/quiz-creation-2.png";
|
import quizCreationImage2 from "../../assets/quiz-creation-2.png";
|
||||||
import { useParams } from "react-router-dom";
|
import { setQuizType } from "@root/quizes/actions";
|
||||||
import { quizStore } from "@root/quizes";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
|
||||||
|
|
||||||
export default function StepOne() {
|
export default function StepOne() {
|
||||||
const params = Number(useParams().quizId);
|
const navigate = useNavigate();
|
||||||
const theme = useTheme();
|
const quiz = useCurrentQuiz();
|
||||||
|
const config = quiz?.config;
|
||||||
|
|
||||||
const { listQuizes, updateQuizesList } = quizStore();
|
if (!config) return null;
|
||||||
return (
|
|
||||||
<Box
|
return (
|
||||||
sx={{
|
<Box
|
||||||
overflowX: "scroll",
|
sx={{
|
||||||
padding: "0 5px 15px",
|
overflowX: "scroll",
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
padding: "0 5px 15px",
|
||||||
}}
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
>
|
}}
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
minWidth: "720px",
|
|
||||||
display: "flex",
|
|
||||||
gap: "20px",
|
|
||||||
mt: "60px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="text"
|
|
||||||
data-cy="create-quiz-card"
|
|
||||||
onClick={() => {
|
|
||||||
let SPageClone = listQuizes[params].config;
|
|
||||||
SPageClone.type = "quize";
|
|
||||||
updateQuizesList(params, { config: SPageClone });
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<CreationCard
|
<Box
|
||||||
header="Создание квиз-опроса"
|
sx={{
|
||||||
text="У стартовой страницы одна ключевая задача - заинтересовать посетителя пройти квиз. С ней сложно ошибиться, сформулируйте суть предложения и подберите живую фотографию, остальное мы сделаем за вас"
|
minWidth: "720px",
|
||||||
image={quizCreationImage1}
|
display: "flex",
|
||||||
border={
|
gap: "20px",
|
||||||
listQuizes[params].config.type === "quize"
|
mt: "60px",
|
||||||
? "1px solid #7E2AEA"
|
}}
|
||||||
: "none"
|
>
|
||||||
}
|
<Button
|
||||||
/>
|
variant="text"
|
||||||
</Button>
|
data-cy="create-quiz-card"
|
||||||
<Button
|
onClick={() => {
|
||||||
variant="text"
|
setQuizType(quiz.id, "quiz", navigate);
|
||||||
onClick={() => {
|
}}
|
||||||
let SPageClone = listQuizes[params].config;
|
>
|
||||||
SPageClone.type = "form";
|
<CreationCard
|
||||||
updateQuizesList(params, { config: SPageClone });
|
header="Создание квиз-опроса"
|
||||||
}}
|
text="У стартовой страницы одна ключевая задача - заинтересовать посетителя пройти квиз. С ней сложно ошибиться, сформулируйте суть предложения и подберите живую фотографию, остальное мы сделаем за вас"
|
||||||
>
|
image={quizCreationImage1}
|
||||||
<CreationCard
|
border={
|
||||||
header="Создание анкеты"
|
config.type === "quiz"
|
||||||
text="У стартовой страницы одна ключевая задача - заинтересовать посетителя пройти квиз. С ней сложно ошибиться, сформулируйте суть предложения и подберите живую фотографию, остальное мы сделаем за вас"
|
? "1px solid #7E2AEA"
|
||||||
image={quizCreationImage2}
|
: "none"
|
||||||
border={
|
}
|
||||||
listQuizes[params].config.type === "form"
|
/>
|
||||||
? "1px solid #7E2AEA"
|
</Button>
|
||||||
: "none"
|
<Button
|
||||||
}
|
variant="text"
|
||||||
/>
|
onClick={() => {
|
||||||
</Button>
|
setQuizType(quiz.id,"form", navigate);
|
||||||
</Box>
|
}}
|
||||||
</Box>
|
>
|
||||||
);
|
<CreationCard
|
||||||
|
header="Создание анкеты"
|
||||||
|
text="У стартовой страницы одна ключевая задача - заинтересовать посетителя пройти квиз. С ней сложно ошибиться, сформулируйте суть предложения и подберите живую фотографию, остальное мы сделаем за вас"
|
||||||
|
image={quizCreationImage2}
|
||||||
|
border={
|
||||||
|
config.type === "form"
|
||||||
|
? "1px solid #7E2AEA"
|
||||||
|
: "none"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,93 +1,99 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Typography,
|
Typography,
|
||||||
useTheme,
|
useMediaQuery,
|
||||||
useMediaQuery,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import CardWithImage from "./CardWithImage";
|
import { setQuizStartpageType } from "@root/quizes/actions";
|
||||||
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
import cardImage1 from "../../assets/card-1.png";
|
import cardImage1 from "../../assets/card-1.png";
|
||||||
import cardImage2 from "../../assets/card-2.png";
|
import cardImage2 from "../../assets/card-2.png";
|
||||||
import cardImage3 from "../../assets/card-3.png";
|
import cardImage3 from "../../assets/card-3.png";
|
||||||
import { quizStore } from "@root/quizes";
|
import CardWithImage from "./CardWithImage";
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
|
|
||||||
export default function Steptwo() {
|
export default function Steptwo() {
|
||||||
const params = Number(useParams().quizId);
|
const navigate = useNavigate();
|
||||||
const { listQuizes, updateQuizesList } = quizStore();
|
const theme = useTheme();
|
||||||
const theme = useTheme();
|
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1300));
|
||||||
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1300));
|
const quiz = useCurrentQuiz();
|
||||||
|
|
||||||
return (
|
const config = quiz?.config;
|
||||||
<Box sx={{ mt: "60px" }}>
|
|
||||||
<Typography variant="h5">Стартовая страница</Typography>
|
if (!config) return null;
|
||||||
<Box
|
|
||||||
sx={{
|
return (
|
||||||
overflowX: "scroll",
|
<Box sx={{ mt: "60px" }}>
|
||||||
paddingBottom: "15px",
|
<Typography variant="h5">Стартовая страница</Typography>
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
<Box
|
||||||
}}
|
sx={{
|
||||||
>
|
overflowX: "scroll",
|
||||||
<Box
|
paddingBottom: "15px",
|
||||||
sx={{
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
minWidth: "950px",
|
}}
|
||||||
display: "flex",
|
>
|
||||||
gap: "20px",
|
<Box
|
||||||
mt: "40px",
|
sx={{
|
||||||
padding: isSmallMonitor ? "0 15px 15px" : 0,
|
minWidth: "950px",
|
||||||
}}
|
display: "flex",
|
||||||
>
|
gap: "20px",
|
||||||
<Button
|
mt: "40px",
|
||||||
variant="text"
|
padding: isSmallMonitor ? "0 15px 15px" : 0,
|
||||||
data-cy="select-quiz-layout-standard"
|
}}
|
||||||
onClick={() => {
|
>
|
||||||
updateQuizesList(params, { startpage: "standard" });
|
<Button
|
||||||
}}
|
variant="text"
|
||||||
>
|
data-cy="select-quiz-layout-standard"
|
||||||
<CardWithImage
|
onClick={() => {
|
||||||
image={cardImage1}
|
setQuizStartpageType(quiz.id, "standard", navigate);
|
||||||
text="Standard"
|
}}
|
||||||
border={
|
>
|
||||||
listQuizes[params].startpage === "standard"
|
<CardWithImage
|
||||||
? "1px solid #7E2AEA"
|
image={cardImage1}
|
||||||
: "none"
|
text="Standard"
|
||||||
}
|
border={
|
||||||
/>
|
config.startpageType === "standard"
|
||||||
</Button>
|
? "1px solid #7E2AEA"
|
||||||
<Button
|
: "none"
|
||||||
variant="text"
|
}
|
||||||
onClick={() => {
|
/>
|
||||||
updateQuizesList(params, { startpage: "expanded" });
|
</Button>
|
||||||
}}
|
<Button
|
||||||
>
|
variant="text"
|
||||||
<CardWithImage
|
onClick={() => {
|
||||||
image={cardImage2}
|
setQuizStartpageType(quiz.id, "expanded", navigate);
|
||||||
text="Expanded"
|
}}
|
||||||
border={
|
>
|
||||||
listQuizes[params].startpage === "expanded"
|
<CardWithImage
|
||||||
? "1px solid #7E2AEA"
|
image={cardImage2}
|
||||||
: "none"
|
text="Expanded"
|
||||||
}
|
border={
|
||||||
/>
|
config.startpageType === "expanded"
|
||||||
</Button>
|
? "1px solid #7E2AEA"
|
||||||
<Button
|
: "none"
|
||||||
variant="text"
|
}
|
||||||
onClick={() => {
|
/>
|
||||||
updateQuizesList(params, { startpage: "centered" });
|
</Button>
|
||||||
}}
|
<Button
|
||||||
>
|
variant="text"
|
||||||
<CardWithImage
|
onClick={() => {
|
||||||
image={cardImage3}
|
setQuizStartpageType(quiz.id, "centered", navigate);
|
||||||
text="Centered"
|
}}
|
||||||
border={
|
>
|
||||||
listQuizes[params].startpage === "centered"
|
<CardWithImage
|
||||||
? "1px solid #7E2AEA"
|
image={cardImage3}
|
||||||
: "none"
|
text="Centered"
|
||||||
}
|
border={
|
||||||
/>
|
config.startpageType === "centered"
|
||||||
</Button>
|
? "1px solid #7E2AEA"
|
||||||
|
: "none"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
);
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,7 +80,7 @@ export const setQuestionFieldOptimistic = async <T extends keyof Question>(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isAxiosCanceledError(error)) return;
|
if (isAxiosCanceledError(error)) return;
|
||||||
|
|
||||||
devlog("Error editing question", { error, question: question, currentUpdatedQuestion });
|
devlog("Error editing question", { error, question, currentUpdatedQuestion });
|
||||||
enqueueSnackbar("Не удалось сохранить вопрос");
|
enqueueSnackbar("Не удалось сохранить вопрос");
|
||||||
if (!savedOriginalQuestion) {
|
if (!savedOriginalQuestion) {
|
||||||
devlog("Cannot rollback question");
|
devlog("Cannot rollback question");
|
||||||
|
|||||||
195
src/stores/quizes/actions.ts
Normal file
195
src/stores/quizes/actions.ts
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
import { quizApi } from "@api/quiz";
|
||||||
|
import { devlog, getMessageFromFetchError } from "@frontend/kitui";
|
||||||
|
import { quizToEditQuizRequest } from "@model/quiz/edit";
|
||||||
|
import { Quiz, RawQuiz, rawQuizToQuiz } from "@model/quiz/quiz";
|
||||||
|
import { QuizConfig, QuizSetupStep, maxQuizSetupSteps } from "@model/quizSettings";
|
||||||
|
import { produce } from "immer";
|
||||||
|
import { enqueueSnackbar } from "notistack";
|
||||||
|
import { NavigateFunction } from "react-router-dom";
|
||||||
|
import { QuizStore, useQuizStore } from "./store";
|
||||||
|
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
||||||
|
|
||||||
|
|
||||||
|
export const setQuizes = (quizes: RawQuiz[] | null) => setProducedState(state => {
|
||||||
|
state.quizById = {};
|
||||||
|
if (quizes === null) return;
|
||||||
|
|
||||||
|
quizes.forEach(quiz => state.quizById[quiz.id] = rawQuizToQuiz(quiz));
|
||||||
|
}, {
|
||||||
|
type: "setQuizes",
|
||||||
|
quizes,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setQuiz = (quiz: Quiz) => setProducedState(state => {
|
||||||
|
state.quizById[quiz.id] = quiz;
|
||||||
|
}, {
|
||||||
|
type: "setQuiz",
|
||||||
|
quiz,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const removeQuiz = (quizId: number) => setProducedState(state => {
|
||||||
|
delete state.quizById[quizId];
|
||||||
|
}, {
|
||||||
|
type: "removeQuiz",
|
||||||
|
quizId,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setQuizField = <T extends keyof Quiz>(
|
||||||
|
quizId: number,
|
||||||
|
field: T,
|
||||||
|
value: Quiz[T],
|
||||||
|
) => setProducedState(state => {
|
||||||
|
const quiz = state.quizById[quizId];
|
||||||
|
if (!quiz) return;
|
||||||
|
|
||||||
|
quiz[field] = value;
|
||||||
|
}, {
|
||||||
|
type: "setQuizField",
|
||||||
|
quizId,
|
||||||
|
field,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateQuiz = (
|
||||||
|
quizId: number,
|
||||||
|
updateFn: (quiz: Quiz) => void,
|
||||||
|
) => setProducedState(state => {
|
||||||
|
const quiz = state.quizById[quizId];
|
||||||
|
if (!quiz) return;
|
||||||
|
|
||||||
|
updateFn(quiz);
|
||||||
|
}, {
|
||||||
|
type: "updateQuiz",
|
||||||
|
quizId,
|
||||||
|
updateFn: updateFn.toString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const incrementCurrentStep = () => setProducedState(state => {
|
||||||
|
state.currentStep = Math.min(
|
||||||
|
maxQuizSetupSteps, state.currentStep + 1
|
||||||
|
) as QuizSetupStep;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const decrementCurrentStep = () => setProducedState(state => {
|
||||||
|
state.currentStep = Math.max(
|
||||||
|
1, state.currentStep - 1
|
||||||
|
) as QuizSetupStep;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setCurrentStep = (step: number) => setProducedState(state => {
|
||||||
|
state.currentStep = Math.max(0, Math.min(maxQuizSetupSteps, step)) as QuizSetupStep;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createQuiz = async (navigate: NavigateFunction) => {
|
||||||
|
try {
|
||||||
|
const quiz = await quizApi.create({
|
||||||
|
name: "Quiz name",
|
||||||
|
description: "Quiz description",
|
||||||
|
});
|
||||||
|
|
||||||
|
setQuiz(rawQuizToQuiz(quiz));
|
||||||
|
navigate(`/setting/${quiz.id}`);
|
||||||
|
} catch (error) {
|
||||||
|
devlog("Error creating quiz", error);
|
||||||
|
|
||||||
|
const message = getMessageFromFetchError(error) ?? "";
|
||||||
|
enqueueSnackbar(`Не удалось создать квиз. ${message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteQuiz = async (quizId: number) => {
|
||||||
|
try {
|
||||||
|
await quizApi.delete(quizId);
|
||||||
|
|
||||||
|
removeQuiz(quizId);
|
||||||
|
} catch (error) {
|
||||||
|
devlog("Error deleting quiz", error);
|
||||||
|
|
||||||
|
const message = getMessageFromFetchError(error) ?? "";
|
||||||
|
enqueueSnackbar(`Не удалось удалить квиз. ${message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setQuizType = (
|
||||||
|
quizId: number,
|
||||||
|
quizType: QuizConfig["type"],
|
||||||
|
navigate: NavigateFunction,
|
||||||
|
) => {
|
||||||
|
updateQuizWithFnOptimistic(
|
||||||
|
quizId,
|
||||||
|
quiz => {
|
||||||
|
quiz.config.type = quizType;
|
||||||
|
},
|
||||||
|
navigate,
|
||||||
|
);
|
||||||
|
incrementCurrentStep();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setQuizStartpageType = (
|
||||||
|
quizId: number,
|
||||||
|
startpageType: QuizConfig["startpageType"],
|
||||||
|
navigate: NavigateFunction,
|
||||||
|
) => {
|
||||||
|
updateQuizWithFnOptimistic(
|
||||||
|
quizId,
|
||||||
|
quiz => {
|
||||||
|
quiz.config.startpageType = startpageType;
|
||||||
|
},
|
||||||
|
navigate,
|
||||||
|
);
|
||||||
|
incrementCurrentStep();
|
||||||
|
};
|
||||||
|
|
||||||
|
let savedOriginalQuiz: Quiz | null = null;
|
||||||
|
let controller: AbortController | null = null;
|
||||||
|
|
||||||
|
export const updateQuizWithFnOptimistic = async (
|
||||||
|
quizId: number,
|
||||||
|
updateFn: (quiz: Quiz) => void,
|
||||||
|
navigate: NavigateFunction,
|
||||||
|
rollbackOnError = true,
|
||||||
|
) => {
|
||||||
|
const quiz = useQuizStore.getState().quizById[quizId] ?? null;
|
||||||
|
if (!quiz) return;
|
||||||
|
|
||||||
|
const currentUpdatedQuiz = produce(quiz, updateFn);
|
||||||
|
|
||||||
|
controller?.abort();
|
||||||
|
controller = new AbortController();
|
||||||
|
savedOriginalQuiz ??= quiz;
|
||||||
|
|
||||||
|
setQuiz(currentUpdatedQuiz);
|
||||||
|
try {
|
||||||
|
const { updated } = await quizApi.edit(quizToEditQuizRequest(currentUpdatedQuiz), controller.signal);
|
||||||
|
// await new Promise((resolve, reject) => setTimeout(reject, 2000, new Error("Api rejected")));
|
||||||
|
|
||||||
|
setQuizField(quiz.id, "version", updated);
|
||||||
|
navigate(`/setting/${quizId}`, { replace: true });
|
||||||
|
|
||||||
|
controller = null;
|
||||||
|
savedOriginalQuiz = null;
|
||||||
|
} catch (error) {
|
||||||
|
if (isAxiosCanceledError(error)) return;
|
||||||
|
|
||||||
|
devlog("Error editing quiz", { error, quiz, currentUpdatedQuiz });
|
||||||
|
enqueueSnackbar("Не удалось сохранить настройки квиза");
|
||||||
|
|
||||||
|
if (rollbackOnError) {
|
||||||
|
if (!savedOriginalQuiz) {
|
||||||
|
devlog("Cannot rollback quiz");
|
||||||
|
throw new Error("Cannot rollback quiz");
|
||||||
|
}
|
||||||
|
setQuiz(savedOriginalQuiz);
|
||||||
|
}
|
||||||
|
|
||||||
|
controller = null;
|
||||||
|
savedOriginalQuiz = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function setProducedState<A extends string | { type: unknown; }>(
|
||||||
|
recipe: (state: QuizStore) => void,
|
||||||
|
action?: A,
|
||||||
|
) {
|
||||||
|
useQuizStore.setState(state => produce(state, recipe), false, action);
|
||||||
|
}
|
||||||
17
src/stores/quizes/hooks.ts
Normal file
17
src/stores/quizes/hooks.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Quiz } from "@model/quiz/quiz";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { useQuizStore } from "./store";
|
||||||
|
|
||||||
|
|
||||||
|
export function useQuizArray(): Quiz[] {
|
||||||
|
const quizes = useQuizStore(state => state.quizById);
|
||||||
|
|
||||||
|
return Object.values(quizes).flatMap(quiz => quiz ? [quiz] : []);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCurrentQuiz() {
|
||||||
|
const quizId = parseInt(useParams().quizId ?? "");
|
||||||
|
const quiz = useQuizStore(state => state.quizById[quizId]);
|
||||||
|
|
||||||
|
return quiz;
|
||||||
|
}
|
||||||
25
src/stores/quizes/store.ts
Normal file
25
src/stores/quizes/store.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Quiz } from "@model/quiz/quiz";
|
||||||
|
import { QuizSetupStep } from "@model/quizSettings";
|
||||||
|
import { create } from "zustand";
|
||||||
|
import { devtools } from "zustand/middleware";
|
||||||
|
|
||||||
|
|
||||||
|
export type QuizStore = {
|
||||||
|
quizById: Record<number, Quiz | undefined>;
|
||||||
|
currentStep: QuizSetupStep;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState: QuizStore = {
|
||||||
|
quizById: {},
|
||||||
|
currentStep: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useQuizStore = create<QuizStore>()(
|
||||||
|
devtools(
|
||||||
|
() => initialState,
|
||||||
|
{
|
||||||
|
name: "QuizStore",
|
||||||
|
enabled: process.env.NODE_ENV === "development",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
@ -1,103 +0,0 @@
|
|||||||
import { quizApi } from "@api/quiz";
|
|
||||||
import { devlog } from "@frontend/kitui";
|
|
||||||
import { produce } from "immer";
|
|
||||||
import { BackendQuiz } from "model/quiz/quiz";
|
|
||||||
import { enqueueSnackbar } from "notistack";
|
|
||||||
import { NavigateFunction } from "react-router";
|
|
||||||
import { create } from "zustand";
|
|
||||||
import { devtools } from "zustand/middleware";
|
|
||||||
|
|
||||||
|
|
||||||
type QuizStore = {
|
|
||||||
quizes: Record<number, BackendQuiz | undefined>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialState: QuizStore = {
|
|
||||||
quizes: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useQuizStore = create<QuizStore>()(
|
|
||||||
devtools(
|
|
||||||
() => initialState,
|
|
||||||
{
|
|
||||||
name: "QuizStore",
|
|
||||||
enabled: process.env.NODE_ENV === "development",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export const setQuizes = (quizes: QuizStore["quizes"]) => useQuizStore.setState({ quizes });
|
|
||||||
|
|
||||||
export const setQuiz = (quiz: BackendQuiz) => setProducedState(state => {
|
|
||||||
state.quizes[quiz.id] = quiz;
|
|
||||||
}, {
|
|
||||||
type: "setQuiz",
|
|
||||||
quiz,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const removeQuiz = (quizId: number) => setProducedState(state => {
|
|
||||||
delete state.quizes[quizId];
|
|
||||||
}, {
|
|
||||||
type: "removeQuiz",
|
|
||||||
quizId,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const setQuizField = <T extends keyof BackendQuiz>(
|
|
||||||
quizId: number,
|
|
||||||
field: T,
|
|
||||||
value: BackendQuiz[T],
|
|
||||||
) => setProducedState(state => {
|
|
||||||
const quiz = state.quizes[quizId];
|
|
||||||
if (!quiz) return;
|
|
||||||
|
|
||||||
quiz[field] = value;
|
|
||||||
}, {
|
|
||||||
type: "setQuizField",
|
|
||||||
quizId,
|
|
||||||
field,
|
|
||||||
value,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateQuiz = (
|
|
||||||
quizId: number,
|
|
||||||
updateFn: (quiz: BackendQuiz) => void,
|
|
||||||
) => setProducedState(state => {
|
|
||||||
const quiz = state.quizes[quizId];
|
|
||||||
if (!quiz) return;
|
|
||||||
|
|
||||||
updateFn(quiz);
|
|
||||||
}, {
|
|
||||||
type: "updateQuiz",
|
|
||||||
quizId,
|
|
||||||
updateFn: updateFn.toString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createQuiz = async (navigate: NavigateFunction) => {
|
|
||||||
try {
|
|
||||||
const quiz = await quizApi.create();
|
|
||||||
|
|
||||||
setQuiz(quiz);
|
|
||||||
navigate(`/settings/${quiz.id}`);
|
|
||||||
} catch (error) {
|
|
||||||
devlog("Error creating quiz", error);
|
|
||||||
enqueueSnackbar("Не удалось создать квиз");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteQuiz = async (quizId: number) => {
|
|
||||||
try {
|
|
||||||
await quizApi.delete(quizId);
|
|
||||||
|
|
||||||
removeQuiz(quizId);
|
|
||||||
} catch (error) {
|
|
||||||
devlog("Error deleting quiz", error);
|
|
||||||
enqueueSnackbar("Не удалось удалить квиз");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function setProducedState<A extends string | { type: unknown; }>(
|
|
||||||
recipe: (state: QuizStore) => void,
|
|
||||||
action?: A,
|
|
||||||
) {
|
|
||||||
useQuizStore.setState(state => produce(state, recipe), false, action);
|
|
||||||
}
|
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import { Box, Button, LinearProgress, Paper, Typography } from "@mui/material";
|
import { Box, Button, LinearProgress, Paper, Typography } from "@mui/material";
|
||||||
import { questionStore } from "@root/questions";
|
import { questionStore } from "@root/questions";
|
||||||
import {
|
import {
|
||||||
decrementCurrentQuestionIndex,
|
decrementCurrentQuestionIndex,
|
||||||
incrementCurrentQuestionIndex,
|
incrementCurrentQuestionIndex,
|
||||||
useQuizPreviewStore,
|
useQuizPreviewStore,
|
||||||
} from "@root/quizPreview";
|
} from "@root/quizPreview";
|
||||||
import { DefiniteQuestionType } from "model/questionTypes/shared";
|
import { AnyQuizQuestion } from "model/questionTypes/shared";
|
||||||
import { FC, useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
||||||
import Date from "./QuizPreviewQuestionTypes/Date";
|
import Date from "./QuizPreviewQuestionTypes/Date";
|
||||||
@ -21,144 +21,140 @@ import Text from "./QuizPreviewQuestionTypes/Text";
|
|||||||
import Variant from "./QuizPreviewQuestionTypes/Variant";
|
import Variant from "./QuizPreviewQuestionTypes/Variant";
|
||||||
import Varimg from "./QuizPreviewQuestionTypes/Varimg";
|
import Varimg from "./QuizPreviewQuestionTypes/Varimg";
|
||||||
|
|
||||||
const QuestionPreviewComponentByType: Record<DefiniteQuestionType, FC<any>> = {
|
|
||||||
variant: Variant,
|
|
||||||
images: Images,
|
|
||||||
varimg: Varimg,
|
|
||||||
emoji: Emoji,
|
|
||||||
text: Text,
|
|
||||||
select: Select,
|
|
||||||
date: Date,
|
|
||||||
number: Number,
|
|
||||||
file: File,
|
|
||||||
page: Page,
|
|
||||||
rating: Rating,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function QuizPreviewLayout() {
|
export default function QuizPreviewLayout() {
|
||||||
const quizId = useParams().quizId ?? 0;
|
const quizId = useParams().quizId ?? 0;
|
||||||
const listQuestions = questionStore((state) => state.listQuestions);
|
const listQuestions = questionStore((state) => state.listQuestions);
|
||||||
const currentQuizStep = useQuizPreviewStore(
|
const currentQuizStep = useQuizPreviewStore(
|
||||||
(state) => state.currentQuestionIndex
|
(state) => state.currentQuestionIndex
|
||||||
);
|
);
|
||||||
|
|
||||||
const quizQuestions = listQuestions[quizId] ?? [];
|
const quizQuestions = listQuestions[quizId] ?? [];
|
||||||
const nonDeletedQuizQuestions = quizQuestions.filter(
|
const nonDeletedQuizQuestions = quizQuestions.filter(
|
||||||
(question) => !question.deleted
|
(question) => !question.deleted
|
||||||
);
|
);
|
||||||
const maxCurrentQuizStep =
|
const maxCurrentQuizStep =
|
||||||
nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0;
|
nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0;
|
||||||
const currentProgress = Math.floor(
|
const currentProgress = Math.floor(
|
||||||
(currentQuizStep / maxCurrentQuizStep) * 100
|
(currentQuizStep / maxCurrentQuizStep) * 100
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentQuestion = nonDeletedQuizQuestions[currentQuizStep];
|
const currentQuestion = nonDeletedQuizQuestions[currentQuizStep];
|
||||||
const QuestionComponent = currentQuestion
|
|
||||||
? QuestionPreviewComponentByType[
|
|
||||||
currentQuestion.type as DefiniteQuestionType
|
|
||||||
]
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const questionElement = QuestionComponent ? (
|
useEffect(
|
||||||
<QuestionComponent key={currentQuestion.id} question={currentQuestion} />
|
function resetCurrentQuizStep() {
|
||||||
) : null;
|
if (currentQuizStep > maxCurrentQuizStep) {
|
||||||
|
decrementCurrentQuestionIndex();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentQuizStep, maxCurrentQuizStep]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(
|
return (
|
||||||
function resetCurrentQuizStep() {
|
<Paper
|
||||||
if (currentQuizStep > maxCurrentQuizStep) {
|
className="quiz-preview-draghandle"
|
||||||
decrementCurrentQuestionIndex();
|
data-cy="quiz-preview-layout"
|
||||||
}
|
sx={{
|
||||||
},
|
height: "100%",
|
||||||
[currentQuizStep, maxCurrentQuizStep]
|
display: "flex",
|
||||||
);
|
flexDirection: "column",
|
||||||
|
flexGrow: 1,
|
||||||
return (
|
borderRadius: "12px",
|
||||||
<Paper
|
pointerEvents: "auto",
|
||||||
className="quiz-preview-draghandle"
|
}}
|
||||||
data-cy="quiz-preview-layout"
|
|
||||||
sx={{
|
|
||||||
height: "100%",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
flexGrow: 1,
|
|
||||||
borderRadius: "12px",
|
|
||||||
pointerEvents: "auto",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
p: "16px",
|
|
||||||
whiteSpace: "break-spaces",
|
|
||||||
overflowY: "auto",
|
|
||||||
flexGrow: 1,
|
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{questionElement}
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
mt: "auto",
|
|
||||||
p: "16px",
|
|
||||||
display: "flex",
|
|
||||||
borderTop: "1px solid #E3E3E3",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
flexGrow: 1,
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: 1,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Typography>
|
<Box
|
||||||
{nonDeletedQuizQuestions.length > 0
|
sx={{
|
||||||
? `Вопрос ${currentQuizStep + 1} из ${
|
p: "16px",
|
||||||
nonDeletedQuizQuestions.length
|
whiteSpace: "break-spaces",
|
||||||
}`
|
overflowY: "auto",
|
||||||
: "Нет вопросов"}
|
flexGrow: 1,
|
||||||
</Typography>
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
{nonDeletedQuizQuestions.length > 0 && (
|
}}
|
||||||
<LinearProgress
|
>
|
||||||
variant="determinate"
|
<QuestionPreviewComponent question={currentQuestion} />
|
||||||
value={currentProgress}
|
</Box>
|
||||||
sx={{
|
<Box
|
||||||
"&.MuiLinearProgress-colorPrimary": {
|
sx={{
|
||||||
backgroundColor: "fadePurple.main",
|
mt: "auto",
|
||||||
},
|
p: "16px",
|
||||||
"& .MuiLinearProgress-barColorPrimary": {
|
display: "flex",
|
||||||
backgroundColor: "brightPurple.main",
|
borderTop: "1px solid #E3E3E3",
|
||||||
},
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
)}
|
<Box
|
||||||
</Box>
|
sx={{
|
||||||
<Box
|
flexGrow: 1,
|
||||||
sx={{
|
display: "flex",
|
||||||
ml: 2,
|
flexDirection: "column",
|
||||||
display: "flex",
|
gap: 1,
|
||||||
gap: 1,
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<Typography>
|
||||||
<Button
|
{nonDeletedQuizQuestions.length > 0
|
||||||
variant="outlined"
|
? `Вопрос ${currentQuizStep + 1} из ${nonDeletedQuizQuestions.length
|
||||||
onClick={decrementCurrentQuestionIndex}
|
}`
|
||||||
disabled={currentQuizStep === 0}
|
: "Нет вопросов"}
|
||||||
sx={{ px: 1, minWidth: 0 }}
|
</Typography>
|
||||||
>
|
{nonDeletedQuizQuestions.length > 0 && (
|
||||||
<ArrowLeft />
|
<LinearProgress
|
||||||
</Button>
|
variant="determinate"
|
||||||
<Button
|
value={currentProgress}
|
||||||
variant="contained"
|
sx={{
|
||||||
onClick={() => incrementCurrentQuestionIndex(maxCurrentQuizStep)}
|
"&.MuiLinearProgress-colorPrimary": {
|
||||||
disabled={currentQuizStep >= maxCurrentQuizStep}
|
backgroundColor: "fadePurple.main",
|
||||||
>
|
},
|
||||||
Далее
|
"& .MuiLinearProgress-barColorPrimary": {
|
||||||
</Button>
|
backgroundColor: "brightPurple.main",
|
||||||
</Box>
|
},
|
||||||
</Box>
|
}}
|
||||||
</Paper>
|
/>
|
||||||
);
|
)}
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
ml: 2,
|
||||||
|
display: "flex",
|
||||||
|
gap: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={decrementCurrentQuestionIndex}
|
||||||
|
disabled={currentQuizStep === 0}
|
||||||
|
sx={{ px: 1, minWidth: 0 }}
|
||||||
|
>
|
||||||
|
<ArrowLeft />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => incrementCurrentQuestionIndex(maxCurrentQuizStep)}
|
||||||
|
disabled={currentQuizStep >= maxCurrentQuizStep}
|
||||||
|
>
|
||||||
|
Далее
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function QuestionPreviewComponent({ question }: {
|
||||||
|
question: AnyQuizQuestion;
|
||||||
|
}) {
|
||||||
|
switch (question.type) {
|
||||||
|
case "variant": return <Variant question={question} />;
|
||||||
|
case "images": return <Images question={question} />;
|
||||||
|
case "varimg": return <Varimg question={question} />;
|
||||||
|
case "emoji": return <Emoji question={question} />;
|
||||||
|
case "text": return <Text question={question} />;
|
||||||
|
case "select": return <Select question={question} />;
|
||||||
|
case "date": return <Date question={question} />;
|
||||||
|
case "number": return <Number question={question} />;
|
||||||
|
case "file": return <File question={question} />;
|
||||||
|
case "page": return <Page question={question} />;
|
||||||
|
case "rating": return <Rating question={question} />;
|
||||||
|
default: throw new Error("Unknown question type");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Box,
|
Box,
|
||||||
useTheme,
|
useTheme,
|
||||||
List,
|
List,
|
||||||
Typography,
|
Typography,
|
||||||
IconButton,
|
IconButton,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { quizStore } from "@root/quizes";
|
import { quizStore } from "@root/quizes";
|
||||||
@ -24,156 +24,157 @@ import PuzzlePieceIcon from "@icons/PuzzlePieceIcon";
|
|||||||
import GearIcon from "@icons/GearIcon";
|
import GearIcon from "@icons/GearIcon";
|
||||||
import LayoutIcon from "@icons/LayoutIcon";
|
import LayoutIcon from "@icons/LayoutIcon";
|
||||||
import MenuItem from "./MenuItem";
|
import MenuItem from "./MenuItem";
|
||||||
|
import { quizSetupSteps } from "@model/quizSettings";
|
||||||
|
import { useQuizStore } from "@root/quizes/store";
|
||||||
|
import { setCurrentStep } from "@root/quizes/actions";
|
||||||
|
|
||||||
const createQuizMenuItems = [
|
const createQuizMenuItems = [
|
||||||
[LayoutIcon, "Стартовая страница"],
|
[LayoutIcon, "Стартовая страница"],
|
||||||
[QuestionIcon, "Вопросы"],
|
[QuestionIcon, "Вопросы"],
|
||||||
[ChartPieIcon, "Результаты"],
|
[ChartPieIcon, "Результаты"],
|
||||||
[QuestionsMapIcon, "Карта вопросов"],
|
[QuestionsMapIcon, "Карта вопросов"],
|
||||||
[ContactBookIcon, "Форма контактов"],
|
[ContactBookIcon, "Форма контактов"],
|
||||||
[FlowArrowIcon, "Установка квиза"],
|
[FlowArrowIcon, "Установка квиза"],
|
||||||
[MegaphoneIcon, "Запуск рекламы"],
|
[MegaphoneIcon, "Запуск рекламы"],
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const quizSettingsMenuItems = [
|
const quizSettingsMenuItems = [
|
||||||
[TagIcon, "Дополнения"],
|
[TagIcon, "Дополнения"],
|
||||||
[PencilCircleIcon, "Дизайн"],
|
[PencilCircleIcon, "Дизайн"],
|
||||||
[PuzzlePieceIcon, "Интеграции"],
|
[PuzzlePieceIcon, "Интеграции"],
|
||||||
[GearIcon, "Настройки"],
|
[GearIcon, "Настройки"],
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export default function Sidebar() {
|
export default function Sidebar() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
|
const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
|
||||||
const [progress, setProgress] = useState<number>(1 / 7);
|
const [progress, setProgress] = useState<number>(1 / 7);
|
||||||
const quizId = Number(useParams().quizId);
|
const currentStep = useQuizStore(state => state.currentStep);
|
||||||
const { listQuizes, updateQuizesList } = quizStore();
|
|
||||||
|
|
||||||
const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev);
|
const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev);
|
||||||
return (
|
|
||||||
<Box
|
return (
|
||||||
sx={{
|
<Box
|
||||||
backgroundColor: theme.palette.lightPurple.main,
|
|
||||||
minWidth: isMenuCollapsed ? "80px" : "230px",
|
|
||||||
width: isMenuCollapsed ? "80px" : "230px",
|
|
||||||
height: "calc(100vh - 80px)",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
py: "19px",
|
|
||||||
transitionProperty: "width, min-width",
|
|
||||||
transitionDuration: "200ms",
|
|
||||||
overflow: "hidden",
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
boxSizing: "border-box",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
pl: isMenuCollapsed ? undefined : "16px",
|
|
||||||
pr: isMenuCollapsed ? undefined : "8px",
|
|
||||||
mb: isMenuCollapsed ? "5px" : undefined,
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: isMenuCollapsed ? "center" : undefined,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!isMenuCollapsed && (
|
|
||||||
<Typography
|
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "14px",
|
backgroundColor: theme.palette.lightPurple.main,
|
||||||
lineHeight: "20px",
|
minWidth: isMenuCollapsed ? "80px" : "230px",
|
||||||
fontWeight: 500,
|
width: isMenuCollapsed ? "80px" : "230px",
|
||||||
color: theme.palette.grey2.main,
|
height: "calc(100vh - 80px)",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
py: "19px",
|
||||||
|
transitionProperty: "width, min-width",
|
||||||
|
transitionDuration: "200ms",
|
||||||
|
overflow: "hidden",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
boxSizing: "border-box",
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
Создание квиза
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
<IconButton
|
|
||||||
onClick={handleMenuCollapseToggle}
|
|
||||||
sx={{ ml: isMenuCollapsed ? undefined : "auto" }}
|
|
||||||
>
|
>
|
||||||
<CollapseMenuIcon
|
<Box
|
||||||
height="16px"
|
sx={{
|
||||||
width="16px"
|
display: "flex",
|
||||||
color={theme.palette.grey2.main}
|
pl: isMenuCollapsed ? undefined : "16px",
|
||||||
transform={isMenuCollapsed ? "rotate(180deg)" : ""}
|
pr: isMenuCollapsed ? undefined : "8px",
|
||||||
/>
|
mb: isMenuCollapsed ? "5px" : undefined,
|
||||||
</IconButton>
|
alignItems: "center",
|
||||||
</Box>
|
justifyContent: isMenuCollapsed ? "center" : undefined,
|
||||||
<List disablePadding>
|
}}
|
||||||
{createQuizMenuItems.map((menuItem, index) => {
|
>
|
||||||
const Icon = menuItem[0];
|
{!isMenuCollapsed && (
|
||||||
return (
|
<Typography
|
||||||
<MenuItem
|
sx={{
|
||||||
onClick={() => {
|
fontSize: "14px",
|
||||||
updateQuizesList(quizId, { step: index + 1 });
|
lineHeight: "20px",
|
||||||
}}
|
fontWeight: 500,
|
||||||
key={menuItem[1]}
|
color: theme.palette.grey2.main,
|
||||||
text={menuItem[1]}
|
}}
|
||||||
isCollapsed={isMenuCollapsed}
|
>
|
||||||
isActive={listQuizes[quizId].step === index + 1}
|
Создание квиза
|
||||||
icon={
|
</Typography>
|
||||||
<Icon
|
)}
|
||||||
color={
|
<IconButton
|
||||||
listQuizes[quizId].step === index + 1
|
onClick={handleMenuCollapseToggle}
|
||||||
? theme.palette.brightPurple.main
|
sx={{ ml: isMenuCollapsed ? undefined : "auto" }}
|
||||||
: isMenuCollapsed
|
>
|
||||||
? "white"
|
<CollapseMenuIcon
|
||||||
: theme.palette.grey2.main
|
height="16px"
|
||||||
}
|
width="16px"
|
||||||
height={isMenuCollapsed ? "35px" : "24px"}
|
color={theme.palette.grey2.main}
|
||||||
width={isMenuCollapsed ? "35px" : "24px"}
|
transform={isMenuCollapsed ? "rotate(180deg)" : ""}
|
||||||
/>
|
/>
|
||||||
}
|
</IconButton>
|
||||||
/>
|
</Box>
|
||||||
);
|
<List disablePadding>
|
||||||
})}
|
{createQuizMenuItems.map((menuItem, index) => {
|
||||||
</List>
|
const Icon = menuItem[0];
|
||||||
{!isMenuCollapsed && (
|
return (
|
||||||
<Typography
|
<MenuItem
|
||||||
sx={{
|
onClick={() => setCurrentStep(index + 1)}
|
||||||
px: "16px",
|
key={menuItem[1]}
|
||||||
mt: "16px",
|
text={menuItem[1]}
|
||||||
mb: "11px",
|
isCollapsed={isMenuCollapsed}
|
||||||
fontSize: "14px",
|
isActive={quizSetupSteps[currentStep].displayStep === index + 1}
|
||||||
lineHeight: "20px",
|
icon={
|
||||||
fontWeight: 500,
|
<Icon
|
||||||
color: theme.palette.grey2.main,
|
color={
|
||||||
}}
|
quizSetupSteps[currentStep].displayStep === index + 1
|
||||||
>
|
? theme.palette.brightPurple.main
|
||||||
Настройки квиза
|
: isMenuCollapsed
|
||||||
</Typography>
|
? "white"
|
||||||
)}
|
: theme.palette.grey2.main
|
||||||
<List disablePadding>
|
}
|
||||||
{quizSettingsMenuItems.map((menuItem, index) => {
|
height={isMenuCollapsed ? "35px" : "24px"}
|
||||||
const Icon = menuItem[0];
|
width={isMenuCollapsed ? "35px" : "24px"}
|
||||||
const totalIndex = index + createQuizMenuItems.length;
|
/>
|
||||||
const isActive = listQuizes[quizId].step === totalIndex + 1;
|
}
|
||||||
return (
|
/>
|
||||||
<MenuItem
|
);
|
||||||
onClick={() => updateQuizesList(quizId, { step: totalIndex + 1 })}
|
})}
|
||||||
key={menuItem[1]}
|
</List>
|
||||||
text={menuItem[1]}
|
{!isMenuCollapsed && (
|
||||||
isActive={isActive}
|
<Typography
|
||||||
isCollapsed={isMenuCollapsed}
|
sx={{
|
||||||
icon={
|
px: "16px",
|
||||||
<Icon
|
mt: "16px",
|
||||||
color={
|
mb: "11px",
|
||||||
isActive
|
fontSize: "14px",
|
||||||
? theme.palette.brightPurple.main
|
lineHeight: "20px",
|
||||||
: isMenuCollapsed
|
fontWeight: 500,
|
||||||
? "white"
|
color: theme.palette.grey2.main,
|
||||||
: theme.palette.grey2.main
|
}}
|
||||||
}
|
>
|
||||||
height={isMenuCollapsed ? "35px" : "24px"}
|
Настройки квиза
|
||||||
width={isMenuCollapsed ? "35px" : "24px"}
|
</Typography>
|
||||||
/>
|
)}
|
||||||
}
|
{/* <List disablePadding> // TODO
|
||||||
/>
|
{quizSettingsMenuItems.map((menuItem, index) => {
|
||||||
);
|
const Icon = menuItem[0];
|
||||||
})}
|
const totalIndex = index + createQuizMenuItems.length;
|
||||||
</List>
|
const isActive = listQuizes[quizId].step === totalIndex + 1;
|
||||||
</Box>
|
return (
|
||||||
);
|
<MenuItem
|
||||||
|
onClick={() => updateQuizesList(quizId, { step: totalIndex + 1 })}
|
||||||
|
key={menuItem[1]}
|
||||||
|
text={menuItem[1]}
|
||||||
|
isActive={isActive}
|
||||||
|
isCollapsed={isMenuCollapsed}
|
||||||
|
icon={
|
||||||
|
<Icon
|
||||||
|
color={
|
||||||
|
isActive
|
||||||
|
? theme.palette.brightPurple.main
|
||||||
|
: isMenuCollapsed
|
||||||
|
? "white"
|
||||||
|
: theme.palette.grey2.main
|
||||||
|
}
|
||||||
|
height={isMenuCollapsed ? "35px" : "24px"}
|
||||||
|
width={isMenuCollapsed ? "35px" : "24px"}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,13 @@
|
|||||||
import * as React from "react";
|
|
||||||
import MobileStepper from "@mui/material/MobileStepper";
|
|
||||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
|
import MobileStepper from "@mui/material/MobileStepper";
|
||||||
|
import { QuizSetupStep, maxDisplayQuizSetupSteps, quizSetupSteps } from "@model/quizSettings";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
desc?: string;
|
activeStep: QuizSetupStep;
|
||||||
activeStep?: number;
|
|
||||||
steps?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ProgressMobileStepper({
|
export default function ProgressMobileStepper({
|
||||||
desc,
|
activeStep,
|
||||||
activeStep = 1,
|
|
||||||
steps = 8,
|
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||||
@ -31,9 +28,9 @@ export default function ProgressMobileStepper({
|
|||||||
>
|
>
|
||||||
<MobileStepper
|
<MobileStepper
|
||||||
variant="progress"
|
variant="progress"
|
||||||
steps={steps}
|
steps={maxDisplayQuizSetupSteps}
|
||||||
position="static"
|
position="static"
|
||||||
activeStep={activeStep}
|
activeStep={activeStep - 1}
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
@ -55,9 +52,9 @@ export default function ProgressMobileStepper({
|
|||||||
sx={{ fontWeight: 400, fontSize: "12px", lineHeight: "14.22px" }}
|
sx={{ fontWeight: 400, fontSize: "12px", lineHeight: "14.22px" }}
|
||||||
>
|
>
|
||||||
{" "}
|
{" "}
|
||||||
Шаг {activeStep} из {steps - 1}
|
Шаг {quizSetupSteps[activeStep].displayStep} из {maxDisplayQuizSetupSteps}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>{desc}</Typography>
|
<Typography>{quizSetupSteps[activeStep].text}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,49 +1,37 @@
|
|||||||
import * as React from "react";
|
import { QuizSetupStep } from "@model/quizSettings";
|
||||||
import StepOne from "../pages/startPage/stepOne";
|
import { notReachable } from "../utils/notReachable";
|
||||||
import Steptwo from "../pages/startPage/steptwo";
|
|
||||||
import StartPageSettings from "../pages/startPage/StartPageSettings";
|
|
||||||
import QuestionsPage from "../pages/Questions/QuestionsPage";
|
|
||||||
import FormQuestionsPage from "../pages/Questions/Form/FormQuestionsPage";
|
|
||||||
import ContactFormPage from "../pages/ContactFormPage/ContactFormPage";
|
import ContactFormPage from "../pages/ContactFormPage/ContactFormPage";
|
||||||
import InstallQuiz from "../pages/InstallQuiz/InstallQuiz";
|
import InstallQuiz from "../pages/InstallQuiz/InstallQuiz";
|
||||||
|
import FormQuestionsPage from "../pages/Questions/Form/FormQuestionsPage";
|
||||||
|
import QuestionsPage from "../pages/Questions/QuestionsPage";
|
||||||
|
import { QuestionsMap } from "../pages/QuestionsMap";
|
||||||
import { Result } from "../pages/Result/Result";
|
import { Result } from "../pages/Result/Result";
|
||||||
import { Setting } from "../pages/Result/Setting";
|
import { Setting } from "../pages/Result/Setting";
|
||||||
import { QuestionsMap } from "../pages/QuestionsMap";
|
import StartPageSettings from "../pages/startPage/StartPageSettings";
|
||||||
|
import StepOne from "../pages/startPage/stepOne";
|
||||||
|
import Steptwo from "../pages/startPage/steptwo";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
activeStep: number;
|
activeStep: QuizSetupStep;
|
||||||
quizType: string;
|
quizType: string;
|
||||||
startpage: string;
|
|
||||||
createResult: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SwitchStepPages({
|
export default function SwitchStepPages({
|
||||||
activeStep = 1,
|
activeStep = 1,
|
||||||
quizType,
|
quizType,
|
||||||
startpage,
|
|
||||||
createResult,
|
|
||||||
}: Props) {
|
}: Props) {
|
||||||
switch (activeStep) {
|
switch (activeStep) {
|
||||||
case 1:
|
case 1: return <StepOne />;
|
||||||
if (!quizType) return <StepOne />;
|
case 2: return <Steptwo />;
|
||||||
if (!startpage) return <Steptwo />;
|
case 3: return <StartPageSettings />;
|
||||||
return <StartPageSettings />;
|
case 4: return quizType === "form" ? <QuestionsPage /> : <FormQuestionsPage />;
|
||||||
case 2:
|
case 5: return <Result />;
|
||||||
if (quizType === "form") return <FormQuestionsPage />;
|
case 6: return <Setting />;
|
||||||
return <QuestionsPage />;
|
case 7: return <QuestionsMap />;
|
||||||
case 3:
|
case 8: return <ContactFormPage />;
|
||||||
if (!createResult) return <Result />;
|
case 9: return <InstallQuiz />;
|
||||||
return <Setting />;
|
case 10: return <>Реклама</>;
|
||||||
case 4:
|
default: return notReachable(activeStep);
|
||||||
return <QuestionsMap />;
|
}
|
||||||
case 5:
|
|
||||||
return <ContactFormPage />;
|
|
||||||
case 6:
|
|
||||||
return <InstallQuiz />;
|
|
||||||
case 7:
|
|
||||||
return <>Реклама</>;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/utils/notReachable.ts
Normal file
3
src/utils/notReachable.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function notReachable(_: never): never {
|
||||||
|
throw new Error(`Shouldn't reach here: ${_}`);
|
||||||
|
}
|
||||||
@ -13,6 +13,9 @@
|
|||||||
],
|
],
|
||||||
"@api/*": [
|
"@api/*": [
|
||||||
"./api/*"
|
"./api/*"
|
||||||
|
],
|
||||||
|
"@model/*": [
|
||||||
|
"./model/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user