diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 1684a63..491be2b 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,40 +1,30 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:react-hooks/recommended", - ], - ignorePatterns: ["dist", ".eslintrc.cjs"], - parser: "@typescript-eslint/parser", - plugins: ["react-refresh"], - rules: { - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, - ], - "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-empty-interface": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-inferrable-types": "off", - "@typescript-eslint/no-misused-promises": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unused-vars": [ - "warn", - { "vars": "all", "args": "none" } - ], - "@typescript-eslint/restrict-template-expressions": "off", - "no-debugger": "off", - "no-empty-function": "off", - "no-empty-pattern": "off", - "no-empty": "off", - "prefer-const": "warn", - }, -}; +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended"], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-misused-promises": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unused-vars": ["warn", { vars: "all", args: "none" }], + "@typescript-eslint/restrict-template-expressions": "off", + "no-debugger": "off", + "no-empty-function": "off", + "no-empty-pattern": "off", + "no-empty": "off", + "prefer-const": "warn", + }, +}; diff --git a/README.md b/README.md index 1d63886..f65f0e0 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,68 @@ ## Правила + - Запрещено использовать vh/vw css-юниты и их производные + ## Виджет + ### Сборка + ```bash yarn build:widget ``` + ### Использование + ```html ``` + ## Npm-пакет + ### Перед использованием и публикацией + ```bash npm config set //penahub.gitlab.yandexcloud.net/api/v4/packages/npm/:_authToken=INSTANCE_TOKEN npm config set //penahub.gitlab.yandexcloud.net/api/v4/projects/43/packages/npm/:_authToken=PROJECT_TOKEN ``` + ### Публикация + 1. Инкрементировать версию в package.json -2. +2. + ```bash yarn publish ``` + 3. Нажать enter при запросе версии + ### Установка + Добавить в корень проекта файл .yarnrc с содержимым + ``` "@frontend:registry" "https://penahub.gitlab.yandexcloud.net/api/v4/packages/npm/" ``` + ```bash yarn add @frontend/squzanswerer ``` + Peer dependencies: + ```bash yarn add @emoji-mart/data @emoji-mart/react @emotion/react @emotion/styled @mui/icons-material @mui/material @mui/x-date-pickers axios emoji-mart immer moment nanoid notistack react-dom react-error-boundary react-router-dom react swr use-debounce zustand ``` + ### Использование + ```ts import { QuizView } from "@frontend/squzanswerer"; diff --git a/cypress.config.ts b/cypress.config.ts index 87067ad..a5f6f74 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,10 +1,10 @@ -import { defineConfig } from "cypress"; - -export default defineConfig({ - e2e: { - baseUrl: 'http://localhost:3000', - viewportWidth: 1440, - viewportHeight: 900, - supportFile: false, - }, -}); +import { defineConfig } from "cypress"; + +export default defineConfig({ + e2e: { + baseUrl: "http://localhost:3000", + viewportWidth: 1440, + viewportHeight: 900, + supportFile: false, + }, +}); diff --git a/deployments/main/docker-compose.yaml b/deployments/main/docker-compose.yaml index 8dacb5e..a93a068 100644 --- a/deployments/main/docker-compose.yaml +++ b/deployments/main/docker-compose.yaml @@ -1,8 +1,7 @@ -services: - respondent: - container_name: respondent - restart: unless-stopped - image: $CI_REGISTRY_IMAGE/main:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID - hostname: respondent - tty: true - +services: + respondent: + container_name: respondent + restart: unless-stopped + image: $CI_REGISTRY_IMAGE/main:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID + hostname: respondent + tty: true diff --git a/deployments/staging/docker-compose.yaml b/deployments/staging/docker-compose.yaml index d258d2e..1b71007 100644 --- a/deployments/staging/docker-compose.yaml +++ b/deployments/staging/docker-compose.yaml @@ -1,8 +1,7 @@ -services: - respondent: - container_name: respondent - restart: unless-stopped - image: $CI_REGISTRY_IMAGE/staging:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID - hostname: respondent - tty: true - +services: + respondent: + container_name: respondent + restart: unless-stopped + image: $CI_REGISTRY_IMAGE/staging:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID + hostname: respondent + tty: true diff --git a/lib/api/quizRelase.ts b/lib/api/quizRelase.ts index 77b1e07..eab13fe 100644 --- a/lib/api/quizRelase.ts +++ b/lib/api/quizRelase.ts @@ -1,6 +1,6 @@ import { GetQuizDataResponse, parseQuizData } from "@model/api/getQuizData"; import axios from "axios"; -import MobileDetect from 'mobile-detect'; +import MobileDetect from "mobile-detect"; import device from "current-device"; import type { AxiosError } from "axios"; @@ -10,226 +10,240 @@ import * as Bowser from "bowser"; import { domain } from "../utils/defineDomain"; let SESSIONS = ""; - const md = new MobileDetect(window.navigator.userAgent); const userAgent = navigator.userAgent; //операционная система let OSDevice: string | undefined; -if (userAgent.toLowerCase().includes("linux")) { OSDevice = "Linux"; } -if (userAgent.toLowerCase().includes("windows")) { OSDevice = "Windows"; } -if (/iPad|iPhone|iPod/.test(userAgent)) { OSDevice = "IOS"; } -if (userAgent.toLowerCase().includes("macintosh")) { OSDevice = "Mac OS"; } -if (OSDevice === undefined) { OSDevice = userAgent; } +if (userAgent.toLowerCase().includes("linux")) { + OSDevice = "Linux"; +} +if (userAgent.toLowerCase().includes("windows")) { + OSDevice = "Windows"; +} +if (/iPad|iPhone|iPod/.test(userAgent)) { + OSDevice = "IOS"; +} +if (userAgent.toLowerCase().includes("macintosh")) { + OSDevice = "Mac OS"; +} +if (OSDevice === undefined) { + OSDevice = userAgent; +} //браузер let browserUser: string; if (Bowser.name === "Chrome") { - browserUser = "Chrome"; + browserUser = "Chrome"; } else if (Bowser.name === "Firefox") { - browserUser = "Firefox"; + browserUser = "Firefox"; } else if (Bowser.name === "Safari") { - browserUser = "Safari"; + browserUser = "Safari"; } else if (Bowser.name === "Yandex Browser") { - browserUser = "Yandex Browser"; -} else { browserUser = userAgent; } + browserUser = "Yandex Browser"; +} else { + browserUser = userAgent; +} const DeviceType = device.type; let Device = md.mobile(); -if (Device === null) { Device = userAgent; } +if (Device === null) { + Device = userAgent; +} type PublicationMakeRequestParams = { - url: string; - body: FormData; - method: "POST"; + url: string; + body: FormData; + method: "POST"; }; export const publicationMakeRequest = ({ url, body }: PublicationMakeRequestParams) => { - return axios(url, { - data: body, - headers: { - "X-Sessionkey": SESSIONS, - "Content-Type": "multipart/form-data", - "DeviceType": DeviceType, - "Device": Device, - "OS": OSDevice, - "Browser": browserUser - }, - method: "POST", - }); + return axios(url, { + data: body, + headers: { + "X-Sessionkey": SESSIONS, + "Content-Type": "multipart/form-data", + DeviceType: DeviceType, + Device: Device, + OS: OSDevice, + Browser: browserUser, + }, + method: "POST", + }); }; export async function getData(quizId: string): Promise<{ - data: GetQuizDataResponse | null; - isRecentlyCompleted: boolean; - error?: AxiosError; + data: GetQuizDataResponse | null; + isRecentlyCompleted: boolean; + error?: AxiosError; }> { - try { - const { data, headers } = await axios( - domain + `/answer/settings`, - { - method: "POST", - headers: { - "X-Sessionkey": SESSIONS, - "Content-Type": "application/json", - "DeviceType": DeviceType, - "Device": Device, - "OS": OSDevice, - "Browser": userAgent - }, - data: { - quiz_id: quizId, - limit: 100, - page: 0, - need_config: true, - }, - } - ); - const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); + try { + const { data, headers } = await axios(domain + `/answer/settings`, { + method: "POST", + headers: { + "X-Sessionkey": SESSIONS, + "Content-Type": "application/json", + DeviceType: DeviceType, + Device: Device, + OS: OSDevice, + Browser: userAgent, + }, + data: { + quiz_id: quizId, + limit: 100, + page: 0, + need_config: true, + }, + }); + const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); - if (typeof sessions[quizId] === "number") { - // unix время. Если меньше суток прошло - выводить ошибку, иначе пустить дальше - if (Date.now() - sessions[quizId] < 86400000) { - return { data, isRecentlyCompleted: true }; - } - } - - SESSIONS = headers["x-sessionkey"] ? headers["x-sessionkey"] : SESSIONS; - - return { data, isRecentlyCompleted: false }; - } catch (nativeError) { - const error = nativeError as AxiosError; - - return { data: null, isRecentlyCompleted: false, error: error }; + if (typeof sessions[quizId] === "number") { + // unix время. Если меньше суток прошло - выводить ошибку, иначе пустить дальше + if (Date.now() - sessions[quizId] < 86400000) { + return { data, isRecentlyCompleted: true }; + } } + + SESSIONS = headers["x-sessionkey"] ? headers["x-sessionkey"] : SESSIONS; + + return { data, isRecentlyCompleted: false }; + } catch (nativeError) { + const error = nativeError as AxiosError; + + return { data: null, isRecentlyCompleted: false, error: error }; + } } export async function getQuizData(quizId: string) { - if (!quizId) throw new Error("No quiz id"); + if (!quizId) throw new Error("No quiz id"); - const response = await getData(quizId); - const quizDataResponse = response.data; + const response = await getData(quizId); + const quizDataResponse = response.data; - if (response.error) { - throw response.error; - } - if (!quizDataResponse) { - throw new Error("Quiz not found"); - } + if (response.error) { + throw response.error; + } + if (!quizDataResponse) { + throw new Error("Quiz not found"); + } - const quizSettings = replaceSpacesToEmptyLines(parseQuizData(quizDataResponse)); + const quizSettings = replaceSpacesToEmptyLines(parseQuizData(quizDataResponse)); - const res = JSON.parse(JSON.stringify({ data: quizSettings }).replaceAll(/\\" \\"/g, '""').replaceAll(/" "/g, '""')).data as QuizSettings; - res.recentlyCompleted = response.isRecentlyCompleted; - return res; + const res = JSON.parse( + JSON.stringify({ data: quizSettings }) + .replaceAll(/\\" \\"/g, '""') + .replaceAll(/" "/g, '""') + ).data as QuizSettings; + res.recentlyCompleted = response.isRecentlyCompleted; + return res; } type SendAnswerProps = { - questionId: string; - body: string | string[]; - qid: string; - preview: boolean; + questionId: string; + body: string | string[]; + qid: string; + preview: boolean; }; export function sendAnswer({ questionId, body, qid, preview }: SendAnswerProps) { - if (preview) return; - const formData = new FormData(); + if (preview) return; + const formData = new FormData(); - const answers = [ - { - question_id: questionId, - content: body, //тут массив с ответом - }, - ]; - formData.append("answers", JSON.stringify(answers)); - console.log("QID", qid); - formData.append("qid", qid); + const answers = [ + { + question_id: questionId, + content: body, //тут массив с ответом + }, + ]; + formData.append("answers", JSON.stringify(answers)); + console.log("QID", qid); + formData.append("qid", qid); - return publicationMakeRequest({ - url: domain + `/answer/answer`, - body: formData, - method: "POST", - }); + return publicationMakeRequest({ + url: domain + `/answer/answer`, + body: formData, + method: "POST", + }); } //body ={file, filename} type SendFileParams = { - questionId: string; - body: { - name: string; - file: File; - preview: boolean; - }; - qid: string; + questionId: string; + body: { + name: string; + file: File; + preview: boolean; + }; + qid: string; }; type Answer = { - question_id: string; - content: string; + question_id: string; + content: string; }; export function sendFile({ questionId, body, qid }: SendFileParams) { - if (body.preview) return; - const formData = new FormData(); + if (body.preview) return; + const formData = new FormData(); - const answers: Answer[] = [ - { - question_id: questionId, - content: "file:" + body.name, - }, - ]; + const answers: Answer[] = [ + { + question_id: questionId, + content: "file:" + body.name, + }, + ]; - formData.append("answers", JSON.stringify(answers)); - formData.append(body.name, body.file); - console.log("QID", qid); - formData.append("qid", qid); + formData.append("answers", JSON.stringify(answers)); + formData.append(body.name, body.file); + console.log("QID", qid); + formData.append("qid", qid); - return publicationMakeRequest({ - url: domain + `/answer/answer`, - body: formData, - method: "POST", - }); + return publicationMakeRequest({ + url: domain + `/answer/answer`, + body: formData, + method: "POST", + }); } //форма контактов export type SendFCParams = { - questionId: string; - body: { - name?: string; - email?: string; - phone?: string; - address?: string; - customs?: Record; - }; - qid: string; - preview: boolean; + questionId: string; + body: { + name?: string; + email?: string; + phone?: string; + address?: string; + customs?: Record; + }; + qid: string; + preview: boolean; }; export function sendFC({ questionId, body, qid, preview }: SendFCParams) { - if (preview) return; - const formData = new FormData(); + if (preview) return; + const formData = new FormData(); - // const keysBody = Object.keys(body) - // const content:any = {} - // fields.forEach((key) => { - // if (keysBody.includes(key)) content[key] = body.key - // }) + // const keysBody = Object.keys(body) + // const content:any = {} + // fields.forEach((key) => { + // if (keysBody.includes(key)) content[key] = body.key + // }) - const answers = [ - { - question_id: questionId, - content: JSON.stringify(body), - result: true, - qid, - }, - ]; + const answers = [ + { + question_id: questionId, + content: JSON.stringify(body), + result: true, + qid, + }, + ]; - formData.append("answers", JSON.stringify(answers)); - formData.append("qid", qid); + formData.append("answers", JSON.stringify(answers)); + formData.append("qid", qid); - return publicationMakeRequest({ - url: domain + `/answer/answer`, - body: formData, - method: "POST", - }); + return publicationMakeRequest({ + url: domain + `/answer/answer`, + body: formData, + method: "POST", + }); } diff --git a/lib/assets/icons/ArrowDownIcon.tsx b/lib/assets/icons/ArrowDownIcon.tsx index c62184b..2d6f602 100644 --- a/lib/assets/icons/ArrowDownIcon.tsx +++ b/lib/assets/icons/ArrowDownIcon.tsx @@ -1,29 +1,32 @@ -import {Box, SxProps, Theme, useTheme} from "@mui/material"; +import { Box, SxProps, Theme, useTheme } from "@mui/material"; -interface Color{ - color?: string +interface Color { + color?: string; } -export default function ArrowDownIcon( - props: any, - {color = "#7E2AEA"}: Color -) { - const theme = useTheme(); +export default function ArrowDownIcon(props: any, { color = "#7E2AEA" }: Color) { + const theme = useTheme(); - return ( - - - - - - ); -} \ No newline at end of file + return ( + + + + + + ); +} diff --git a/lib/assets/icons/BlankImage.tsx b/lib/assets/icons/BlankImage.tsx index 6e976bc..93f2630 100644 --- a/lib/assets/icons/BlankImage.tsx +++ b/lib/assets/icons/BlankImage.tsx @@ -1,10 +1,26 @@ export default function BlankImage() { - - return ( - - - - - - ); -} + return ( + + + + + + ); +} diff --git a/lib/assets/icons/CalendarIcon.tsx b/lib/assets/icons/CalendarIcon.tsx index 1e1a930..2a127cd 100644 --- a/lib/assets/icons/CalendarIcon.tsx +++ b/lib/assets/icons/CalendarIcon.tsx @@ -1,6 +1,6 @@ -import { Box, SxProps, Theme } from "@mui/material"; +import { Box, SxProps, Theme } from "@mui/material"; interface Props { - sx?: SxProps; + sx?: SxProps; } export default function CalendarIcon({ sx }: Props) { return ( @@ -22,7 +22,7 @@ export default function CalendarIcon({ sx }: Props) { "&:active rect": { stroke: "#FB5607", }, - ...sx + ...sx, }} > diff --git a/lib/assets/icons/Checkbox.tsx b/lib/assets/icons/Checkbox.tsx index 52f5a8a..d95ca7a 100644 --- a/lib/assets/icons/Checkbox.tsx +++ b/lib/assets/icons/Checkbox.tsx @@ -5,7 +5,7 @@ type CheckboxIconProps = { color?: string; }; -export const CheckboxIcon = ({ checked = false, color = "#7E2AEA", }: CheckboxIconProps) => { +export const CheckboxIcon = ({ checked = false, color = "#7E2AEA" }: CheckboxIconProps) => { const theme = useTheme(); return ( @@ -17,26 +17,13 @@ export const CheckboxIcon = ({ checked = false, color = "#7E2AEA", }: CheckboxIc display: "flex", justifyContent: "center", alignItems: "center", - backgroundColor: checked - ? color - : "#F2F3F7", + backgroundColor: checked ? color : "#F2F3F7", border: `1px solid #9A9AAF`, }} > {checked && ( - - + + )} diff --git a/lib/assets/icons/CloseBold.tsx b/lib/assets/icons/CloseBold.tsx index 84942f7..e58a885 100644 --- a/lib/assets/icons/CloseBold.tsx +++ b/lib/assets/icons/CloseBold.tsx @@ -8,13 +8,7 @@ export default function CloseBold({ width }: Props) { const theme = useTheme(); return ( - + @@ -55,30 +49,13 @@ export default function CloseBold({ width }: Props) { values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" /> - + - - - + + + diff --git a/lib/assets/icons/ContactFormIcon/AddressIcon.tsx b/lib/assets/icons/ContactFormIcon/AddressIcon.tsx index cd9abf0..fa22355 100644 --- a/lib/assets/icons/ContactFormIcon/AddressIcon.tsx +++ b/lib/assets/icons/ContactFormIcon/AddressIcon.tsx @@ -2,7 +2,7 @@ import { Box } from "@mui/material"; interface Props { color: string; - backgroundColor: string + backgroundColor: string; } export default function AddressIcon({ color, backgroundColor }: Props) { @@ -14,18 +14,12 @@ export default function AddressIcon({ color, backgroundColor }: Props) { justifyContent: "center", height: "58px", width: "45px", - backgroundColor: {backgroundColor}, + backgroundColor: { backgroundColor }, borderBottomLeftRadius: "12px", borderTopLeftRadius: "12px", }} > - + - - - - - + + + + ); } diff --git a/lib/assets/icons/ContactFormIcon/NameIcon.tsx b/lib/assets/icons/ContactFormIcon/NameIcon.tsx index 8aa0dbe..ae04cab 100644 --- a/lib/assets/icons/ContactFormIcon/NameIcon.tsx +++ b/lib/assets/icons/ContactFormIcon/NameIcon.tsx @@ -2,7 +2,7 @@ import { Box } from "@mui/material"; interface Props { color: string; - backgroundColor: string + backgroundColor: string; } export default function NameIcon({ color, backgroundColor }: Props) { @@ -14,15 +14,26 @@ export default function NameIcon({ color, backgroundColor }: Props) { justifyContent: "center", height: "58px", width: "45px", - backgroundColor: {backgroundColor}, + backgroundColor: { backgroundColor }, borderBottomLeftRadius: "12px", borderTopLeftRadius: "12px", }} > - - - - + + + + ); } diff --git a/lib/assets/icons/ContactFormIcon/PhoneIcon.tsx b/lib/assets/icons/ContactFormIcon/PhoneIcon.tsx index 394bb4f..89982b3 100644 --- a/lib/assets/icons/ContactFormIcon/PhoneIcon.tsx +++ b/lib/assets/icons/ContactFormIcon/PhoneIcon.tsx @@ -2,7 +2,7 @@ import { Box } from "@mui/material"; interface Props { color: string; - backgroundColor: string + backgroundColor: string; } export default function PhoneIcon({ color, backgroundColor }: Props) { @@ -14,14 +14,18 @@ export default function PhoneIcon({ color, backgroundColor }: Props) { justifyContent: "center", height: "58px", width: "45px", - backgroundColor: {backgroundColor}, + backgroundColor: { backgroundColor }, borderBottomLeftRadius: "12px", borderTopLeftRadius: "12px", }} > - - - + + + ); } diff --git a/lib/assets/icons/ContactFormIcon/TextIcon.tsx b/lib/assets/icons/ContactFormIcon/TextIcon.tsx index f9c644f..73527e4 100644 --- a/lib/assets/icons/ContactFormIcon/TextIcon.tsx +++ b/lib/assets/icons/ContactFormIcon/TextIcon.tsx @@ -2,7 +2,7 @@ import { Box } from "@mui/material"; interface Props { color: string; - backgroundColor: string + backgroundColor: string; } export default function TextIcon({ color, backgroundColor }: Props) { @@ -14,24 +14,13 @@ export default function TextIcon({ color, backgroundColor }: Props) { justifyContent: "center", height: "58px", width: "45px", - backgroundColor: {backgroundColor}, + backgroundColor: { backgroundColor }, borderBottomLeftRadius: "12px", borderTopLeftRadius: "12px", }} > - - + + - - - - + + + + ); diff --git a/lib/assets/icons/Info.tsx b/lib/assets/icons/Info.tsx index f319760..e3611fe 100644 --- a/lib/assets/icons/Info.tsx +++ b/lib/assets/icons/Info.tsx @@ -6,23 +6,13 @@ type InfoProps = { sx?: SxProps; onClick?: () => void; className?: string; - color?: string + color?: string; }; export default function Info({ width = 20, height = 20, sx, onClick, className, color = "#7e2aea" }: InfoProps) { return ( - - + + > = (props) => ( - + - + - - - - - + + + + + diff --git a/lib/assets/icons/NameplateLogoFQDark.tsx b/lib/assets/icons/NameplateLogoFQDark.tsx index 9916f39..a893b47 100644 --- a/lib/assets/icons/NameplateLogoFQDark.tsx +++ b/lib/assets/icons/NameplateLogoFQDark.tsx @@ -1,24 +1,46 @@ import { FC, SVGProps } from "react"; export const NameplateLogoFQDark: FC> = (props) => ( - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + ); diff --git a/lib/assets/icons/UploadIcon.tsx b/lib/assets/icons/UploadIcon.tsx index 573fcbf..8c6dc39 100644 --- a/lib/assets/icons/UploadIcon.tsx +++ b/lib/assets/icons/UploadIcon.tsx @@ -1,27 +1,39 @@ import { Box, useTheme } from "@mui/material"; -interface Props{ - color?: string +interface Props { + color?: string; } -export default function UploadIcon({color= "#9A9AAF"}: Props) { - const theme = useTheme(); +export default function UploadIcon({ color = "#9A9AAF" }: Props) { + const theme = useTheme(); - return ( - - - - - - - - ); -} \ No newline at end of file + return ( + + + + + + + + ); +} diff --git a/lib/assets/icons/questionsPage/FlagIcon.tsx b/lib/assets/icons/questionsPage/FlagIcon.tsx index 9eebf28..35c5265 100644 --- a/lib/assets/icons/questionsPage/FlagIcon.tsx +++ b/lib/assets/icons/questionsPage/FlagIcon.tsx @@ -16,24 +16,9 @@ export default function FlagIcon({ color, width = 30 }: Props) { justifyContent: "center", }} > - - - + + + - + - + - - + + - + - + - - + + ( - () => window.innerWidth - ); + const [rootContainerWidth, setRootContainerWidth] = useState(() => window.innerWidth); const rootContainerRef = useRef(null); const { data, error, isLoading } = useSWR( quizSettings ? null : ["quizData", quizId], @@ -73,12 +59,8 @@ function QuizAnswererInner({ refreshInterval: 0, } ); - const vkMetrics = useVkMetricsGoals( - quizSettings?.settings.cfg.vkMetricsNumber - ); - const yandexMetrics = useYandexMetricsGoals( - quizSettings?.settings.cfg.yandexMetricsNumber - ); + const vkMetrics = useVkMetricsGoals(quizSettings?.settings.cfg.vkMetricsNumber); + const yandexMetrics = useYandexMetricsGoals(quizSettings?.settings.cfg.yandexMetricsNumber); useEffect(() => { setTimeout(() => { @@ -88,15 +70,13 @@ function QuizAnswererInner({ }, []); useLayoutEffect(() => { - if (rootContainerRef.current) - setRootContainerWidth(rootContainerRef.current.clientWidth); + if (rootContainerRef.current) setRootContainerWidth(rootContainerRef.current.clientWidth); }, []); useEffect(() => { const handleWindowResize = () => { startTransition(() => { - if (rootContainerRef.current) - setRootContainerWidth(rootContainerRef.current.clientWidth); + if (rootContainerRef.current) setRootContainerWidth(rootContainerRef.current.clientWidth); }); }; window.addEventListener("resize", handleWindowResize); @@ -112,8 +92,7 @@ function QuizAnswererInner({ quizSettings ??= data; if (!quizSettings) throw new Error("Quiz data is null"); - if (quizSettings.questions.length === 0) - return ; + if (quizSettings.questions.length === 0) return ; if (!quizId) return ; const quizContainer = ( @@ -126,10 +105,7 @@ function QuizAnswererInner({ position: "relative", }} > - + @@ -138,9 +114,7 @@ function QuizAnswererInner({ return ( - + {disableGlobalCss ? ( + - + diff --git a/lib/components/ViewPublicationPage/ApologyPage.tsx b/lib/components/ViewPublicationPage/ApologyPage.tsx index f869827..a7c3e6b 100644 --- a/lib/components/ViewPublicationPage/ApologyPage.tsx +++ b/lib/components/ViewPublicationPage/ApologyPage.tsx @@ -4,30 +4,32 @@ import { FallbackProps } from "react-error-boundary"; type Props = Partial; export const ApologyPage = ({ error }: Props) => { - let message = "Что-то пошло не так"; + let message = "Что-то пошло не так"; - if (error.response?.data === "quiz is inactive") message = "Квиз не активирован"; - if (error.message === "No questions found") message = "Нет созданных вопросов"; - if (error.message === "Quiz already completed") message = "Вы уже прошли этот опрос"; - if (error.message === "No quiz id") message = "Отсутствует id квиза"; - if (error.response?.data === "Invalid request data") message = "Такого квиза не существует"; + if (error.response?.data === "quiz is inactive") message = "Квиз не активирован"; + if (error.message === "No questions found") message = "Нет созданных вопросов"; + if (error.message === "Quiz already completed") message = "Вы уже прошли этот опрос"; + if (error.message === "No quiz id") message = "Отсутствует id квиза"; + if (error.response?.data === "Invalid request data") message = "Такого квиза не существует"; - return ( - - {message} - - ); + return ( + + + {message} + + + ); }; diff --git a/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx b/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx index 990b34c..12f8a67 100644 --- a/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx @@ -21,10 +21,7 @@ import { DESIGN_LIST } from "@utils/designList"; import { NameplateLogo } from "@icons/NameplateLogo"; -import type { - FormContactFieldData, - FormContactFieldName, -} from "@model/settingsData"; +import type { FormContactFieldData, FormContactFieldName } from "@model/settingsData"; import type { QuizQuestionResult } from "@model/questionTypes/result"; import type { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; @@ -69,18 +66,12 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { currentQuestion.type === "result" ? currentQuestion : questions.find((question): question is QuizQuestionResult => { - if (settings?.cfg.haveRoot) { - return ( - question.type === "result" && - question.content.rule.parentId === currentQuestion.content.id - ); - } else { - return ( - question.type === "result" && - question.content.rule.parentId === "line" - ); - } - }); + if (settings?.cfg.haveRoot) { + return question.type === "result" && question.content.rule.parentId === currentQuestion.content.id; + } else { + return question.type === "result" && question.content.rule.parentId === "line"; + } + }); if (!resultQuestion) throw new Error("Result question not found"); @@ -103,10 +94,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { }); const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); - localStorage.setItem( - "sessions", - JSON.stringify({ ...sessions, [quizId]: new Date().getTime() }) - ); + localStorage.setItem("sessions", JSON.stringify({ ...sessions, [quizId]: new Date().getTime() })); } catch (e) { enqueueSnackbar("ответ не был засчитан"); } @@ -116,9 +104,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { const FCcopy: Record = settings.cfg.formContact.fields || settings.cfg.formContact; - const filteredFC: Partial< - Record - > = {}; + const filteredFC: Partial> = {}; for (const i in FCcopy) { const field = FCcopy[i as keyof typeof FCcopy]; if (field.used) { @@ -133,13 +119,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { } if (fireOnce.current) { - if ( - name.length === 0 && - email.length === 0 && - phone.length === 0 && - text.length === 0 && - adress.length === 0 - ) + if (name.length === 0 && email.length === 0 && phone.length === 0 && text.length === 0 && adress.length === 0) return enqueueSnackbar("Пожалуйста, заполните поля"); //почта валидна, хоть одно поле заполнено @@ -189,172 +169,168 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { yandexMetrics.contactsFormOpened(); }, []); - return ( + return ( + 500 ? "100%" : "auto", + overflow: "auto", + "&::-webkit-scrollbar": { + width: "0", + display: "none", + msOverflowStyle: "none", + }, + scrollbarWidth: "none", + msOverflowStyle: "none", + backgroundPosition: "center", + backgroundSize: "cover", + backgroundImage: + settings.cfg.design && !isMobile + ? quizThemes[settings.cfg.theme].isLight + ? `url(${DESIGN_LIST[settings.cfg.theme]})` + : `linear-gradient(90deg, rgba(39, 38, 38, 0.95) 7.66%, rgba(42, 42, 46, 0.85) 42.12%, rgba(51, 54, 71, 0.4) 100%), url(${ + DESIGN_LIST[settings.cfg.theme] + })` + : null, + }} + > + + + + + + + + { + setReady(target.checked); + }} + checked={ready} + colorIcon={theme.palette.primary.main} + sx={{ marginRight: "0" }} + /> + + С  + + Положением об обработке персональных данных{" "} + +  и  + + {" "} + Политикой конфиденциальности{" "} + +  ознакомлен + + + + + + {show_badge && ( + 500 ? "100%" : "auto", - overflow: "auto", - "&::-webkit-scrollbar": { - width: "0", - display: "none", - msOverflowStyle: "none", - }, - scrollbarWidth: "none", - msOverflowStyle: "none", - backgroundPosition: "center", - backgroundSize: "cover", - backgroundImage: - settings.cfg.design && !isMobile - ? quizThemes[settings.cfg.theme].isLight - ? `url(${DESIGN_LIST[settings.cfg.theme]})` - : `linear-gradient(90deg, rgba(39, 38, 38, 0.95) 7.66%, rgba(42, 42, 46, 0.85) 42.12%, rgba(51, 54, 71, 0.4) 100%), url(${DESIGN_LIST[settings.cfg.theme] - })` - : null, - }} - > - - - - - - - - - { - setReady(target.checked); - }} - checked={ready} - colorIcon={theme.palette.primary.main} - sx={{marginRight: "0"}} - /> - - С  - - Положением об обработке персональных - данных{" "} - -  и  - - {" "} - Политикой конфиденциальности{" "} - -  ознакомлен - - - - - - {show_badge && ( - - - - )} - + + )} - ); + + + ); }; diff --git a/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx b/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx index e76fda9..f250b1a 100644 --- a/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx @@ -1,66 +1,65 @@ -import {Box, Typography, useTheme} from "@mui/material"; -import {useRootContainerSize} from "@contexts/RootContainerWidthContext.ts"; -import {QuizSettingsConfig} from "@model/settingsData.ts"; -import {FC} from "react"; +import { Box, Typography, useTheme } from "@mui/material"; +import { useRootContainerSize } from "@contexts/RootContainerWidthContext.ts"; +import { QuizSettingsConfig } from "@model/settingsData.ts"; +import { FC } from "react"; type ContactTextBlockProps = { - settings: QuizSettingsConfig; -} + settings: QuizSettingsConfig; +}; -export const ContactTextBlock: FC = ({settings}) => { - const theme = useTheme(); - const isMobile = useRootContainerSize() < 850; - const isTablet = useRootContainerSize() < 1000; - return ( - = ({ settings }) => { + const theme = useTheme(); + const isMobile = useRootContainerSize() < 850; + const isTablet = useRootContainerSize() < 1000; + return ( + + + - - - {settings.cfg.formContact.title || - "Заполните форму, чтобы получить результаты теста"} - - {settings.cfg.formContact.desc && ( - - {settings.cfg.formContact.desc} - - )} - - - ) -} + {settings.cfg.formContact.title || "Заполните форму, чтобы получить результаты теста"} + + {settings.cfg.formContact.desc && ( + + {settings.cfg.formContact.desc} + + )} + + + ); +}; diff --git a/lib/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx b/lib/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx index 4b019a0..0cc9702 100644 --- a/lib/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx @@ -1,67 +1,65 @@ -import {MenuItem, Select, SelectChangeEvent, useTheme} from "@mui/material"; -import {Dispatch, FC, SetStateAction, useState} from "react"; -import {phoneMasksByCountry} from "@utils/phoneMasksByCountry.tsx"; -import {Value} from "react-phone-number-input"; +import { MenuItem, Select, SelectChangeEvent, useTheme } from "@mui/material"; +import { Dispatch, FC, SetStateAction, useState } from "react"; +import { phoneMasksByCountry } from "@utils/phoneMasksByCountry.tsx"; +import { Value } from "react-phone-number-input"; type CountrySelectorProps = { - setMask: Dispatch>; -} - -export const CountrySelector: FC = ({setMask}) => { - const theme = useTheme(); - const [country, setCountry] = useState('RU'); - - const handleChange = (e: SelectChangeEvent) => { - setCountry(e.target.value); - setMask(phoneMasksByCountry[e.target.value][1]); - }; - return ( - - ); + setMask: Dispatch>; }; +export const CountrySelector: FC = ({ setMask }) => { + const theme = useTheme(); + const [country, setCountry] = useState("RU"); + + const handleChange = (e: SelectChangeEvent) => { + setCountry(e.target.value); + setMask(phoneMasksByCountry[e.target.value][1]); + }; + return ( + + ); +}; diff --git a/lib/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx b/lib/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx index 23c4704..312c14b 100644 --- a/lib/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx @@ -1,91 +1,70 @@ -import { - Box, - InputAdornment, - TextField as MuiTextField, - TextFieldProps, - Typography, - useTheme -} from "@mui/material"; -import {useRootContainerSize} from "@contexts/RootContainerWidthContext.ts"; -import {useQuizData} from "@contexts/QuizDataContext.ts"; -import {useIMask} from "react-imask"; -import {quizThemes} from "@utils/themes/Publication/themePublication.ts"; -import {FC, useState} from "react"; -import { - CountrySelector -} from "@/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx"; -import {phoneMasksByCountry} from "@utils/phoneMasksByCountry.tsx"; +import { Box, InputAdornment, TextField as MuiTextField, TextFieldProps, Typography, useTheme } from "@mui/material"; +import { useRootContainerSize } from "@contexts/RootContainerWidthContext.ts"; +import { useQuizData } from "@contexts/QuizDataContext.ts"; +import { useIMask } from "react-imask"; +import { quizThemes } from "@utils/themes/Publication/themePublication.ts"; +import { FC, useState } from "react"; +import { CountrySelector } from "@/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx"; +import { phoneMasksByCountry } from "@utils/phoneMasksByCountry.tsx"; type InputProps = { - title: string; - desc: string; - Icon: FC<{ color: string; backgroundColor: string }>; - onChange: TextFieldProps["onChange"]; - id: string; - isPhone?:boolean + title: string; + desc: string; + Icon: FC<{ color: string; backgroundColor: string }>; + onChange: TextFieldProps["onChange"]; + id: string; + isPhone?: boolean; }; const TextField = MuiTextField as unknown as FC; +export const CustomInput = ({ title, desc, Icon, onChange, isPhone }: InputProps) => { + const theme = useTheme(); + const isMobile = useRootContainerSize() < 600; + const { settings } = useQuizData(); + const [mask, setMask] = useState(phoneMasksByCountry["RU"][1]); + const { ref } = useIMask({ mask }); + return ( + + + {title} + -export const CustomInput = ({ title, desc, Icon, onChange ,isPhone}: InputProps) => { - const theme = useTheme(); - const isMobile = useRootContainerSize() < 600; - const { settings } = useQuizData(); - const [mask, setMask] = useState(phoneMasksByCountry['RU'][1]); - const { ref } = useIMask({mask}); - return ( - - - {title} - - - - - - ), - endAdornment: ( - - {isPhone && ( - )} - - ), - }} - /> - - - ); + + + + ), + endAdornment: ( + {isPhone && } + ), + }} + /> + + ); }; diff --git a/lib/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx b/lib/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx index 6f9b539..bb94104 100644 --- a/lib/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx @@ -1,107 +1,105 @@ -import {useQuizData} from "@contexts/QuizDataContext.ts"; +import { useQuizData } from "@contexts/QuizDataContext.ts"; import NameIcon from "@icons/ContactFormIcon/NameIcon.tsx"; import EmailIcon from "@icons/ContactFormIcon/EmailIcon.tsx"; import TextIcon from "@icons/ContactFormIcon/TextIcon.tsx"; import AddressIcon from "@icons/ContactFormIcon/AddressIcon.tsx"; -import {Dispatch, SetStateAction} from "react"; -import { - CustomInput -} from "@/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx"; +import { Dispatch, SetStateAction } from "react"; +import { CustomInput } from "@/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx"; import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon.tsx"; type InputsProps = { - name: string; - setName: Dispatch>; - email: string; - setEmail: Dispatch>; - phone: string; - setPhone: Dispatch>; - text: string; - setText: Dispatch>; - adress: string; - setAdress: Dispatch>; + name: string; + setName: Dispatch>; + email: string; + setEmail: Dispatch>; + phone: string; + setPhone: Dispatch>; + text: string; + setText: Dispatch>; + adress: string; + setAdress: Dispatch>; }; export const Inputs = ({ - name, - setName, - email, - setEmail, - phone, - setPhone, - text, - setText, - adress, - setAdress, - }: InputsProps) => { - const { settings } = useQuizData(); - const FC = settings.cfg.formContact.fields; + name, + setName, + email, + setEmail, + phone, + setPhone, + text, + setText, + adress, + setAdress, +}: InputsProps) => { + const { settings } = useQuizData(); + const FC = settings.cfg.formContact.fields; - if (!FC) return null; - const Name = ( - setName(target.value)} - id={name} - title={FC["name"].innerText || "Введите имя"} - desc={FC["name"].text || "Имя"} - Icon={NameIcon} - /> - ); - const Email = ( - setEmail(target.value.replaceAll(/\s/g, ""))} - id={email} - title={FC["email"].innerText || "Введите Email"} - desc={FC["email"].text || "Email"} - Icon={EmailIcon} - /> - ); - const Phone = ( - setPhone(target.value)} - id={phone} - title={FC["phone"].innerText || "Введите номер телефона"} - desc={FC["phone"].text || "Номер телефона"} - Icon={PhoneIcon} - isPhone={true} - /> - ); - const Text = ( - setText(target.value)} - id={text} - title={FC["text"].text || "Введите фамилию"} - desc={FC["text"].innerText || "Фамилия"} - Icon={TextIcon} - /> - ); - const Adress = ( - setAdress(target.value)} - id={adress} - title={FC["address"].innerText || "Введите адрес"} - desc={FC["address"].text || "Адрес"} - Icon={AddressIcon} - /> - ); + if (!FC) return null; + const Name = ( + setName(target.value)} + id={name} + title={FC["name"].innerText || "Введите имя"} + desc={FC["name"].text || "Имя"} + Icon={NameIcon} + /> + ); + const Email = ( + setEmail(target.value.replaceAll(/\s/g, ""))} + id={email} + title={FC["email"].innerText || "Введите Email"} + desc={FC["email"].text || "Email"} + Icon={EmailIcon} + /> + ); + const Phone = ( + setPhone(target.value)} + id={phone} + title={FC["phone"].innerText || "Введите номер телефона"} + desc={FC["phone"].text || "Номер телефона"} + Icon={PhoneIcon} + isPhone={true} + /> + ); + const Text = ( + setText(target.value)} + id={text} + title={FC["text"].text || "Введите фамилию"} + desc={FC["text"].innerText || "Фамилия"} + Icon={TextIcon} + /> + ); + const Adress = ( + setAdress(target.value)} + id={adress} + title={FC["address"].innerText || "Введите адрес"} + desc={FC["address"].text || "Адрес"} + Icon={AddressIcon} + /> + ); - if (Object.values(FC).some((data) => data.used)) { - return ( - <> - {FC["name"].used ? Name : <>} - {FC["email"].used ? Email : <>} - {FC["phone"].used ? Phone : <>} - {FC["text"].used ? Text : <>} - {FC["address"].used ? Adress : <>} - - ); - } else { - return ( - <> - {Name} - {Email} - {Phone} - - ); - } + if (Object.values(FC).some((data) => data.used)) { + return ( + <> + {FC["name"].used ? Name : <>} + {FC["email"].used ? Email : <>} + {FC["phone"].used ? Phone : <>} + {FC["text"].used ? Text : <>} + {FC["address"].used ? Adress : <>} + + ); + } else { + return ( + <> + {Name} + {Email} + {Phone} + + ); + } }; diff --git a/lib/components/ViewPublicationPage/Footer.tsx b/lib/components/ViewPublicationPage/Footer.tsx index fa5b6ac..fa2f621 100644 --- a/lib/components/ViewPublicationPage/Footer.tsx +++ b/lib/components/ViewPublicationPage/Footer.tsx @@ -6,53 +6,49 @@ import { useQuizData } from "@contexts/QuizDataContext"; import Stepper from "@ui_kit/Stepper"; type FooterProps = { - stepNumber: number | null; - nextButton: ReactNode; - prevButton: ReactNode; + stepNumber: number | null; + nextButton: ReactNode; + prevButton: ReactNode; }; export const Footer = ({ stepNumber, nextButton, prevButton }: FooterProps) => { - const theme = useTheme(); - const { questions, settings } = useQuizData(); - const questionsAmount = questions.filter( - ({ type }) => type !== "result" - ).length; + const theme = useTheme(); + const { questions, settings } = useQuizData(); + const questionsAmount = questions.filter(({ type }) => type !== "result").length; - return ( - - - {stepNumber !== null && ( - - - Вопрос {stepNumber} из {questionsAmount} - - - - )} - {prevButton} - {nextButton} - - - ); + return ( + + + {stepNumber !== null && ( + + + Вопрос {stepNumber} из {questionsAmount} + + + + )} + {prevButton} + {nextButton} + + + ); }; diff --git a/lib/components/ViewPublicationPage/Question.tsx b/lib/components/ViewPublicationPage/Question.tsx index 5a9f7bb..7579d72 100644 --- a/lib/components/ViewPublicationPage/Question.tsx +++ b/lib/components/ViewPublicationPage/Question.tsx @@ -48,9 +48,7 @@ export const Question = ({ height: "100%", backgroundPosition: "center", backgroundSize: "cover", - backgroundImage: settings.cfg.design - ? `url(${DESIGN_LIST[settings.cfg.theme]})` - : null, + backgroundImage: settings.cfg.design ? `url(${DESIGN_LIST[settings.cfg.theme]})` : null, }} > {questionSelect} -
+
); }; -function QuestionByType({ - question, - stepNumber, -}: { - question: RealTypedQuizQuestion; - stepNumber: number | null; -}) { +function QuestionByType({ question, stepNumber }: { question: RealTypedQuizQuestion; stepNumber: number | null }) { switch (question.type) { case "variant": return ; diff --git a/lib/components/ViewPublicationPage/QuestionSelect.tsx b/lib/components/ViewPublicationPage/QuestionSelect.tsx index 3c4964b..69dfdb5 100644 --- a/lib/components/ViewPublicationPage/QuestionSelect.tsx +++ b/lib/components/ViewPublicationPage/QuestionSelect.tsx @@ -2,111 +2,111 @@ import { useQuizData } from "@/contexts/QuizDataContext"; import { AnyTypedQuizQuestion } from "@/model/questionTypes/shared"; import { Box, FormControl, MenuItem, Select as MuiSelect, useTheme } from "@mui/material"; - interface Props { - selectedQuestion: AnyTypedQuizQuestion; - setQuestion: (questionIdF: string) => void; + selectedQuestion: AnyTypedQuizQuestion; + setQuestion: (questionIdF: string) => void; } export default function QuestionSelect({ selectedQuestion, setQuestion }: Props) { - const theme = useTheme(); - const { questions, preview } = useQuizData(); + const theme = useTheme(); + const { questions, preview } = useQuizData(); - if (!preview) return null; + if (!preview) return null; - return ( - - + + { + setQuestion(target.value); + }} + sx={{ + height: "48px", + borderRadius: "8px", + "& .MuiOutlinedInput-notchedOutline": { + border: `1px solid ${theme.palette.primary.main} !important`, + }, + "& .MuiSelect-icon": { + color: theme.palette.primary.main, + }, + }} + MenuProps={{ + PaperProps: { + sx: { + mt: "8px", + p: "4px", + borderRadius: "8px", + border: "1px solid #EEE4FC", + boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)", + backgroundColor: theme.palette.background.default, + }, + }, + MenuListProps: { + sx: { + py: 0, + display: "flex", + flexDirection: "column", + gap: "8px", + "& .Mui-selected": { + backgroundColor: theme.palette.background.default, + color: theme.palette.primary.main, + }, + }, + }, + }} + inputProps={{ + sx: { + color: theme.palette.primary.main, + display: "block", + px: "9px", + gap: "20px", + width: "87%", + overflow: "hidden", + textOverflow: "ellipsis", + }, + }} + > + {questions + .filter((q) => q.type !== "result") + .map((question, index) => ( + - { - setQuestion(target.value); - }} - sx={{ - height: "48px", - borderRadius: "8px", - "& .MuiOutlinedInput-notchedOutline": { - border: `1px solid ${theme.palette.primary.main} !important`, - }, - "& .MuiSelect-icon": { - color: theme.palette.primary.main, - }, - }} - MenuProps={{ - PaperProps: { - sx: { - mt: "8px", - p: "4px", - borderRadius: "8px", - border: "1px solid #EEE4FC", - boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)", - backgroundColor: theme.palette.background.default, - }, - }, - MenuListProps: { - sx: { - py: 0, - display: "flex", - flexDirection: "column", - gap: "8px", - "& .Mui-selected": { - backgroundColor: theme.palette.background.default, - color: theme.palette.primary.main, - }, - }, - }, - }} - inputProps={{ - sx: { - color: theme.palette.primary.main, - display: "block", - px: "9px", - gap: "20px", - width: "87%", - overflow: "hidden", - textOverflow: "ellipsis", - }, - }} - > - {questions.filter((q) => q.type !== "result").map( - (question, index) => ( - - {`${index + 1}. ${question.title}`} - - ), - )} - - - - ); + > + {`${index + 1}. ${question.title}`} + + ))} + + + + ); } diff --git a/lib/components/ViewPublicationPage/ResultForm.tsx b/lib/components/ViewPublicationPage/ResultForm.tsx index a1f707d..629f8c1 100644 --- a/lib/components/ViewPublicationPage/ResultForm.tsx +++ b/lib/components/ViewPublicationPage/ResultForm.tsx @@ -26,9 +26,7 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => { const isMobile = useRootContainerSize() < 650; const isTablet = useRootContainerSize() < 1000; const { settings, show_badge, quizId } = useQuizData(); - const setCurrentQuizStep = useQuizViewStore( - (state) => state.setCurrentQuizStep - ); + const setCurrentQuizStep = useQuizViewStore((state) => state.setCurrentQuizStep); const spec = settings.cfg.spec; const vkMetrics = useVkMetricsGoals(settings.cfg.vkMetricsNumber); const yandexMetrics = useYandexMetricsGoals(settings.cfg.yandexMetricsNumber); @@ -38,246 +36,236 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => { yandexMetrics.resultIdShown(resultQuestion.id); }, []); - return ( + return ( + + - + - - - - Ваш результат: - - - - {!resultQuestion?.content.useImage && - resultQuestion.content.video && ( - - )} - {resultQuestion?.content.useImage && - resultQuestion.content.back && ( - -
resultImage - - )} - {resultQuestion.description !== "" && - resultQuestion.description !== " " && ( - - {resultQuestion.description} - - )} + Ваш результат: + + + + {!resultQuestion?.content.useImage && resultQuestion.content.video && ( + + )} + {resultQuestion?.content.useImage && resultQuestion.content.back && ( + + resultImage + + )} + {resultQuestion.description !== "" && resultQuestion.description !== " " && ( + + {resultQuestion.description} + + )} - - {resultQuestion.title} - + + {resultQuestion.title} + - {resultQuestion.content.text !== "" && - resultQuestion.content.text !== " " && ( - - {resultQuestion.content.text} - - )} - - - {show_badge && ( - - - - )} - - {settings.cfg.resultInfo.showResultForm === "before" && - !settings.cfg.score && ( - - )} - {settings.cfg.resultInfo.showResultForm === "after" && - resultQuestion.content.redirect && ( - - )} - - + {resultQuestion.content.text !== "" && resultQuestion.content.text !== " " && ( + + {resultQuestion.content.text} + + )} + - ); + {show_badge && ( + + + + )} + + {settings.cfg.resultInfo.showResultForm === "before" && !settings.cfg.score && ( + + )} + {settings.cfg.resultInfo.showResultForm === "after" && resultQuestion.content.redirect && ( + + )} + + + + ); }; diff --git a/lib/components/ViewPublicationPage/StartPageViewPublication/QuizPreviewLayoutByType.tsx b/lib/components/ViewPublicationPage/StartPageViewPublication/QuizPreviewLayoutByType.tsx index bebcfb1..c643e99 100644 --- a/lib/components/ViewPublicationPage/StartPageViewPublication/QuizPreviewLayoutByType.tsx +++ b/lib/components/ViewPublicationPage/StartPageViewPublication/QuizPreviewLayoutByType.tsx @@ -3,10 +3,7 @@ import { StartPageMobile } from "./StartPageMobile"; import { useRootContainerSize } from "@contexts/RootContainerWidthContext"; -import type { - QuizStartpageAlignType, - QuizStartpageType, -} from "@model/settingsData"; +import type { QuizStartpageAlignType, QuizStartpageType } from "@model/settingsData"; type QuizPreviewLayoutByTypeProps = { quizHeaderBlock: JSX.Element; diff --git a/lib/components/ViewPublicationPage/StartPageViewPublication/StartPageDesktop.tsx b/lib/components/ViewPublicationPage/StartPageViewPublication/StartPageDesktop.tsx index 53790ff..7e2c846 100644 --- a/lib/components/ViewPublicationPage/StartPageViewPublication/StartPageDesktop.tsx +++ b/lib/components/ViewPublicationPage/StartPageViewPublication/StartPageDesktop.tsx @@ -1,277 +1,261 @@ -import {Box} from "@mui/material"; +import { Box } from "@mui/material"; -import {useRootContainerSize} from "@contexts/RootContainerWidthContext"; -import {useQuizData} from "@contexts/QuizDataContext"; +import { useRootContainerSize } from "@contexts/RootContainerWidthContext"; +import { useQuizData } from "@contexts/QuizDataContext"; -import {notReachable} from "@utils/notReachable"; -import {quizThemes} from "@utils/themes/Publication/themePublication"; +import { notReachable } from "@utils/notReachable"; +import { quizThemes } from "@utils/themes/Publication/themePublication"; -import type { - QuizStartpageAlignType, - QuizStartpageType, -} from "@model/settingsData"; -import {DESIGN_LIST} from "@/utils/designList"; +import type { QuizStartpageAlignType, QuizStartpageType } from "@model/settingsData"; +import { DESIGN_LIST } from "@/utils/designList"; type StartPageDesktopProps = { - quizHeaderBlock: JSX.Element; - quizMainBlock: JSX.Element; - backgroundBlock: JSX.Element | null; - startpageType: QuizStartpageType; - alignType: QuizStartpageAlignType; + quizHeaderBlock: JSX.Element; + quizMainBlock: JSX.Element; + backgroundBlock: JSX.Element | null; + startpageType: QuizStartpageType; + alignType: QuizStartpageAlignType; }; type LayoutProps = Omit; -const StandartLayout = ({ - alignType, - quizHeaderBlock, - quizMainBlock, - backgroundBlock, - }: LayoutProps) => { - const size = useRootContainerSize(); - const isTablet = size >= 700 && size < 1100; - const {settings} = useQuizData(); +const StandartLayout = ({ alignType, quizHeaderBlock, quizMainBlock, backgroundBlock }: LayoutProps) => { + const size = useRootContainerSize(); + const isTablet = size >= 700 && size < 1100; + const { settings } = useQuizData(); - return ( + return ( + + - - - {quizHeaderBlock} - {quizMainBlock} - - {settings.cfg.startpage.background.desktop && ( - img": {width: "100%", borderRadius: "12px"}, - }} - >{backgroundBlock} - )} - + {quizHeaderBlock} + {quizMainBlock} - ); + {settings.cfg.startpage.background.desktop && ( + + img": { width: "100%", borderRadius: "12px" }, + }} + > + {backgroundBlock} + + + )} + + + ); }; -const ExpandedLayout = ({ - alignType, - quizHeaderBlock, - quizMainBlock, - backgroundBlock, - }: LayoutProps) => { - const size = useRootContainerSize(); - const isTablet = size >= 700 && size < 1100; - return ( - <> - - - {alignType !== "center" && quizHeaderBlock} - {quizMainBlock} - - - - {backgroundBlock} - - - ); -} - -const CenteredLayout = ({ - quizHeaderBlock, - quizMainBlock, - backgroundBlock, - }: LayoutProps) => { - const isTablet = useRootContainerSize() < 1100; - const {settings} = useQuizData(); - return ( +const ExpandedLayout = ({ alignType, quizHeaderBlock, quizMainBlock, backgroundBlock }: LayoutProps) => { + const size = useRootContainerSize(); + const isTablet = size >= 700 && size < 1100; + return ( + <> + - {quizHeaderBlock} - {backgroundBlock && settings.cfg.startpage.background.desktop && ( - img": {width: "100%", borderRadius: "12px"}, - }} - > - {backgroundBlock} - - )} - {quizMainBlock} + {alignType !== "center" && quizHeaderBlock} + {quizMainBlock} - ); + + + {backgroundBlock} + + + ); +}; + +const CenteredLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock }: LayoutProps) => { + const isTablet = useRootContainerSize() < 1100; + const { settings } = useQuizData(); + return ( + + {quizHeaderBlock} + {backgroundBlock && settings.cfg.startpage.background.desktop && ( + img": { width: "100%", borderRadius: "12px" }, + }} + > + {backgroundBlock} + + )} + {quizMainBlock} + + ); }; export const StartPageDesktop = ({ - quizHeaderBlock, - quizMainBlock, - backgroundBlock, - startpageType, - alignType, - }: StartPageDesktopProps) => { - switch (startpageType) { - case null: - case "standard": { - return ( - - ); - } - - case "expanded": { - return ( - - ); - } - - case "centered": { - return ( - - ); - } - - default: - notReachable(startpageType); + quizHeaderBlock, + quizMainBlock, + backgroundBlock, + startpageType, + alignType, +}: StartPageDesktopProps) => { + switch (startpageType) { + case null: + case "standard": { + return ( + + ); } + + case "expanded": { + return ( + + ); + } + + case "centered": { + return ( + + ); + } + + default: + notReachable(startpageType); + } }; diff --git a/lib/components/ViewPublicationPage/StartPageViewPublication/StartPageMobile.tsx b/lib/components/ViewPublicationPage/StartPageViewPublication/StartPageMobile.tsx index 7b11f30..bc8ae5c 100644 --- a/lib/components/ViewPublicationPage/StartPageViewPublication/StartPageMobile.tsx +++ b/lib/components/ViewPublicationPage/StartPageViewPublication/StartPageMobile.tsx @@ -1,283 +1,270 @@ -import {Box} from "@mui/material"; +import { Box } from "@mui/material"; -import {useQuizData} from "@contexts/QuizDataContext"; +import { useQuizData } from "@contexts/QuizDataContext"; -import {notReachable} from "@utils/notReachable"; -import {quizThemes} from "@utils/themes/Publication/themePublication"; +import { notReachable } from "@utils/notReachable"; +import { quizThemes } from "@utils/themes/Publication/themePublication"; -import type {QuizStartpageType} from "@model/settingsData"; -import {DESIGN_LIST} from "@/utils/designList"; +import type { QuizStartpageType } from "@model/settingsData"; +import { DESIGN_LIST } from "@/utils/designList"; type StartPageMobileProps = { - quizHeaderBlock: JSX.Element; - quizMainBlock: JSX.Element; - backgroundBlock: JSX.Element | null; - startpageType: QuizStartpageType; + quizHeaderBlock: JSX.Element; + quizMainBlock: JSX.Element; + backgroundBlock: JSX.Element | null; + startpageType: QuizStartpageType; }; type MobileLayoutProps = Omit; -const StandartMobileLayout = ({ - quizHeaderBlock, - quizMainBlock, - backgroundBlock, - }: MobileLayoutProps) => { - const {settings} = useQuizData(); +const StandartMobileLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock }: MobileLayoutProps) => { + const { settings } = useQuizData(); - return ( - + return ( + + + {quizHeaderBlock} + {settings.cfg.startpage.background.desktop && ( + img": { + width: "100%", + borderRadius: "12px", + }, + }} > - - {quizHeaderBlock} - - {settings.cfg.startpage.background.desktop && ( - - img": { - width: "100%", - borderRadius: "12px" - }, - }} - > - {backgroundBlock} - - - )} - - {quizMainBlock} - + {backgroundBlock} + + )} + + {quizMainBlock} - ); + + + ); }; -const ExpandedMobileLayout = ({ - quizHeaderBlock, - quizMainBlock, - backgroundBlock, - }: MobileLayoutProps) => ( +const ExpandedMobileLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock }: MobileLayoutProps) => ( + - - - {quizHeaderBlock} - {quizMainBlock} - - - img": { - display: "block", - minHeight: "100%", - }, - }} - > - {backgroundBlock} - + + {quizHeaderBlock} + {quizMainBlock} + + img": { + display: "block", + minHeight: "100%", + }, + }} + > + {backgroundBlock} + + ); -const CenteredMobileLayout = ({ - quizHeaderBlock, - quizMainBlock, - backgroundBlock, - }: MobileLayoutProps) => { - const {settings} = useQuizData(); - return ( - { + const { settings } = useQuizData(); + return ( + + + {quizHeaderBlock} + {settings.cfg.startpage.background.desktop && ( + img": { width: "100%", borderRadius: "12px" }, }} + > + {backgroundBlock} + + )} + - - {quizHeaderBlock} - {settings.cfg.startpage.background.desktop && ( - img": {width: "100%", borderRadius: "12px"}, - }} - > - {backgroundBlock} - - )} - - {quizMainBlock} - - + {quizMainBlock} - ); -} + + + ); +}; export const StartPageMobile = ({ - quizHeaderBlock, - quizMainBlock, - backgroundBlock, - startpageType, - }: StartPageMobileProps) => { - switch (startpageType) { - case null: - case "standard": { - return ( - - ); - } - - case "expanded": { - return ( - - ); - } - - case "centered": { - return ( - - ); - } - - default: - notReachable(startpageType); + quizHeaderBlock, + quizMainBlock, + backgroundBlock, + startpageType, +}: StartPageMobileProps) => { + switch (startpageType) { + case null: + case "standard": { + return ( + + ); } + + case "expanded": { + return ( + + ); + } + + case "centered": { + return ( + + ); + } + + default: + notReachable(startpageType); + } }; diff --git a/lib/components/ViewPublicationPage/StartPageViewPublication/index.tsx b/lib/components/ViewPublicationPage/StartPageViewPublication/index.tsx index ee6508d..d249b7f 100644 --- a/lib/components/ViewPublicationPage/StartPageViewPublication/index.tsx +++ b/lib/components/ViewPublicationPage/StartPageViewPublication/index.tsx @@ -1,12 +1,4 @@ -import { - Box, - Button, - ButtonBase, - Link, - Paper, - Typography, - useTheme, -} from "@mui/material"; +import { Box, Button, ButtonBase, Link, Paper, Typography, useTheme } from "@mui/material"; import { QuizPreviewLayoutByType } from "./QuizPreviewLayoutByType"; @@ -29,9 +21,7 @@ export const StartPageViewPublication = () => { const theme = useTheme(); const { settings, show_badge, quizId, questions } = useQuizData(); const { isMobileDevice } = useUADevice(); - const setCurrentQuizStep = useQuizViewStore( - (state) => state.setCurrentQuizStep - ); + const setCurrentQuizStep = useQuizViewStore((state) => state.setCurrentQuizStep); const size = useRootContainerSize(); const isMobile = size < 700; @@ -50,18 +40,11 @@ export const StartPageViewPublication = () => { const background = settings.cfg.startpage.background.type === "image" ? ( { videoUrl={settings.cfg.startpage.background.video} containerSX={{ width: settings.cfg.startpageType === "centered" ? "550px" : "100%", - height: - settings.cfg.startpageType === "centered" ? "275px" : "100%", - borderRadius: - settings.cfg.startpageType === "centered" ? "10px" : "0", + height: settings.cfg.startpageType === "centered" ? "275px" : "100%", + borderRadius: settings.cfg.startpageType === "centered" ? "10px" : "0", overflow: "hidden", "& iframe": { width: "100%", @@ -106,8 +87,7 @@ export const StartPageViewPublication = () => { display: "flex", alignItems: "center", flexWrap: - settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" + settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? "nowrap" : "wrap", gap: isMobile ? "20px" : "30px", @@ -116,15 +96,11 @@ export const StartPageViewPublication = () => { ? isMobile ? "20px" : "25px" - : settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" && - !isMobile + : settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && !isMobile ? 0 : "7px", justifyContent: - settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" && - isMobile + settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && isMobile ? "center" : undefined, }} @@ -143,13 +119,9 @@ export const StartPageViewPublication = () => { { { textDecoration: "none", marginLeft: settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" && - !isTablet && - !isMobile + settings.cfg.startpage.position === "center" && + !isTablet && + !isMobile ? "61px" : undefined, }} @@ -223,14 +194,11 @@ export const StartPageViewPublication = () => { vkMetrics.emailOpened(); yandexMetrics.emailOpened(); - setTimeout(() => { location.href = ( - settings.cfg.info.site.includes("https") - ? settings.cfg.info.site - : `https://${settings.cfg.info.site}` + settings.cfg.info.site.includes("https") ? settings.cfg.info.site : `https://${settings.cfg.info.site}` ).replace(/\s+/g, ""); - }, 1000) + }, 1000); }; return ( @@ -242,8 +210,7 @@ export const StartPageViewPublication = () => { width: "100%", background: settings.cfg.startpageType === "expanded" - ? settings.cfg.startpage.position === "left" || - (isMobile && settings.cfg.startpage.position === "right") + ? settings.cfg.startpage.position === "left" || (isMobile && settings.cfg.startpage.position === "right") ? "linear-gradient(90deg, rgba(39, 38, 38, 0.95) 7.66%, rgba(42, 42, 46, 0.85) 42.12%, rgba(51, 54, 71, 0.4) 100%)" : settings.cfg.startpage.position === "center" ? "linear-gradient(0deg, rgba(39, 38, 38, 0.95) 7.66%, rgba(42, 42, 46, 0.85) 42.12%, rgba(51, 54, 71, 0.4) 100%)" @@ -261,10 +228,7 @@ export const StartPageViewPublication = () => { sx={{ display: "flex", flexDirection: "column", - justifyContent: - settings.cfg.startpageType === "standard" && isMobile - ? "start" - : "center", + justifyContent: settings.cfg.startpageType === "standard" && isMobile ? "start" : "center", flexGrow: settings.cfg.startpageType === "centered" ? 0 : 1, wordBreak: "break-word", alignItems: @@ -275,19 +239,14 @@ export const StartPageViewPublication = () => { ? "center" : "start" : "start", - marginTop: - settings.cfg.startpageType === "centered" - ? "30px" - : isMobile - ? "0px" - : "5px", + marginTop: settings.cfg.startpageType === "centered" ? "30px" : isMobile ? "0px" : "5px", maxWidth: isMobile ? "100%" : settings.cfg.startpageType === "centered" ? "700px" : isTablet && - settings.cfg.startpageType !== "expanded" && - settings.cfg.startpage.position !== "center" + settings.cfg.startpageType !== "expanded" && + settings.cfg.startpage.position !== "center" ? "380px" : "531px", }} @@ -302,14 +261,10 @@ export const StartPageViewPublication = () => { overflowWrap: "break-word", width: "100%", textAlign: - settings.cfg.startpageType === "centered" || - settings.cfg.startpage.position === "center" + settings.cfg.startpageType === "centered" || settings.cfg.startpage.position === "center" ? "center" : "-moz-initial", - color: - settings.cfg.startpageType === "expanded" - ? "white" - : theme.palette.text.primary, + color: settings.cfg.startpageType === "expanded" ? "white" : theme.palette.text.primary, }} > {settings.name} @@ -323,23 +278,15 @@ export const StartPageViewPublication = () => { overflowWrap: "break-word", width: "100%", textAlign: - settings.cfg.startpageType === "centered" || - settings.cfg.startpage.position === "center" + settings.cfg.startpageType === "centered" || settings.cfg.startpage.position === "center" ? "center" : "-moz-initial", - color: - settings.cfg.startpageType === "expanded" - ? "white" - : theme.palette.text.primary, + color: settings.cfg.startpageType === "expanded" ? "white" : theme.palette.text.primary, }} > {settings.cfg.startpage.description} - + { maxWidth: "300px", display: (settings.cfg.startpageType === "centered" && isMobile) || - (settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" && - isMobile) + (settings.cfg.startpageType === "expanded" && + settings.cfg.startpage.position === "center" && + isMobile) ? "flex" : "block", flexDirection: "column", alignItems: "center", order: - settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" + settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? "2" : "0", }} @@ -418,8 +356,8 @@ export const StartPageViewPublication = () => { marginTop: "10px", marginLeft: settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" && - !isMobile + settings.cfg.startpage.position === "center" && + !isMobile ? "auto" : undefined, }} @@ -430,12 +368,14 @@ export const StartPageViewPublication = () => { fontSize: "16px", textAlign: settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" && - !isMobile - ? "end" : settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" && - isMobile || settings.cfg.startpageType === "centered" && - isMobile ? "center" + settings.cfg.startpage.position === "center" && + !isMobile + ? "end" + : (settings.cfg.startpageType === "expanded" && + settings.cfg.startpage.position === "center" && + isMobile) || + (settings.cfg.startpageType === "centered" && isMobile) + ? "center" : "start", color: theme.palette.primary.main, overflow: "hidden", @@ -454,15 +394,11 @@ export const StartPageViewPublication = () => { sx={{ lineHeight: "19px", textAlign: - settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" + settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? "end" : "none", fontSize: "16px", - color: - settings.cfg.startpageType === "expanded" - ? "#FFFFFF" - : theme.palette.text.primary, + color: settings.cfg.startpageType === "expanded" ? "#FFFFFF" : theme.palette.text.primary, }} > {settings.cfg.info.phonenumber} @@ -476,8 +412,8 @@ export const StartPageViewPublication = () => { marginTop: "10px", marginLeft: settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" && - !isMobile + settings.cfg.startpage.position === "center" && + !isMobile ? "auto" : undefined, }} @@ -485,16 +421,12 @@ export const StartPageViewPublication = () => { {settings.cfg.info.phonenumber} @@ -506,16 +438,12 @@ export const StartPageViewPublication = () => { sx={{ lineHeight: "19px", textAlign: - settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" + settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? "end" : "none", fontSize: "16px", marginTop: "10px", - color: - settings.cfg.startpageType === "expanded" - ? "#FFFFFF" - : theme.palette.text.primary, + color: settings.cfg.startpageType === "expanded" ? "#FFFFFF" : theme.palette.text.primary, }} > {settings.cfg.info.phonenumber} @@ -530,24 +458,20 @@ export const StartPageViewPublication = () => { fontSize: "12px", textAlign: settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" && - !isMobile + settings.cfg.startpage.position === "center" && + !isMobile ? "end" : (settings.cfg.startpageType === "expanded" && - settings.cfg.startpage.position === "center" && - isMobile) || - (settings.cfg.startpageType === "centered" && - isMobile) + settings.cfg.startpage.position === "center" && + isMobile) || + (settings.cfg.startpageType === "centered" && isMobile) ? "center" : "none", maxHeight: "120px", overflow: "auto", marginTop: "10px", "&::-webkit-scrollbar": { width: 0 }, - color: - settings.cfg.startpageType === "expanded" - ? "white" - : theme.palette.text.primary, + color: settings.cfg.startpageType === "expanded" ? "white" : theme.palette.text.primary, }} > {settings.cfg.info.law} diff --git a/lib/components/ViewPublicationPage/ViewPublicationPage.tsx b/lib/components/ViewPublicationPage/ViewPublicationPage.tsx index e73e768..3919ac3 100644 --- a/lib/components/ViewPublicationPage/ViewPublicationPage.tsx +++ b/lib/components/ViewPublicationPage/ViewPublicationPage.tsx @@ -18,13 +18,7 @@ import { useVKMetrics } from "@/utils/hooks/metrics/useVKMetrics"; import { ContactForm } from "@/components/ViewPublicationPage/ContactForm/ContactForm.tsx"; export default function ViewPublicationPage() { - const { - settings, - recentlyCompleted, - quizId, - preview, - changeFaviconAndTitle, - } = useQuizData(); + const { settings, recentlyCompleted, quizId, preview, changeFaviconAndTitle } = useQuizData(); const answers = useQuizViewStore((state) => state.answers); let currentQuizStep = useQuizViewStore((state) => state.currentQuizStep); const { @@ -40,9 +34,7 @@ export default function ViewPublicationPage() { useYandexMetrics(settings?.cfg?.yandexMetricsNumber); useVKMetrics(settings?.cfg?.vkMetricsNumber); - const isAnswer = answers.some( - (ans) => ans.questionId === currentQuestion?.id - ); + const isAnswer = answers.some((ans) => ans.questionId === currentQuestion?.id); useEffect( function setFaviconAndTitle() { @@ -59,14 +51,11 @@ export default function ViewPublicationPage() { ); if (recentlyCompleted) throw new Error("Quiz already completed"); - if (currentQuizStep === "startpage" && settings.cfg.noStartPage) - currentQuizStep = "question"; + if (currentQuizStep === "startpage" && settings.cfg.noStartPage) currentQuizStep = "question"; if (!currentQuestion) return ( - + Вопрос не выбран @@ -91,10 +80,7 @@ export default function ViewPublicationPage() { currentQuestion={currentQuestion} currentQuestionStepNumber={currentQuestionStepNumber} prevButton={ - + } nextButton={ } - questionSelect={ - - } + questionSelect={} /> ); break; } case "contactform": { - quizStepElement = ( - - ); + quizStepElement = ; break; } default: @@ -140,10 +116,6 @@ export default function ViewPublicationPage() { } return ( - - {quizStepElement} - + {quizStepElement} ); } diff --git a/lib/components/ViewPublicationPage/questions/Date/index.tsx b/lib/components/ViewPublicationPage/questions/Date/index.tsx index 5f8daed..4a64c9f 100644 --- a/lib/components/ViewPublicationPage/questions/Date/index.tsx +++ b/lib/components/ViewPublicationPage/questions/Date/index.tsx @@ -25,9 +25,7 @@ export const Date = ({ currentQuestion }: DateProps) => { const answers = useQuizViewStore((state) => state.answers); const { updateAnswer } = useQuizViewStore((state) => state); const theme = useTheme(); - const answer = answers.find( - ({ questionId }) => questionId === currentQuestion.id - )?.answer as string; + const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string; const currentAnswer = moment(answer) || moment(); const onDateChange = async (date: Moment | null) => { @@ -52,11 +50,7 @@ export const Date = ({ currentQuestion }: DateProps) => { return ( - + {currentQuestion.title} { ? "#F2F3F7" : "rgba(154,154,175, 0.2)" : quizThemes[settings.cfg.theme].isLight - ? "white" - : theme.palette.background.default, + ? "white" + : theme.palette.background.default, borderRadius: "10px", maxWidth: "250px", pr: "30px", diff --git a/lib/components/ViewPublicationPage/questions/Emoji/EmojiVariant.tsx b/lib/components/ViewPublicationPage/questions/Emoji/EmojiVariant.tsx index 1041e91..bdcaa17 100644 --- a/lib/components/ViewPublicationPage/questions/Emoji/EmojiVariant.tsx +++ b/lib/components/ViewPublicationPage/questions/Emoji/EmojiVariant.tsx @@ -1,11 +1,4 @@ -import { - Box, - FormControl, - FormControlLabel, - Radio, - Typography, - useTheme, -} from "@mui/material"; +import { Box, FormControl, FormControlLabel, Radio, Typography, useTheme } from "@mui/material"; import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill"; import { enqueueSnackbar } from "notistack"; @@ -31,19 +24,12 @@ type EmojiVariantProps = { setIsSending: (isSending: boolean) => void; }; -export const EmojiVariant = ({ - currentQuestion, - variant, - index, - isSending, - setIsSending, -}: EmojiVariantProps) => { +export const EmojiVariant = ({ currentQuestion, variant, index, isSending, setIsSending }: EmojiVariantProps) => { const { quizId, settings, preview } = useQuizData(); const answers = useQuizViewStore((state) => state.answers); const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state); const theme = useTheme(); - const { answer } = - answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; + const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; const onVariantClick = async (event: MouseEvent) => { event.preventDefault(); @@ -54,9 +40,7 @@ export const EmojiVariant = ({ await sendAnswer({ questionId: currentQuestion.id, body: - currentQuestion.content.variants[index].extendedText + - " " + - currentQuestion.content.variants[index].answer, + currentQuestion.content.variants[index].extendedText + " " + currentQuestion.content.variants[index].answer, qid: quizId, preview, }); @@ -94,8 +78,7 @@ export const EmojiVariant = ({ sx={{ borderRadius: "12px", border: `1px solid`, - borderColor: - answer === variant.id ? theme.palette.primary.main : "#9A9AAF", + borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF", overflow: "hidden", maxWidth: "317px", width: "100%", @@ -103,10 +86,9 @@ export const EmojiVariant = ({ background: settings.cfg.design && !quizThemes[settings.cfg.theme].isLight ? "rgba(255,255,255, 0.3)" - : (settings.cfg.design && quizThemes[settings.cfg.theme].isLight) || - quizThemes[settings.cfg.theme].isLight - ? "#FFFFFF" - : "transparent", + : (settings.cfg.design && quizThemes[settings.cfg.theme].isLight) || quizThemes[settings.cfg.theme].isLight + ? "#FFFFFF" + : "transparent", "&:hover": { borderColor: theme.palette.primary.main }, }} // value={index} @@ -128,9 +110,7 @@ export const EmojiVariant = ({ justifyContent: "center", }} > - {variant.extendedText && ( - {variant.extendedText} - )} + {variant.extendedText && {variant.extendedText}} - - {variant.answer} - + {variant.answer} } /> diff --git a/lib/components/ViewPublicationPage/questions/Emoji/index.tsx b/lib/components/ViewPublicationPage/questions/Emoji/index.tsx index ae53dab..d9f7521 100644 --- a/lib/components/ViewPublicationPage/questions/Emoji/index.tsx +++ b/lib/components/ViewPublicationPage/questions/Emoji/index.tsx @@ -18,23 +18,16 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => { const answers = useQuizViewStore((state) => state.answers); const { updateAnswer } = useQuizViewStore((state) => state); const theme = useTheme(); - const { answer } = - answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; + const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; return ( - + {currentQuestion.title} answer === id - )} + value={currentQuestion.content.variants.findIndex(({ id }) => answer === id)} onChange={({ target }) => updateAnswer( currentQuestion.id, @@ -50,9 +43,7 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => { marginTop: "20px", }} > - + {currentQuestion.content.variants.map((variant, index) => ( void; }; -export const UploadFile = ({ - currentQuestion, - setModalWarningType, - isSending, - setIsSending, -}: UploadFileProps) => { +export const UploadFile = ({ currentQuestion, setModalWarningType, isSending, setIsSending }: UploadFileProps) => { const { quizId, preview } = useQuizData(); - const [isDropzoneHighlighted, setIsDropzoneHighlighted] = - useState(false); + const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState(false); const theme = useTheme(); const answers = useQuizViewStore((state) => state.answers); const { updateAnswer } = useQuizViewStore((state) => state); const isMobile = useRootContainerSize() < 500; - const answer = answers.find( - ({ questionId }) => questionId === currentQuestion.id - )?.answer as string; + const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string; const uploadFile = async (file: File | undefined) => { if (isSending) return; if (!file) return; if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize"); - const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP[ - currentQuestion.content.type - ].some((fileType) => file.name.toLowerCase().endsWith(fileType)); + const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].some((fileType) => + file.name.toLowerCase().endsWith(fileType) + ); if (!isFileTypeAccepted) return setModalWarningType("errorType"); @@ -76,11 +68,7 @@ export const UploadFile = ({ preview, }); - updateAnswer( - currentQuestion.id, - `${file.name}|${URL.createObjectURL(file)}`, - 0 - ); + updateAnswer(currentQuestion.id, `${file.name}|${URL.createObjectURL(file)}`, 0); } catch (error) { console.error(error); enqueueSnackbar("ответ не был засчитан"); @@ -101,28 +89,18 @@ export const UploadFile = ({ return ( {isSending ? ( - + ) : ( - + uploadFile(target.files?.[0])} hidden - accept={ACCEPT_SEND_FILE_TYPES_MAP[ - currentQuestion.content.type - ].join(",")} + accept={ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].join(",")} multiple type="file" /> - !answer?.split("|")[0] && setIsDropzoneHighlighted(true) - } + onDragEnter={() => !answer?.split("|")[0] && setIsDropzoneHighlighted(true)} onDragLeave={() => setIsDropzoneHighlighted(false)} onDragOver={(event) => event.preventDefault()} onDrop={onDrop} @@ -142,10 +120,7 @@ export const UploadFile = ({ - { - UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type] - .title - } + {UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type].title} - { - UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type] - .description - } + {UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type].description} diff --git a/lib/components/ViewPublicationPage/questions/File/UploadedFile.tsx b/lib/components/ViewPublicationPage/questions/File/UploadedFile.tsx index f887b98..e18e6b6 100644 --- a/lib/components/ViewPublicationPage/questions/File/UploadedFile.tsx +++ b/lib/components/ViewPublicationPage/questions/File/UploadedFile.tsx @@ -13,18 +13,13 @@ type UploadedFileProps = { setIsSending: (isSending: boolean) => void; }; -export const UploadedFile = ({ - currentQuestion, - setIsSending, -}: UploadedFileProps) => { +export const UploadedFile = ({ currentQuestion, setIsSending }: UploadedFileProps) => { const { quizId, preview } = useQuizData(); const answers = useQuizViewStore((state) => state.answers); const { updateAnswer } = useQuizViewStore((state) => state); const theme = useTheme(); - const answer = answers.find( - ({ questionId }) => questionId === currentQuestion.id - )?.answer as string; + const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string; const deleteFile = async () => { if (answer.length > 0) { diff --git a/lib/components/ViewPublicationPage/questions/File/index.tsx b/lib/components/ViewPublicationPage/questions/File/index.tsx index 34cfb15..b1ad427 100644 --- a/lib/components/ViewPublicationPage/questions/File/index.tsx +++ b/lib/components/ViewPublicationPage/questions/File/index.tsx @@ -11,14 +11,7 @@ import { ACCEPT_SEND_FILE_TYPES_MAP } from "@/components/ViewPublicationPage/too import type { QuizQuestionFile } from "@model/questionTypes/file"; -export type ModalWarningType = - | "errorType" - | "errorSize" - | "picture" - | "video" - | "audio" - | "document" - | null; +export type ModalWarningType = "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | null; type FileProps = { currentQuestion: QuizQuestionFile; @@ -27,22 +20,15 @@ type FileProps = { export const File = ({ currentQuestion }: FileProps) => { const theme = useTheme(); const answers = useQuizViewStore((state) => state.answers); - const [modalWarningType, setModalWarningType] = - useState(null); + const [modalWarningType, setModalWarningType] = useState(null); const [isSending, setIsSending] = useState(false); const isMobile = useRootContainerSize() < 500; - const answer = answers.find( - ({ questionId }) => questionId === currentQuestion.id - )?.answer as string; + const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string; return ( - + {currentQuestion.title} { }} > {answer?.split("|")[0] ? ( - + ) : ( { /> )} {answer && currentQuestion.content.type === "picture" && ( - + )} {answer && currentQuestion.content.type === "video" && ( - setModalWarningType(null)} - > + setModalWarningType(null)}> { case "errorType": return Выбран некорректный тип файла; case "errorSize": - return ( - Файл слишком большой. Максимальный размер 50 МБ - ); + return Файл слишком большой. Максимальный размер 50 МБ; default: return ( <> Допустимые расширения файлов: - - {ACCEPT_SEND_FILE_TYPES_MAP[status].join(" ")} - + {ACCEPT_SEND_FILE_TYPES_MAP[status].join(" ")} ); } diff --git a/lib/components/ViewPublicationPage/questions/Images/ImageVariant.tsx b/lib/components/ViewPublicationPage/questions/Images/ImageVariant.tsx index e30fc1a..9cca91f 100644 --- a/lib/components/ViewPublicationPage/questions/Images/ImageVariant.tsx +++ b/lib/components/ViewPublicationPage/questions/Images/ImageVariant.tsx @@ -21,21 +21,13 @@ type ImagesProps = { index: number; }; -export const ImageVariant = ({ - currentQuestion, - variant, - isSending, - setIsSending, - index, -}: ImagesProps) => { +export const ImageVariant = ({ currentQuestion, variant, isSending, setIsSending, index }: ImagesProps) => { const { quizId, preview } = useQuizData(); const { settings } = useQuizData(); const answers = useQuizViewStore((state) => state.answers); const { deleteAnswer, updateAnswer } = useQuizViewStore((state) => state); const theme = useTheme(); - const answer = answers.find( - ({ questionId }) => questionId === currentQuestion.id - )?.answer; + const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer; const onVariantClick = async (event: MouseEvent) => { event.preventDefault(); @@ -81,16 +73,14 @@ export const ImageVariant = ({ cursor: "pointer", borderRadius: "12px", border: `1px solid`, - borderColor: - answer === variant.id ? theme.palette.primary.main : "#9A9AAF", + borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF", "&:hover": { borderColor: theme.palette.primary.main }, background: settings.cfg.design && !quizThemes[settings.cfg.theme].isLight ? "rgba(255,255,255, 0.3)" - : (settings.cfg.design && quizThemes[settings.cfg.theme].isLight) || - quizThemes[settings.cfg.theme].isLight - ? "#FFFFFF" - : "transparent", + : (settings.cfg.design && quizThemes[settings.cfg.theme].isLight) || quizThemes[settings.cfg.theme].isLight + ? "#FFFFFF" + : "transparent", }} onClick={onVariantClick} > diff --git a/lib/components/ViewPublicationPage/questions/Images/index.tsx b/lib/components/ViewPublicationPage/questions/Images/index.tsx index 5ecefd9..9b3f236 100644 --- a/lib/components/ViewPublicationPage/questions/Images/index.tsx +++ b/lib/components/ViewPublicationPage/questions/Images/index.tsx @@ -16,26 +16,18 @@ export const Images = ({ currentQuestion }: ImagesProps) => { const [isSending, setIsSending] = useState(false); const answers = useQuizViewStore((state) => state.answers); const theme = useTheme(); - const answer = answers.find( - ({ questionId }) => questionId === currentQuestion.id - )?.answer; + const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer; const isTablet = useRootContainerSize() < 1000; const isMobile = useRootContainerSize() < 500; return ( - + {currentQuestion.title} answer === id - )} + value={currentQuestion.content.variants.findIndex(({ id }) => answer === id)} sx={{ display: "flex", flexWrap: "wrap", @@ -48,11 +40,7 @@ export const Images = ({ currentQuestion }: ImagesProps) => { sx={{ display: "grid", gap: "15px", - gridTemplateColumns: isTablet - ? isMobile - ? "repeat(1, 1fr)" - : "repeat(2, 1fr)" - : "repeat(3, 1fr)", + gridTemplateColumns: isTablet ? (isMobile ? "repeat(1, 1fr)" : "repeat(2, 1fr)") : "repeat(3, 1fr)", width: "100%", }} > diff --git a/lib/components/ViewPublicationPage/questions/Number/index.tsx b/lib/components/ViewPublicationPage/questions/Number/index.tsx index f1484ee..4bd47fa 100644 --- a/lib/components/ViewPublicationPage/questions/Number/index.tsx +++ b/lib/components/ViewPublicationPage/questions/Number/index.tsx @@ -27,29 +27,22 @@ export const Number = ({ currentQuestion }: NumberProps) => { const [maxRange, setMaxRange] = useState("100000000000"); const [reversedInputValue, setReversedInputValue] = useState("0"); const [reversedMinRange, setReversedMinRange] = useState("0"); - const [reversedMaxRange, setReversedMaxRange] = - useState("100000000000"); + const [reversedMaxRange, setReversedMaxRange] = useState("100000000000"); const { settings, quizId, preview } = useQuizData(); const { updateAnswer } = useQuizViewStore((state) => state); const answers = useQuizViewStore((state) => state.answers); const theme = useTheme(); - const [minBorder, maxBorder] = currentQuestion.content.range - .split("—") - .map(window.Number); + const [minBorder, maxBorder] = currentQuestion.content.range.split("—").map(window.Number); const min = minBorder < maxBorder ? minBorder : maxBorder; const max = minBorder < maxBorder ? maxBorder : minBorder; const reversed = minBorder > maxBorder; - const answer = answers.find( - ({ questionId }) => questionId === currentQuestion.id - )?.answer as string; + const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string; const sliderValue = answer || - (reversed - ? max + min - currentQuestion.content.start + "—" + max - : currentQuestion.content.start + "—" + max); + (reversed ? max + min - currentQuestion.content.start + "—" + max : currentQuestion.content.start + "—" + max); useEffect(() => { console.log("reversed:", reversed); @@ -81,15 +74,11 @@ export const Number = ({ currentQuestion }: NumberProps) => { window.Number(value) < window.Number(min) ? String(min) : window.Number(value) > window.Number(max) - ? String(max) - : value; + ? String(max) + : value; setReversedInputValue(newValue); - updateAnswer( - currentQuestion.id, - String(max + min - window.Number(newValue)), - 0 - ); + updateAnswer(currentQuestion.id, String(max + min - window.Number(newValue)), 0); await sendAnswerToBackend(String(window.Number(newValue)), true); return; @@ -99,107 +88,73 @@ export const Number = ({ currentQuestion }: NumberProps) => { window.Number(value) < window.Number(minRange) ? minRange : window.Number(value) > window.Number(maxRange) - ? maxRange - : value; + ? maxRange + : value; setInputValue(newValue); await sendAnswerToBackend(newValue); }, 1000); - const updateMinRangeDebounced = useDebouncedCallback( - async (value: string, crowded = false) => { - if (reversed) { - const newMinRange = crowded - ? window.Number(value.split("—")[1]) - : max + min - window.Number(value.split("—")[0]) < min + const updateMinRangeDebounced = useDebouncedCallback(async (value: string, crowded = false) => { + if (reversed) { + const newMinRange = crowded + ? window.Number(value.split("—")[1]) + : max + min - window.Number(value.split("—")[0]) < min ? min : max + min - window.Number(value.split("—")[0]); - const newMinValue = - window.Number(value.split("—")[0]) > max - ? String(max) - : value.split("—")[0]; + const newMinValue = window.Number(value.split("—")[0]) > max ? String(max) : value.split("—")[0]; - setReversedMinRange( - crowded ? String(max + min - window.Number(newMinValue)) : newMinValue - ); - updateAnswer( - currentQuestion.id, - `${newMinRange}—${value.split("—")[1]}`, - 0 - ); - await sendAnswerToBackend( - `${newMinValue}—${value.split("—")[1]}`, - true - ); + setReversedMinRange(crowded ? String(max + min - window.Number(newMinValue)) : newMinValue); + updateAnswer(currentQuestion.id, `${newMinRange}—${value.split("—")[1]}`, 0); + await sendAnswerToBackend(`${newMinValue}—${value.split("—")[1]}`, true); - return; - } + return; + } - const newMinValue = crowded - ? maxRange - : window.Number(value.split("—")[0]) < min + const newMinValue = crowded + ? maxRange + : window.Number(value.split("—")[0]) < min ? String(min) : value.split("—")[0]; - setMinRange(newMinValue); - await sendAnswerToBackend(`${newMinValue}—${value.split("—")[1]}`); - }, - 1000 - ); - - const updateMaxRangeDebounced = useDebouncedCallback( - async (value: string, crowded = false) => { - if (reversed) { - const newMaxRange = crowded - ? window.Number(value.split("—")[1]) - : max + min - window.Number(value.split("—")[1]) > max + setMinRange(newMinValue); + await sendAnswerToBackend(`${newMinValue}—${value.split("—")[1]}`); + }, 1000); + + const updateMaxRangeDebounced = useDebouncedCallback(async (value: string, crowded = false) => { + if (reversed) { + const newMaxRange = crowded + ? window.Number(value.split("—")[1]) + : max + min - window.Number(value.split("—")[1]) > max ? max : max + min - window.Number(value.split("—")[1]); - const newMaxValue = - window.Number(value.split("—")[1]) < min - ? String(min) - : value.split("—")[1]; + const newMaxValue = window.Number(value.split("—")[1]) < min ? String(min) : value.split("—")[1]; - setReversedMaxRange( - crowded ? String(max + min - window.Number(newMaxValue)) : newMaxValue - ); - updateAnswer( - currentQuestion.id, - `${value.split("—")[0]}—${newMaxRange}`, - 0 - ); - await sendAnswerToBackend( - `${value.split("—")[0]}—${newMaxValue}`, - true - ); + setReversedMaxRange(crowded ? String(max + min - window.Number(newMaxValue)) : newMaxValue); + updateAnswer(currentQuestion.id, `${value.split("—")[0]}—${newMaxRange}`, 0); + await sendAnswerToBackend(`${value.split("—")[0]}—${newMaxValue}`, true); - return; - } + return; + } - const newMaxValue = crowded - ? minRange - : window.Number(value.split("—")[1]) > max + const newMaxValue = crowded + ? minRange + : window.Number(value.split("—")[1]) > max ? String(max) : value.split("—")[1]; - setMaxRange(newMaxValue); - await sendAnswerToBackend(`${value.split("—")[0]}—${newMaxValue}`); - }, - 1000 - ); + setMaxRange(newMaxValue); + await sendAnswerToBackend(`${value.split("—")[0]}—${newMaxValue}`); + }, 1000); useEffect(() => { if (answer) { if (answer.includes("—")) { if (reversed) { - setReversedMinRange( - String(max + min - window.Number(answer.split("—")[0])) - ); - setReversedMaxRange( - String(max + min - window.Number(answer.split("—")[1])) - ); + setReversedMinRange(String(max + min - window.Number(answer.split("—")[0]))); + setReversedMaxRange(String(max + min - window.Number(answer.split("—")[1]))); } else { setMinRange(answer.split("—")[0]); setMaxRange(answer.split("—")[1]); @@ -228,17 +183,12 @@ export const Number = ({ currentQuestion }: NumberProps) => { }, []); const onSliderChange = (_: Event, value: number | number[]) => { - const range = Array.isArray(value) - ? `${value[0]}—${value[1]}` - : String(value); + const range = Array.isArray(value) ? `${value[0]}—${value[1]}` : String(value); updateAnswer(currentQuestion.id, range, 0); }; - const onChangeCommitted = async ( - _: Event | SyntheticEvent, - value: number | number[] - ) => { + const onChangeCommitted = async (_: Event | SyntheticEvent, value: number | number[]) => { if (currentQuestion.content.chooseRange && Array.isArray(value)) { if (reversed) { const newMinReversedValue = String(max + min - value[0]); @@ -248,10 +198,7 @@ export const Number = ({ currentQuestion }: NumberProps) => { setMaxRange(String(value[1])); setReversedMinRange(newMinReversedValue); setReversedMaxRange(newMaxReversedValue); - await sendAnswerToBackend( - `${newMinReversedValue}—${newMaxReversedValue}`, - true - ); + await sendAnswerToBackend(`${newMinReversedValue}—${newMaxReversedValue}`, true); return; } @@ -277,9 +224,7 @@ export const Number = ({ currentQuestion }: NumberProps) => { return value; } - const [minSliderBorder, maxSliderBorder] = sliderValue - .split("—") - .map(window.Number); + const [minSliderBorder, maxSliderBorder] = sliderValue.split("—").map(window.Number); if (value === minSliderBorder) { return max + min - minSliderBorder; @@ -313,9 +258,7 @@ export const Number = ({ currentQuestion }: NumberProps) => { return; } - updateMinRangeDebounced( - `${newValue}—${max + min - window.Number(reversedMaxRange)}` - ); + updateMinRangeDebounced(`${newValue}—${max + min - window.Number(reversedMaxRange)}`); return; } @@ -344,9 +287,7 @@ export const Number = ({ currentQuestion }: NumberProps) => { return; } - updateMaxRangeDebounced( - `${max + min - window.Number(reversedMinRange)}—${newValue}` - ); + updateMaxRangeDebounced(`${max + min - window.Number(reversedMinRange)}—${newValue}`); return; } @@ -364,11 +305,7 @@ export const Number = ({ currentQuestion }: NumberProps) => { return ( - + {currentQuestion.title} { "& .MuiOutlinedInput-root": { background: "transparent" }, "& .MuiInputBase-input": { textAlign: "center", zIndex: 1 }, "& .MuiOutlinedInput-notchedOutline": { - backgroundColor: quizThemes[settings.cfg.theme].isLight - ? "white" - : theme.palette.background.default, + backgroundColor: quizThemes[settings.cfg.theme].isLight ? "white" : theme.palette.background.default, borderColor: "#9A9AAF", }, }} @@ -445,9 +380,7 @@ export const Number = ({ currentQuestion }: NumberProps) => { "& .MuiOutlinedInput-root": { background: "transparent" }, "& .MuiInputBase-input": { textAlign: "center", zIndex: 1 }, "& .MuiOutlinedInput-notchedOutline": { - backgroundColor: quizThemes[settings.cfg.theme].isLight - ? "white" - : theme.palette.background.default, + backgroundColor: quizThemes[settings.cfg.theme].isLight ? "white" : theme.palette.background.default, borderColor: "#9A9AAF", }, }} @@ -462,9 +395,7 @@ export const Number = ({ currentQuestion }: NumberProps) => { "& .MuiOutlinedInput-root": { background: "transparent" }, "& .MuiInputBase-input": { textAlign: "center", zIndex: 1 }, "& .MuiOutlinedInput-notchedOutline": { - backgroundColor: quizThemes[settings.cfg.theme].isLight - ? "white" - : theme.palette.background.default, + backgroundColor: quizThemes[settings.cfg.theme].isLight ? "white" : theme.palette.background.default, borderColor: "#9A9AAF", }, }} diff --git a/lib/components/ViewPublicationPage/questions/Page/index.tsx b/lib/components/ViewPublicationPage/questions/Page/index.tsx index b27191e..48e1b97 100644 --- a/lib/components/ViewPublicationPage/questions/Page/index.tsx +++ b/lib/components/ViewPublicationPage/questions/Page/index.tsx @@ -23,10 +23,7 @@ export const Page = ({ currentQuestion }: PageProps) => { > {currentQuestion.title} - + {currentQuestion.content.text} ( - - ), + icon: (color: string, width: number) => , }, { name: "trophie", - icon: (color: string, width: number) => ( - - ), + icon: (color: string, width: number) => , }, { name: "flag", - icon: (color: string, width: number) => ( - - ), + icon: (color: string, width: number) => , }, { name: "heart", - icon: (color: string, width: number) => ( - - ), + icon: (color: string, width: number) => , }, { name: "like", - icon: (color: string, width: number) => ( - - ), + icon: (color: string, width: number) => , }, { name: "bubble", - icon: (color: string, width: number) => ( - - ), + icon: (color: string, width: number) => , }, { name: "hashtag", - icon: (color: string, width: number) => ( - - ), + icon: (color: string, width: number) => , }, ]; @@ -79,11 +60,8 @@ export const Rating = ({ currentQuestion }: RatingProps) => { const theme = useTheme(); const isMobile = useRootContainerSize() < 650; const isTablet = useRootContainerSize() < 750; - const { answer } = - answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; - const form = RATING_FORM_BUTTONS.find( - ({ name }) => name === currentQuestion.content.form - ); + const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; + const form = RATING_FORM_BUTTONS.find(({ name }) => name === currentQuestion.content.form); const sendRating = async (value: number | null) => { setIsSending(true); @@ -106,11 +84,7 @@ export const Rating = ({ currentQuestion }: RatingProps) => { return ( - + {currentQuestion.title} { "& .MuiRating-icon": { mr: isMobile ? undefined : "15px" }, }} max={currentQuestion.content.steps} - icon={form?.icon( - theme.palette.primary.main, - isMobile ? 30 : isTablet ? 40 : 50 - )} - emptyIcon={form?.icon( - "#9A9AAF", - isMobile ? 30 : isTablet ? 40 : 50 - )} + icon={form?.icon(theme.palette.primary.main, isMobile ? 30 : isTablet ? 40 : 50)} + emptyIcon={form?.icon("#9A9AAF", isMobile ? 30 : isTablet ? 40 : 50)} /> { width: "100%", }} > - - {currentQuestion.content.ratingNegativeDescription} - - - {currentQuestion.content.ratingPositiveDescription} - + {currentQuestion.content.ratingNegativeDescription} + {currentQuestion.content.ratingPositiveDescription} diff --git a/lib/components/ViewPublicationPage/questions/Select/index.tsx b/lib/components/ViewPublicationPage/questions/Select/index.tsx index 44c51c0..03e86a2 100644 --- a/lib/components/ViewPublicationPage/questions/Select/index.tsx +++ b/lib/components/ViewPublicationPage/questions/Select/index.tsx @@ -22,8 +22,7 @@ export const Select = ({ currentQuestion }: SelectProps) => { const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state); const answers = useQuizViewStore((state) => state.answers); const theme = useTheme(); - const { answer } = - answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; + const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; const sendSelectedAnswer = async (value: number) => { setIsSending(true); @@ -63,11 +62,7 @@ export const Select = ({ currentQuestion }: SelectProps) => { return ( - + {currentQuestion.title} { +export const TextNormal = ({ currentQuestion, answer, inputHC }: TextNormalProps) => { const { settings } = useQuizData(); const { updateAnswer } = useQuizViewStore((state) => state); const isMobile = useRootContainerSize() < 650; @@ -35,11 +31,7 @@ export const TextNormal = ({ return ( - + {currentQuestion.title} - {currentQuestion.content.back && - currentQuestion.content.back !== " " && ( - - - - )} + {currentQuestion.content.back && currentQuestion.content.back !== " " && ( + + + + )} ); diff --git a/lib/components/ViewPublicationPage/questions/Text/TextSpecial.tsx b/lib/components/ViewPublicationPage/questions/Text/TextSpecial.tsx index e228bb4..752f967 100644 --- a/lib/components/ViewPublicationPage/questions/Text/TextSpecial.tsx +++ b/lib/components/ViewPublicationPage/questions/Text/TextSpecial.tsx @@ -1,10 +1,4 @@ -import { - Box, - TextField as MuiTextField, - TextFieldProps, - Typography, - useTheme, -} from "@mui/material"; +import { Box, TextField as MuiTextField, TextFieldProps, Typography, useTheme } from "@mui/material"; import { Answer, useQuizViewStore } from "@stores/quizView"; import { useQuizData } from "@contexts/QuizDataContext"; @@ -51,12 +45,7 @@ interface TextSpecialProps { stepNumber?: number | null; } -export const TextSpecial = ({ - currentQuestion, - answer, - inputHC, - stepNumber, -}: TextSpecialProps) => { +export const TextSpecial = ({ currentQuestion, answer, inputHC, stepNumber }: TextSpecialProps) => { const { settings } = useQuizData(); const { updateAnswer } = useQuizViewStore((state) => state); const isHorizontal = ORIENTATION[Number(stepNumber) - 1].horizontal; @@ -86,25 +75,19 @@ export const TextSpecial = ({ gap: "20px", }} > - + {currentQuestion.title} - {isHorizontal && - currentQuestion.content.back && - currentQuestion.content.back !== " " && ( - - - - )} + {isHorizontal && currentQuestion.content.back && currentQuestion.content.back !== " " && ( + + + + )} { } - {!isHorizontal && - currentQuestion.content.back && - currentQuestion.content.back !== " " && ( - - - - )} + {!isHorizontal && currentQuestion.content.back && currentQuestion.content.back !== " " && ( + + + + )} ); }; diff --git a/lib/components/ViewPublicationPage/questions/Text/index.tsx b/lib/components/ViewPublicationPage/questions/Text/index.tsx index 47e16e3..dc5dc0b 100644 --- a/lib/components/ViewPublicationPage/questions/Text/index.tsx +++ b/lib/components/ViewPublicationPage/questions/Text/index.tsx @@ -21,12 +21,11 @@ export const Text = ({ currentQuestion, stepNumber }: TextProps) => { const { settings, preview } = useQuizData(); const { quizId } = useQuizData(); const answers = useQuizViewStore((state) => state.answers); - const { answer } = - answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; + const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; const inputHC = useDebouncedCallback(async (text) => { setIsSending(true); - + try { await sendAnswer({ questionId: currentQuestion.id, @@ -48,30 +47,13 @@ export const Text = ({ currentQuestion, stepNumber }: TextProps) => { switch (settings.cfg.spec) { case true: return ( - + ); case undefined: - return ( - - ); + return ; default: - return ( - - ); + return ; } }; diff --git a/lib/components/ViewPublicationPage/questions/Variant/VariantItem.tsx b/lib/components/ViewPublicationPage/questions/Variant/VariantItem.tsx index 9f48478..d50dc4e 100644 --- a/lib/components/ViewPublicationPage/questions/Variant/VariantItem.tsx +++ b/lib/components/ViewPublicationPage/questions/Variant/VariantItem.tsx @@ -1,11 +1,4 @@ -import { - Checkbox, - FormControlLabel, - TextField as MuiTextField, - Radio, - TextFieldProps, - useTheme, -} from "@mui/material"; +import { Checkbox, FormControlLabel, TextField as MuiTextField, Radio, TextFieldProps, useTheme } from "@mui/material"; import { enqueueSnackbar } from "notistack"; import { sendAnswer } from "@api/quizRelase"; @@ -96,9 +89,7 @@ export const VariantItem = ({ updateAnswer( currentQuestion.id, variantId, - answer === variantId - ? 0 - : currentQuestion.content.variants[index].points || 0 + answer === variantId ? 0 : currentQuestion.content.variants[index].points || 0 ); } catch (error) { console.error(error); @@ -133,15 +124,14 @@ export const VariantItem = ({ color: theme.palette.text.primary, padding: "15px", border: `1px solid`, - borderColor: - answer === variant.id ? theme.palette.primary.main : "#9A9AAF", + borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF", backgroundColor: settings.cfg.design ? quizThemes[settings.cfg.theme].isLight ? "#FFFFFF" : "rgba(255,255,255, 0.3)" : quizThemes[settings.cfg.theme].isLight - ? "white" - : theme.palette.background.default, + ? "white" + : theme.palette.background.default, display: "flex", maxWidth: "685px", maxHeight: "85px", @@ -167,16 +157,11 @@ export const VariantItem = ({ currentQuestion.content.multi ? ( - } + checkedIcon={} icon={} /> ) : ( - } - icon={} - /> + } icon={} /> ) } label={own ? : variant.answer} diff --git a/lib/components/ViewPublicationPage/questions/Variant/index.tsx b/lib/components/ViewPublicationPage/questions/Variant/index.tsx index c4dfed2..2796601 100644 --- a/lib/components/ViewPublicationPage/questions/Variant/index.tsx +++ b/lib/components/ViewPublicationPage/questions/Variant/index.tsx @@ -1,11 +1,5 @@ import { useEffect, useState } from "react"; -import { - Box, - FormGroup, - RadioGroup, - Typography, - useTheme, -} from "@mui/material"; +import { Box, FormGroup, RadioGroup, Typography, useTheme } from "@mui/material"; import { VariantItem } from "./VariantItem"; @@ -26,11 +20,8 @@ export const Variant = ({ currentQuestion }: VariantProps) => { const theme = useTheme(); const isMobile = useRootContainerSize() < 650; - const { answer } = - answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; - const ownVariant = ownVariants.find( - (variant) => variant.id === currentQuestion.id - ); + const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; + const ownVariant = ownVariants.find((variant) => variant.id === currentQuestion.id); const Group = currentQuestion.content.multi ? FormGroup : RadioGroup; @@ -44,11 +35,7 @@ export const Variant = ({ currentQuestion }: VariantProps) => { return ( - + {currentQuestion.title} { > answer === id - )} + value={currentQuestion.content.variants.findIndex(({ id }) => answer === id)} sx={{ display: "flex", flexWrap: "wrap", @@ -107,17 +92,16 @@ export const Variant = ({ currentQuestion }: VariantProps) => { )} - {currentQuestion.content.back && - currentQuestion.content.back !== " " && ( - - - - )} + {currentQuestion.content.back && currentQuestion.content.back !== " " && ( + + + + )} ); diff --git a/lib/components/ViewPublicationPage/questions/Varimg/VarimgVariant.tsx b/lib/components/ViewPublicationPage/questions/Varimg/VarimgVariant.tsx index c5ec48d..12dd016 100644 --- a/lib/components/ViewPublicationPage/questions/Varimg/VarimgVariant.tsx +++ b/lib/components/ViewPublicationPage/questions/Varimg/VarimgVariant.tsx @@ -23,21 +23,14 @@ type VarimgVariantProps = { setIsSending: (isSending: boolean) => void; }; -export const VarimgVariant = ({ - currentQuestion, - variant, - index, - isSending, - setIsSending, -}: VarimgVariantProps) => { +export const VarimgVariant = ({ currentQuestion, variant, index, isSending, setIsSending }: VarimgVariantProps) => { const { settings, quizId, preview } = useQuizData(); const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state); const answers = useQuizViewStore((state) => state.answers); const theme = useTheme(); - const { answer } = - answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; + const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; const sendVariant = async (event: MouseEvent) => { event.preventDefault(); @@ -93,11 +86,10 @@ export const VarimgVariant = ({ ? "#FFFFFF" : "rgba(255,255,255, 0.3)" : quizThemes[settings.cfg.theme].isLight - ? "white" - : theme.palette.background.default, + ? "white" + : theme.palette.background.default, border: `1px solid`, - borderColor: - answer === variant.id ? theme.palette.primary.main : "#9A9AAF", + borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF", display: "flex", margin: 0, justifyContent: "space-between", @@ -118,12 +110,7 @@ export const VarimgVariant = ({ value={index} onClick={sendVariant} label={variant.answer} - control={ - } - icon={} - /> - } + control={} icon={} />} /> ); }; diff --git a/lib/components/ViewPublicationPage/questions/Varimg/index.tsx b/lib/components/ViewPublicationPage/questions/Varimg/index.tsx index a186597..5e58738 100644 --- a/lib/components/ViewPublicationPage/questions/Varimg/index.tsx +++ b/lib/components/ViewPublicationPage/questions/Varimg/index.tsx @@ -21,19 +21,12 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => { const theme = useTheme(); const isMobile = useRootContainerSize() < 650; - const { answer } = - answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; - const variant = currentQuestion.content.variants.find( - ({ id }) => answer === id - ); + const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; + const variant = currentQuestion.content.variants.find(({ id }) => answer === id); return ( - + {currentQuestion.title} { > answer === id - )} + value={currentQuestion.content.variants.findIndex(({ id }) => answer === id)} sx={{ display: "flex", flexWrap: "wrap", @@ -116,8 +107,7 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => { style={{ width: "100%", height: "100%", objectFit: "cover" }} alt="" /> - ) : currentQuestion.content.replText !== " " && - currentQuestion.content.replText.length > 0 ? ( + ) : currentQuestion.content.replText !== " " && currentQuestion.content.replText.length > 0 ? ( currentQuestion.content.replText ) : variant?.extendedText || isMobile ? ( "Выберите вариант ответа ниже" diff --git a/lib/components/ViewPublicationPage/tools/NextButton.tsx b/lib/components/ViewPublicationPage/tools/NextButton.tsx index b5b310c..f951321 100644 --- a/lib/components/ViewPublicationPage/tools/NextButton.tsx +++ b/lib/components/ViewPublicationPage/tools/NextButton.tsx @@ -1,28 +1,29 @@ -import {Button, useTheme} from "@mui/material"; +import { Button, useTheme } from "@mui/material"; import { quizThemes } from "@utils/themes/Publication/themePublication"; import { useQuizData } from "@contexts/QuizDataContext"; -interface Props{ - isNextButtonEnabled: boolean, - moveToNextQuestion: () => void, +interface Props { + isNextButtonEnabled: boolean; + moveToNextQuestion: () => void; } -export default function NextButton ({isNextButtonEnabled, moveToNextQuestion}: Props) { - const theme = useTheme(); - const { settings } = useQuizData(); - return( - - )} +export default function NextButton({ isNextButtonEnabled, moveToNextQuestion }: Props) { + const theme = useTheme(); + const { settings } = useQuizData(); + return ( + + ); +} diff --git a/lib/components/ViewPublicationPage/tools/PrevButton.tsx b/lib/components/ViewPublicationPage/tools/PrevButton.tsx index 347c222..2ec4a5b 100644 --- a/lib/components/ViewPublicationPage/tools/PrevButton.tsx +++ b/lib/components/ViewPublicationPage/tools/PrevButton.tsx @@ -4,36 +4,38 @@ import { quizThemes } from "@utils/themes/Publication/themePublication"; import { useQuizData } from "@contexts/QuizDataContext"; interface Props { - isPreviousButtonEnabled: boolean, - moveToPrevQuestion: () => void, + isPreviousButtonEnabled: boolean; + moveToPrevQuestion: () => void; } export default function PrevButton({ isPreviousButtonEnabled, moveToPrevQuestion }: Props) { - const theme = useTheme(); - const { settings } = useQuizData(); - const isMobileMini = useRootContainerSize() < 382; - return ( - - ); + const theme = useTheme(); + const { settings } = useQuizData(); + const isMobileMini = useRootContainerSize() < 382; + return ( + + ); } diff --git a/lib/components/ViewPublicationPage/tools/Select.tsx b/lib/components/ViewPublicationPage/tools/Select.tsx index d12195b..f4b3f22 100644 --- a/lib/components/ViewPublicationPage/tools/Select.tsx +++ b/lib/components/ViewPublicationPage/tools/Select.tsx @@ -1,11 +1,5 @@ import { useState, useEffect } from "react"; -import { - Select as MuiSelect, - MenuItem, - FormControl, - Typography, - useTheme, -} from "@mui/material"; +import { Select as MuiSelect, MenuItem, FormControl, Typography, useTheme } from "@mui/material"; import ArrowDown from "@icons/ArrowDownIcon"; @@ -34,9 +28,7 @@ export const Select = ({ colorPlaceholder = "#9A9AAF", disabled = false, }: SelectProps) => { - const [activeItem, setActiveItem] = useState( - empty ? -1 : activeItemIndex - ); + const [activeItem, setActiveItem] = useState(empty ? -1 : activeItemIndex); const theme = useTheme(); useEffect(() => { @@ -58,22 +50,11 @@ export const Select = ({ }; return ( - + - value ? ( - items[Number(value)] - ) : ( - - {placeholder} - - ) + value ? items[Number(value)] : {placeholder} } id="display-select" variant="outlined" @@ -88,8 +69,8 @@ export const Select = ({ borderRadius: "10px", }, "& .MuiSelect-icon": { - color: theme.palette.primary.main - } + color: theme.palette.primary.main, + }, }} MenuProps={{ PaperProps: { diff --git a/lib/components/ViewPublicationPage/tools/checkEmptyData.ts b/lib/components/ViewPublicationPage/tools/checkEmptyData.ts index 1d6cbe2..d96066c 100644 --- a/lib/components/ViewPublicationPage/tools/checkEmptyData.ts +++ b/lib/components/ViewPublicationPage/tools/checkEmptyData.ts @@ -1,16 +1,17 @@ import { QuizQuestionResult } from "@model/questionTypes/result"; export const isResultQuestionEmpty = (resultQuestion: QuizQuestionResult) => { - if ( - (resultQuestion.title.length > 0 && resultQuestion.title !== " ") - || (resultQuestion.description.length > 0 && resultQuestion.description !== " ") - || (resultQuestion.content.back.length > 0 && resultQuestion.content.back !== " ") - || (resultQuestion.content.originalBack.length > 0 && resultQuestion.content.originalBack !== " ") - || (resultQuestion.content.innerName.length > 0 && resultQuestion.content.innerName !== " ") - || (resultQuestion.content.text.length > 0 && resultQuestion.content.text !== " ") - || (resultQuestion.content.video.length > 0 && resultQuestion.content.video !== " ") - || (resultQuestion.content.hint.text.length > 0 && resultQuestion.content.hint.text !== " ") - ) return false; + if ( + (resultQuestion.title.length > 0 && resultQuestion.title !== " ") || + (resultQuestion.description.length > 0 && resultQuestion.description !== " ") || + (resultQuestion.content.back.length > 0 && resultQuestion.content.back !== " ") || + (resultQuestion.content.originalBack.length > 0 && resultQuestion.content.originalBack !== " ") || + (resultQuestion.content.innerName.length > 0 && resultQuestion.content.innerName !== " ") || + (resultQuestion.content.text.length > 0 && resultQuestion.content.text !== " ") || + (resultQuestion.content.video.length > 0 && resultQuestion.content.video !== " ") || + (resultQuestion.content.hint.text.length > 0 && resultQuestion.content.hint.text !== " ") + ) + return false; - return true; + return true; }; diff --git a/lib/components/ViewPublicationPage/tools/fileUpload.ts b/lib/components/ViewPublicationPage/tools/fileUpload.ts index 972eda4..af24ccb 100644 --- a/lib/components/ViewPublicationPage/tools/fileUpload.ts +++ b/lib/components/ViewPublicationPage/tools/fileUpload.ts @@ -1,69 +1,23 @@ import { UploadFileType } from "@model/questionTypes/file"; - export const MAX_FILE_SIZE = 419430400; export const UPLOAD_FILE_DESCRIPTIONS_MAP = { - picture: { - title: "Добавить изображение", - description: "Принимает изображения", - }, - video: { - title: "Добавить видео", - description: "Принимает .mp4 и .mov формат — максимум 50мб", - }, - audio: { title: "Добавить аудиофайл", description: "Принимает аудиофайлы" }, - document: { title: "Добавить документ", description: "Принимает документы" }, -} as const satisfies Record; + picture: { + title: "Добавить изображение", + description: "Принимает изображения", + }, + video: { + title: "Добавить видео", + description: "Принимает .mp4 и .mov формат — максимум 50мб", + }, + audio: { title: "Добавить аудиофайл", description: "Принимает аудиофайлы" }, + document: { title: "Добавить документ", description: "Принимает документы" }, +} as const satisfies Record; export const ACCEPT_SEND_FILE_TYPES_MAP = { - picture: [ - ".jpeg", - ".jpg", - ".png", - ".ico", - ".gif", - ".tiff", - ".webp", - ".eps", - ".svg" - ], - video: [ - ".mp4", - ".mov", - ".wmv", - ".avi", - ".avchd", - ".flv", - ".f4v", - ".swf", - ".mkv", - ".webm", - ".mpeg-2" - ], - audio: [ - ".aac", - ".aiff", - ".dsd", - ".flac", - ".mp3", - ".mqa", - ".ogg", - ".wav", - ".wma" - ], - document: [ - ".doc", - ".docx", - ".dotx", - ".rtf", - ".odt", - ".pdf", - ".txt", - ".xls", - ".ppt", - ".xlsx", - ".pptx", - ".pages", - ], + picture: [".jpeg", ".jpg", ".png", ".ico", ".gif", ".tiff", ".webp", ".eps", ".svg"], + video: [".mp4", ".mov", ".wmv", ".avi", ".avchd", ".flv", ".f4v", ".swf", ".mkv", ".webm", ".mpeg-2"], + audio: [".aac", ".aiff", ".dsd", ".flac", ".mp3", ".mqa", ".ogg", ".wav", ".wma"], + document: [".doc", ".docx", ".dotx", ".rtf", ".odt", ".pdf", ".txt", ".xls", ".ppt", ".xlsx", ".pptx", ".pages"], } as const; diff --git a/lib/components/ViewPublicationPage/tools/replaceSpacesToEmptyLines.ts b/lib/components/ViewPublicationPage/tools/replaceSpacesToEmptyLines.ts index 534e03d..0376c75 100644 --- a/lib/components/ViewPublicationPage/tools/replaceSpacesToEmptyLines.ts +++ b/lib/components/ViewPublicationPage/tools/replaceSpacesToEmptyLines.ts @@ -11,11 +11,7 @@ export const replaceSpacesToEmptyLines = (object: T): T => { for (const [key, value] of Object.entries(object)) { if (typeof value === "string") { - - result[key] = value.replace( - "squiz.pena.digital", - "storage.yandexcloud.net" - ); + result[key] = value.replace("squiz.pena.digital", "storage.yandexcloud.net"); continue; } diff --git a/lib/contexts/QuizDataContext.ts b/lib/contexts/QuizDataContext.ts index 24b6d54..01b38d1 100644 --- a/lib/contexts/QuizDataContext.ts +++ b/lib/contexts/QuizDataContext.ts @@ -2,16 +2,16 @@ import { QuizSettings } from "@model/settingsData"; import { createContext, useContext } from "react"; type QuizData = QuizSettings & { - quizId: string; - preview: boolean; - changeFaviconAndTitle: boolean; + quizId: string; + preview: boolean; + changeFaviconAndTitle: boolean; }; export const QuizDataContext = createContext(null); export const useQuizData = () => { - const quizData = useContext(QuizDataContext); - if (quizData === null) throw new Error("QuizData context is null"); + const quizData = useContext(QuizDataContext); + if (quizData === null) throw new Error("QuizData context is null"); - return quizData; + return quizData; }; diff --git a/lib/contexts/RootContainerWidthContext.ts b/lib/contexts/RootContainerWidthContext.ts index e1987a7..7ea299f 100644 --- a/lib/contexts/RootContainerWidthContext.ts +++ b/lib/contexts/RootContainerWidthContext.ts @@ -1,11 +1,10 @@ import { createContext, useContext } from "react"; - export const RootContainerWidthContext = createContext(null); export const useRootContainerSize = () => { - const rootContainerSize = useContext(RootContainerWidthContext); - if (rootContainerSize === null) throw new Error("rootContainerSize context is null"); + const rootContainerSize = useContext(RootContainerWidthContext); + if (rootContainerSize === null) throw new Error("rootContainerSize context is null"); - return rootContainerSize; + return rootContainerSize; }; diff --git a/lib/model/api/getQuizData.ts b/lib/model/api/getQuizData.ts index 9c57bcc..21f685c 100644 --- a/lib/model/api/getQuizData.ts +++ b/lib/model/api/getQuizData.ts @@ -2,54 +2,54 @@ import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; import { QuizSettings } from "@model/settingsData"; export interface GetQuizDataResponse { - cnt: number; - settings: { - fp: boolean; - rep: boolean; - name: string; - cfg: string; - lim: number; - due: number; - delay: number; - pausable: boolean; - }; - items: { - id: number; - title: string; - desc: string; - typ: string; - req: boolean; - p: number; - c: string; - }[]; - show_badge: boolean; + cnt: number; + settings: { + fp: boolean; + rep: boolean; + name: string; + cfg: string; + lim: number; + due: number; + delay: number; + pausable: boolean; + }; + items: { + id: number; + title: string; + desc: string; + typ: string; + req: boolean; + p: number; + c: string; + }[]; + show_badge: boolean; } export function parseQuizData(quizDataResponse: GetQuizDataResponse): Omit { - const items: QuizSettings["questions"] = quizDataResponse.items.map((item) => { - const content = JSON.parse(item.c); + const items: QuizSettings["questions"] = quizDataResponse.items.map((item) => { + const content = JSON.parse(item.c); - return { - description: item.desc, - id: item.id, - page: item.p, - required: item.req, - title: item.title, - type: item.typ, - content - } as unknown as AnyTypedQuizQuestion; - }); + return { + description: item.desc, + id: item.id, + page: item.p, + required: item.req, + title: item.title, + type: item.typ, + content, + } as unknown as AnyTypedQuizQuestion; + }); - const settings: QuizSettings["settings"] = { - fp: quizDataResponse.settings.fp, - rep: quizDataResponse.settings.rep, - name: quizDataResponse.settings.name, - cfg: JSON.parse(quizDataResponse?.settings.cfg), - lim: quizDataResponse.settings.lim, - due: quizDataResponse.settings.due, - delay: quizDataResponse.settings.delay, - pausable: quizDataResponse.settings.pausable - }; + const settings: QuizSettings["settings"] = { + fp: quizDataResponse.settings.fp, + rep: quizDataResponse.settings.rep, + name: quizDataResponse.settings.name, + cfg: JSON.parse(quizDataResponse?.settings.cfg), + lim: quizDataResponse.settings.lim, + due: quizDataResponse.settings.due, + delay: quizDataResponse.settings.delay, + pausable: quizDataResponse.settings.pausable, + }; - return { cnt: quizDataResponse.cnt, settings, questions: items, show_badge: quizDataResponse.show_badge }; + return { cnt: quizDataResponse.cnt, settings, questions: items, show_badge: quizDataResponse.show_badge }; } diff --git a/lib/model/metrics.ts b/lib/model/metrics.ts index ce8260d..8acc1a5 100644 --- a/lib/model/metrics.ts +++ b/lib/model/metrics.ts @@ -1,8 +1 @@ -export type MetricsMessengers = - | "telegram" - | "viber" - | "whatsapp" - | "vkontakte" - | "messenger" - | "skype" - | "instagram"; +export type MetricsMessengers = "telegram" | "viber" | "whatsapp" | "vkontakte" | "messenger" | "skype" | "instagram"; diff --git a/lib/model/questionTypes/date.ts b/lib/model/questionTypes/date.ts index 651550e..291062b 100644 --- a/lib/model/questionTypes/date.ts +++ b/lib/model/questionTypes/date.ts @@ -1,8 +1,4 @@ -import type { - QuizQuestionBase, - QuestionHint, - QuestionBranchingRule, -} from "./shared"; +import type { QuizQuestionBase, QuestionHint, QuestionBranchingRule } from "./shared"; export interface QuizQuestionDate extends QuizQuestionBase { type: "date"; diff --git a/lib/model/questionTypes/emoji.ts b/lib/model/questionTypes/emoji.ts index 7fa34a1..96e0c9e 100644 --- a/lib/model/questionTypes/emoji.ts +++ b/lib/model/questionTypes/emoji.ts @@ -1,9 +1,4 @@ -import type { - QuizQuestionBase, - QuestionVariant, - QuestionHint, - QuestionBranchingRule, -} from "./shared"; +import type { QuizQuestionBase, QuestionVariant, QuestionHint, QuestionBranchingRule } from "./shared"; export interface QuizQuestionEmoji extends QuizQuestionBase { type: "emoji"; diff --git a/lib/model/questionTypes/file.ts b/lib/model/questionTypes/file.ts index 1346635..c1b3490 100644 --- a/lib/model/questionTypes/file.ts +++ b/lib/model/questionTypes/file.ts @@ -1,8 +1,4 @@ -import type { - QuizQuestionBase, - QuestionHint, - QuestionBranchingRule, -} from "./shared"; +import type { QuizQuestionBase, QuestionHint, QuestionBranchingRule } from "./shared"; export const UPLOAD_FILE_TYPES_MAP = { picture: "Изображения", diff --git a/lib/model/questionTypes/images.ts b/lib/model/questionTypes/images.ts index 7e0b71a..f310fc1 100644 --- a/lib/model/questionTypes/images.ts +++ b/lib/model/questionTypes/images.ts @@ -1,37 +1,32 @@ -import type { - QuestionHint, - QuestionVariant, - QuizQuestionBase, - QuestionBranchingRule, -} from "./shared"; +import type { QuestionHint, QuestionVariant, QuizQuestionBase, QuestionBranchingRule } from "./shared"; export interface QuizQuestionImages extends QuizQuestionBase { - type: "images"; - content: { - id: string; - /** Чекбокс "Вариант "свой ответ"" */ - own: boolean; - /** Чекбокс "Можно несколько" */ - multi: boolean; - /** Пропорции */ - xy: "1:1" | "1:2" | "2:1"; - /** Чекбокс "Внутреннее название вопроса" */ - innerNameCheck: boolean; - /** Поле "Внутреннее название вопроса" */ - innerName: string; - /** Чекбокс "Большие картинки" */ - large: boolean; - /** Форма */ - format: "carousel" | "masonry"; - /** Чекбокс "Необязательный вопрос" */ - required: boolean; - /** Варианты (картинки) */ - variants: QuestionVariant[]; - hint: QuestionHint; - rule: QuestionBranchingRule; - back: string | null; - originalBack: string | null; - autofill: boolean; - largeCheck: boolean; - }; + type: "images"; + content: { + id: string; + /** Чекбокс "Вариант "свой ответ"" */ + own: boolean; + /** Чекбокс "Можно несколько" */ + multi: boolean; + /** Пропорции */ + xy: "1:1" | "1:2" | "2:1"; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + /** Чекбокс "Большие картинки" */ + large: boolean; + /** Форма */ + format: "carousel" | "masonry"; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + /** Варианты (картинки) */ + variants: QuestionVariant[]; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string | null; + originalBack: string | null; + autofill: boolean; + largeCheck: boolean; + }; } diff --git a/lib/model/questionTypes/number.ts b/lib/model/questionTypes/number.ts index 530db03..ab22e25 100644 --- a/lib/model/questionTypes/number.ts +++ b/lib/model/questionTypes/number.ts @@ -1,8 +1,4 @@ -import type { - QuizQuestionBase, - QuestionHint, - QuestionBranchingRule, -} from "./shared"; +import type { QuizQuestionBase, QuestionHint, QuestionBranchingRule } from "./shared"; export interface QuizQuestionNumber extends QuizQuestionBase { type: "number"; diff --git a/lib/model/questionTypes/page.ts b/lib/model/questionTypes/page.ts index 272975e..37a9e3b 100644 --- a/lib/model/questionTypes/page.ts +++ b/lib/model/questionTypes/page.ts @@ -1,8 +1,4 @@ -import type { - QuizQuestionBase, - QuestionHint, - QuestionBranchingRule, -} from "./shared"; +import type { QuizQuestionBase, QuestionHint, QuestionBranchingRule } from "./shared"; export interface QuizQuestionPage extends QuizQuestionBase { type: "page"; diff --git a/lib/model/questionTypes/rating.ts b/lib/model/questionTypes/rating.ts index 2626077..01b0bc8 100644 --- a/lib/model/questionTypes/rating.ts +++ b/lib/model/questionTypes/rating.ts @@ -1,8 +1,4 @@ -import type { - QuizQuestionBase, - QuestionHint, - QuestionBranchingRule, -} from "./shared"; +import type { QuizQuestionBase, QuestionHint, QuestionBranchingRule } from "./shared"; export interface QuizQuestionRating extends QuizQuestionBase { type: "rating"; diff --git a/lib/model/questionTypes/result.ts b/lib/model/questionTypes/result.ts index e2eeb33..179410a 100644 --- a/lib/model/questionTypes/result.ts +++ b/lib/model/questionTypes/result.ts @@ -1,11 +1,7 @@ -import type { - QuizQuestionBase, - QuestionBranchingRule, - QuestionHint, -} from "./shared"; +import type { QuizQuestionBase, QuestionBranchingRule, QuestionHint } from "./shared"; interface ResultQuestionBranchingRule extends QuestionBranchingRule { - minScore?: number + minScore?: number; } export interface QuizQuestionResult extends QuizQuestionBase { type: "result"; @@ -18,9 +14,9 @@ export interface QuizQuestionResult extends QuizQuestionBase { text: string; price: [number] | [number, number]; useImage: boolean; - rule: ResultQuestionBranchingRule, + rule: ResultQuestionBranchingRule; hint: QuestionHint; autofill: boolean; - redirect: string + redirect: string; }; } diff --git a/lib/model/questionTypes/select.ts b/lib/model/questionTypes/select.ts index 3145b80..e2e0a85 100644 --- a/lib/model/questionTypes/select.ts +++ b/lib/model/questionTypes/select.ts @@ -1,9 +1,4 @@ -import type { - QuizQuestionBase, - QuestionVariant, - QuestionHint, - QuestionBranchingRule, -} from "./shared"; +import type { QuizQuestionBase, QuestionVariant, QuestionHint, QuestionBranchingRule } from "./shared"; export interface QuizQuestionSelect extends QuizQuestionBase { type: "select"; diff --git a/lib/model/questionTypes/shared.ts b/lib/model/questionTypes/shared.ts index 453a9be..067a120 100644 --- a/lib/model/questionTypes/shared.ts +++ b/lib/model/questionTypes/shared.ts @@ -13,115 +13,121 @@ import type { QuizQuestionVariant } from "./variant"; import type { QuizQuestionVarImg } from "./varimg"; export interface QuestionBranchingRuleMain { - next: string; - or: boolean; - rules: { - question: string; //id родителя (пока что) - answers: string[]; - }[]; + next: string; + or: boolean; + rules: { + question: string; //id родителя (пока что) + answers: string[]; + }[]; } export interface QuestionBranchingRule { - children: string[]; - //список условий - main: QuestionBranchingRuleMain[]; - parentId: string | null | "root"; - default: string; + children: string[]; + //список условий + main: QuestionBranchingRuleMain[]; + parentId: string | null | "root"; + default: string; } export interface QuestionHint { - /** Текст подсказки */ - text: string; - /** URL видео подсказки */ - video: string; + /** Текст подсказки */ + text: string; + /** URL видео подсказки */ + video: string; } export type QuestionVariant = { - id: string; - /** Текст */ - answer: string; - /** Текст подсказки */ - hints: string; - /** Дополнительное поле для текста, emoji, ссылки на картинку */ - extendedText: string; - /** Оригинал изображения (до кропа) */ - originalImageUrl: string; - points?: number; + id: string; + /** Текст */ + answer: string; + /** Текст подсказки */ + hints: string; + /** Дополнительное поле для текста, emoji, ссылки на картинку */ + extendedText: string; + /** Оригинал изображения (до кропа) */ + originalImageUrl: string; + points?: number; }; export type QuestionType = - | "variant" - | "images" - | "varimg" - | "emoji" - | "text" - | "select" - | "date" - | "number" - | "file" - | "page" - | "rating" - | "result"; + | "variant" + | "images" + | "varimg" + | "emoji" + | "text" + | "select" + | "date" + | "number" + | "file" + | "page" + | "rating" + | "result"; export interface QuizQuestionBase { - /** Stable id, generated on client */ + /** Stable id, generated on client */ + id: string; + quizId: number; + title: string; + description: string; + page: number; + type?: QuestionType | null; + expanded: boolean; + openedModalSettings: boolean; + deleted: boolean; + required: boolean; + deleteTimeoutId: number; + content: { id: string; - quizId: number; - title: string; - description: string; - page: number; - type?: QuestionType | null; - expanded: boolean; - openedModalSettings: boolean; - deleted: boolean; - required: boolean; - deleteTimeoutId: number; - content: { - id: string; - hint: QuestionHint; - rule: QuestionBranchingRule; - back: string | null; - originalBack: string | null; - autofill: boolean; - }; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string | null; + originalBack: string | null; + autofill: boolean; + }; } export type AnyTypedQuizQuestion = - | QuizQuestionVariant - | QuizQuestionImages - | QuizQuestionVarImg - | QuizQuestionEmoji - | QuizQuestionText - | QuizQuestionSelect - | QuizQuestionDate - | QuizQuestionNumber - | QuizQuestionFile - | QuizQuestionPage - | QuizQuestionRating - | QuizQuestionResult; + | QuizQuestionVariant + | QuizQuestionImages + | QuizQuestionVarImg + | QuizQuestionEmoji + | QuizQuestionText + | QuizQuestionSelect + | QuizQuestionDate + | QuizQuestionNumber + | QuizQuestionFile + | QuizQuestionPage + | QuizQuestionRating + | QuizQuestionResult; export type RealTypedQuizQuestion = Exclude; type FilterQuestionsWithVariants = T extends { - content: { variants: QuestionVariant[]; }; -} ? T : never; + content: { variants: QuestionVariant[] }; +} + ? T + : never; export type QuizQuestionsWithVariants = FilterQuestionsWithVariants; - -export const createBranchingRuleMain: (targetId: string, parentId: string) => QuestionBranchingRuleMain = (targetId, parentId) => ({ - next: targetId, - or: false, - rules: [{ - question: parentId, - answers: [] as string[], - }] +export const createBranchingRuleMain: (targetId: string, parentId: string) => QuestionBranchingRuleMain = ( + targetId, + parentId +) => ({ + next: targetId, + or: false, + rules: [ + { + question: parentId, + answers: [] as string[], + }, + ], }); export const createQuestionVariant: () => QuestionVariant = () => ({ - id: nanoid(), - answer: "", - extendedText: "", - hints: "", - originalImageUrl: "", + id: nanoid(), + answer: "", + extendedText: "", + hints: "", + originalImageUrl: "", }); diff --git a/lib/model/questionTypes/text.ts b/lib/model/questionTypes/text.ts index 610bad5..5686852 100644 --- a/lib/model/questionTypes/text.ts +++ b/lib/model/questionTypes/text.ts @@ -1,8 +1,4 @@ -import type { - QuizQuestionBase, - QuestionHint, - QuestionBranchingRule, -} from "./shared"; +import type { QuizQuestionBase, QuestionHint, QuestionBranchingRule } from "./shared"; export interface QuizQuestionText extends QuizQuestionBase { type: "text"; diff --git a/lib/model/questionTypes/variant.ts b/lib/model/questionTypes/variant.ts index 54f457d..de0a167 100644 --- a/lib/model/questionTypes/variant.ts +++ b/lib/model/questionTypes/variant.ts @@ -1,9 +1,4 @@ -import type { - QuizQuestionBase, - QuestionVariant, - QuestionHint, - QuestionBranchingRule, -} from "./shared"; +import type { QuizQuestionBase, QuestionVariant, QuestionHint, QuestionBranchingRule } from "./shared"; export interface QuizQuestionVariant extends QuizQuestionBase { type: "variant"; diff --git a/lib/model/questionTypes/varimg.ts b/lib/model/questionTypes/varimg.ts index 6d274d3..c850390 100644 --- a/lib/model/questionTypes/varimg.ts +++ b/lib/model/questionTypes/varimg.ts @@ -1,29 +1,24 @@ -import type { - QuestionHint, - QuestionVariant, - QuizQuestionBase, - QuestionBranchingRule, -} from "./shared"; +import type { QuestionHint, QuestionVariant, QuizQuestionBase, QuestionBranchingRule } from "./shared"; export interface QuizQuestionVarImg extends QuizQuestionBase { - type: "varimg"; - content: { - id: string; - /** Чекбокс "Вариант "свой ответ"" */ - own: boolean; - /** Чекбокс "Внутреннее название вопроса" */ - innerNameCheck: boolean; - /** Поле "Внутреннее название вопроса" */ - innerName: string; - /** Чекбокс "Необязательный вопрос" */ - required: boolean; - variants: QuestionVariant[]; - hint: QuestionHint; - rule: QuestionBranchingRule; - back: string; - originalBack: string; - autofill: boolean; - largeCheck: boolean; - replText: string; - }; + type: "varimg"; + content: { + id: string; + /** Чекбокс "Вариант "свой ответ"" */ + own: boolean; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + variants: QuestionVariant[]; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + autofill: boolean; + largeCheck: boolean; + replText: string; + }; } diff --git a/lib/model/settingsData.ts b/lib/model/settingsData.ts index 32de93a..74864a5 100644 --- a/lib/model/settingsData.ts +++ b/lib/model/settingsData.ts @@ -1,4 +1,4 @@ -import {AnyTypedQuizQuestion} from "./questionTypes/shared"; +import { AnyTypedQuizQuestion } from "./questionTypes/shared"; export type QuizStartpageType = "standard" | "expanded" | "centered" | null; @@ -50,7 +50,7 @@ export type QuizSettingsConfig = { delay: number; pausable: boolean; cfg: QuizConfig; -} +}; export type QuizSettings = { questions: AnyTypedQuizQuestion[]; @@ -114,12 +114,7 @@ export interface QuizConfig { vkMetricsNumber: number | undefined; } -export type FormContactFieldName = - | "name" - | "email" - | "phone" - | "text" - | "address"; +export type FormContactFieldName = "name" | "email" | "phone" | "text" | "address"; export type FormContactFieldData = { text: string; diff --git a/lib/model/widget/banner.ts b/lib/model/widget/banner.ts index 4929416..7984bdf 100644 --- a/lib/model/widget/banner.ts +++ b/lib/model/widget/banner.ts @@ -1,28 +1,28 @@ export interface BannerWidgetComponentProps { - quizId: string; - position: "topleft" | "topright" | "bottomleft" | "bottomright"; - onWidgetClose?: () => void; - dialogDimensions?: { width: string; height: string; }; - appealText?: string; - quizHeaderText?: string; - buttonTextColor?: string; - buttonBackgroundColor?: string; - /** - * Показывать виджет через X секунд - */ - autoShowWidgetTime?: number; - /** - * Открыть квиз через X секунд, 0 - сразу, null - не открывать - */ - autoShowQuizTime?: number | null; - openOnLeaveAttempt?: boolean; - buttonFlash?: boolean; - hideOnMobile?: boolean; - withShadow?: boolean; - rounded?: boolean; - bannerFullWidth?: boolean; - pulsation?: boolean; - fullScreen?: boolean; + quizId: string; + position: "topleft" | "topright" | "bottomleft" | "bottomright"; + onWidgetClose?: () => void; + dialogDimensions?: { width: string; height: string }; + appealText?: string; + quizHeaderText?: string; + buttonTextColor?: string; + buttonBackgroundColor?: string; + /** + * Показывать виджет через X секунд + */ + autoShowWidgetTime?: number; + /** + * Открыть квиз через X секунд, 0 - сразу, null - не открывать + */ + autoShowQuizTime?: number | null; + openOnLeaveAttempt?: boolean; + buttonFlash?: boolean; + hideOnMobile?: boolean; + withShadow?: boolean; + rounded?: boolean; + bannerFullWidth?: boolean; + pulsation?: boolean; + fullScreen?: boolean; } export type BannerWidgetParams = Omit; diff --git a/lib/model/widget/button.ts b/lib/model/widget/button.ts index 5e72ec6..0be2a27 100644 --- a/lib/model/widget/button.ts +++ b/lib/model/widget/button.ts @@ -1,30 +1,30 @@ export interface ButtonWidgetComponentProps { - quizId: string; - fixedSide?: "left" | "right"; - dialogDimensions?: { width: string; height: string; }; - /** - * Открыть квиз через X секунд, 0 - сразу, null - не открывать - */ - autoShowQuizTime?: number | null; - hideOnMobile?: boolean; - openOnLeaveAttempt?: boolean; - buttonFlash?: boolean; - withShadow?: boolean; - rounded?: boolean; - buttonText?: string; - buttonTextColor?: string; - buttonBackgroundColor?: string; - fullScreen?: boolean; + quizId: string; + fixedSide?: "left" | "right"; + dialogDimensions?: { width: string; height: string }; + /** + * Открыть квиз через X секунд, 0 - сразу, null - не открывать + */ + autoShowQuizTime?: number | null; + hideOnMobile?: boolean; + openOnLeaveAttempt?: boolean; + buttonFlash?: boolean; + withShadow?: boolean; + rounded?: boolean; + buttonText?: string; + buttonTextColor?: string; + buttonBackgroundColor?: string; + fullScreen?: boolean; } export type ButtonWidgetParams = Omit & { - selector: string; - /** - * In seconds, null - polling disabled - */ - selectorPollingTimeLimit?: number | null; + selector: string; + /** + * In seconds, null - polling disabled + */ + selectorPollingTimeLimit?: number | null; }; export type ButtonWidgetFixedParams = Omit & { - fixedSide: "left" | "right"; + fixedSide: "left" | "right"; }; diff --git a/lib/model/widget/container.ts b/lib/model/widget/container.ts index 759e5fe..df195f0 100644 --- a/lib/model/widget/container.ts +++ b/lib/model/widget/container.ts @@ -1,15 +1,15 @@ import { ButtonWidgetComponentProps } from "./button"; export type ContainerWidgetComponentProps = ButtonWidgetComponentProps & { - quizId: string; - showButtonOnMobile?: boolean; - dimensions?: { width: string; height: string; }; + quizId: string; + showButtonOnMobile?: boolean; + dimensions?: { width: string; height: string }; }; export type ContainerWidgetParams = ContainerWidgetComponentProps & { - selector: string; - /** - * In seconds, null - polling disabled - */ - selectorPollingTimeLimit?: number | null; + selector: string; + /** + * In seconds, null - polling disabled + */ + selectorPollingTimeLimit?: number | null; }; diff --git a/lib/model/widget/popup.ts b/lib/model/widget/popup.ts index 27cf9eb..c481f61 100644 --- a/lib/model/widget/popup.ts +++ b/lib/model/widget/popup.ts @@ -1,13 +1,13 @@ export interface PopupWidgetComponentProps { - quizId: string; - dialogDimensions?: { width: string; height: string; }; - /** - * Открыть квиз через X секунд, 0 - сразу, null - не открывать - */ - autoShowQuizTime?: number | null; - hideOnMobile?: boolean; - openOnLeaveAttempt?: boolean; - fullScreen?: boolean; + quizId: string; + dialogDimensions?: { width: string; height: string }; + /** + * Открыть квиз через X секунд, 0 - сразу, null - не открывать + */ + autoShowQuizTime?: number | null; + hideOnMobile?: boolean; + openOnLeaveAttempt?: boolean; + fullScreen?: boolean; } export type PopupWidgetParams = PopupWidgetComponentProps; diff --git a/lib/model/widget/side.ts b/lib/model/widget/side.ts index 16e9e79..1fa531b 100644 --- a/lib/model/widget/side.ts +++ b/lib/model/widget/side.ts @@ -1,20 +1,20 @@ export interface SideWidgetComponentProps { - quizId: string; - position: "left" | "right"; - buttonBackgroundColor?: string; - buttonTextColor?: string; - dialogDimensions?: { width: string; height: string; }; - fullScreen?: boolean; - buttonFlash?: boolean; - /** - * Показывать виджет через X секунд - */ - autoShowWidgetTime?: number; - /** - * Открыть квиз через X секунд, 0 - сразу, null - не открывать - */ - autoShowQuizTime?: number | null; - hideOnMobile?: boolean; + quizId: string; + position: "left" | "right"; + buttonBackgroundColor?: string; + buttonTextColor?: string; + dialogDimensions?: { width: string; height: string }; + fullScreen?: boolean; + buttonFlash?: boolean; + /** + * Показывать виджет через X секунд + */ + autoShowWidgetTime?: number; + /** + * Открыть квиз через X секунд, 0 - сразу, null - не открывать + */ + autoShowQuizTime?: number | null; + hideOnMobile?: boolean; } export type SideWidgetParams = SideWidgetComponentProps; diff --git a/lib/stores/quizView.ts b/lib/stores/quizView.ts index 07f87db..d614b1f 100644 --- a/lib/stores/quizView.ts +++ b/lib/stores/quizView.ts @@ -9,96 +9,95 @@ import { immer } from "zustand/middleware/immer"; export type Answer = string | string[] | Moment; type QuestionAnswer = { - questionId: string; - answer: Answer + questionId: string; + answer: Answer; }; type OwnVariant = { - id: string; - variant: QuestionVariant; + id: string; + variant: QuestionVariant; }; interface QuizViewStore { - answers: QuestionAnswer[]; - ownVariants: OwnVariant[]; - pointsSum: number; - points: Record; - currentQuizStep: QuizStep; + answers: QuestionAnswer[]; + ownVariants: OwnVariant[]; + pointsSum: number; + points: Record; + currentQuizStep: QuizStep; } interface QuizViewActions { - updateAnswer: (questionId: string, answer: string | string[] | Moment, points: number) => void; - deleteAnswer: (questionId: string) => void; - updateOwnVariant: (id: string, answer: string) => void; - deleteOwnVariant: (id: string) => void; - setCurrentQuizStep: (step: QuizStep) => void; + updateAnswer: (questionId: string, answer: string | string[] | Moment, points: number) => void; + deleteAnswer: (questionId: string) => void; + updateOwnVariant: (id: string, answer: string) => void; + deleteOwnVariant: (id: string) => void; + setCurrentQuizStep: (step: QuizStep) => void; } export const QuizViewContext = createContext | null>(null); export function useQuizViewStore(selector: (state: QuizViewStore & QuizViewActions) => U): U { - const store = useContext(QuizViewContext); - if (!store) throw new Error("QuizViewStore context is null"); + const store = useContext(QuizViewContext); + if (!store) throw new Error("QuizViewStore context is null"); - return useStore(store, selector); + return useStore(store, selector); } -export const createQuizViewStore = () => createStore()( - immer( - (set, get) => ({ - answers: [], - ownVariants: [], - points: {}, - pointsSum: 0, - currentQuizStep: "startpage", - updateAnswer(questionId, answer, points) { - set(state => { - const index = state.answers.findIndex(answer => questionId === answer.questionId); +export const createQuizViewStore = () => + createStore()( + immer((set, get) => ({ + answers: [], + ownVariants: [], + points: {}, + pointsSum: 0, + currentQuizStep: "startpage", + updateAnswer(questionId, answer, points) { + set((state) => { + const index = state.answers.findIndex((answer) => questionId === answer.questionId); - if (index < 0) { - state.answers.push({ questionId, answer }); - } else { - state.answers[index] = { questionId, answer }; - } + if (index < 0) { + state.answers.push({ questionId, answer }); + } else { + state.answers[index] = { questionId, answer }; + } - state.points = { ...state.points, ...{ [questionId]: points } }; + state.points = { ...state.points, ...{ [questionId]: points } }; - state.pointsSum = Object.values(state.points).reduce((sum, value) => sum + value); - }); - }, - deleteAnswer(questionId) { - set(state => { - state.answers = state.answers.filter(answer => questionId !== answer.questionId); - }); - }, - updateOwnVariant(id, answer) { - set(state => { - const index = state.ownVariants.findIndex((variant) => variant.id === id); + state.pointsSum = Object.values(state.points).reduce((sum, value) => sum + value); + }); + }, + deleteAnswer(questionId) { + set((state) => { + state.answers = state.answers.filter((answer) => questionId !== answer.questionId); + }); + }, + updateOwnVariant(id, answer) { + set((state) => { + const index = state.ownVariants.findIndex((variant) => variant.id === id); - if (index < 0) { - state.ownVariants.push({ - id, - variant: { - id: nanoid(), - answer, - extendedText: "", - hints: "", - originalImageUrl: "", - }, - }); - } else { - state.ownVariants[index].variant.answer = answer; - } - }); - }, - deleteOwnVariant(id) { - set(state => { - state.ownVariants = state.ownVariants.filter((variant) => variant.id !== id); - }); - }, - setCurrentQuizStep(step) { - set({ currentQuizStep: step }); - }, - }) - ) -); + if (index < 0) { + state.ownVariants.push({ + id, + variant: { + id: nanoid(), + answer, + extendedText: "", + hints: "", + originalImageUrl: "", + }, + }); + } else { + state.ownVariants[index].variant.answer = answer; + } + }); + }, + deleteOwnVariant(id) { + set((state) => { + state.ownVariants = state.ownVariants.filter((variant) => variant.id !== id); + }); + }, + setCurrentQuizStep(step) { + set({ currentQuizStep: step }); + }, + })) + ); diff --git a/lib/ui_kit/CustomCheckbox.tsx b/lib/ui_kit/CustomCheckbox.tsx index 1937936..6b3e9db 100644 --- a/lib/ui_kit/CustomCheckbox.tsx +++ b/lib/ui_kit/CustomCheckbox.tsx @@ -15,7 +15,6 @@ interface Props { } export default function CustomCheckbox({ label, handleChange, checked, sx, dataCy, colorIcon }: Props) { - return ( ; onChange?: (_: Event, value: number | number[]) => void; - onChangeCommitted?: ( - _: React.SyntheticEvent | Event, - value: number | number[] - ) => void; + onChangeCommitted?: (_: React.SyntheticEvent | Event, value: number | number[]) => void; valueLabelFormat?: (value: number, index: number) => ReactNode; }; diff --git a/lib/ui_kit/CustomTextField.tsx b/lib/ui_kit/CustomTextField.tsx index 5252397..cee4440 100644 --- a/lib/ui_kit/CustomTextField.tsx +++ b/lib/ui_kit/CustomTextField.tsx @@ -2,71 +2,70 @@ import { FormControl, TextField as MuiTextField, SxProps, Theme, useTheme } from import type { InputProps, TextFieldProps } from "@mui/material"; import type { ChangeEvent, FC, FocusEvent, KeyboardEvent } from "react"; -import {Answer} from "@stores/quizView.ts"; - +import { Answer } from "@stores/quizView.ts"; const TextField = MuiTextField as unknown as FC; // temporary fix ts(2590) interface CustomTextFieldProps { - placeholder: string; - value?: Answer; - error?: string; - onChange?: (event: ChangeEvent) => void; - onKeyDown?: (event: KeyboardEvent) => void; - onBlur?: (event: FocusEvent) => void; - text?: string; - sx?: SxProps; - InputProps?: Partial; + placeholder: string; + value?: Answer; + error?: string; + onChange?: (event: ChangeEvent) => void; + onKeyDown?: (event: KeyboardEvent) => void; + onBlur?: (event: FocusEvent) => void; + text?: string; + sx?: SxProps; + InputProps?: Partial; } export default function CustomTextField({ - placeholder, - value, - text, - sx, - error, - onChange, - onKeyDown, - onBlur, - InputProps, + placeholder, + value, + text, + sx, + error, + onChange, + onKeyDown, + onBlur, + InputProps, }: CustomTextFieldProps) { - const theme = useTheme(); + const theme = useTheme(); - return ( - - - - ); + return ( + + + + ); } diff --git a/lib/ui_kit/LoadingSkeleton.tsx b/lib/ui_kit/LoadingSkeleton.tsx index 813df2f..e3a8613 100644 --- a/lib/ui_kit/LoadingSkeleton.tsx +++ b/lib/ui_kit/LoadingSkeleton.tsx @@ -1,17 +1,15 @@ import { Skeleton } from "@mui/material"; - export default function LoadingSkeleton() { - - return ( - - ); + return ( + + ); } diff --git a/lib/ui_kit/RadioCheck.tsx b/lib/ui_kit/RadioCheck.tsx index f480219..def9e66 100644 --- a/lib/ui_kit/RadioCheck.tsx +++ b/lib/ui_kit/RadioCheck.tsx @@ -1,26 +1,25 @@ import { Box, useTheme } from "@mui/material"; interface Props { - color?: string; + color?: string; } export default function RadioCheck({ color = "#7E2AEA" }: Props) { - const theme = useTheme(); + const theme = useTheme(); - return ( - - - - - - - ); -} \ No newline at end of file + return ( + + + + + + + ); +} diff --git a/lib/ui_kit/RadioIcon.tsx b/lib/ui_kit/RadioIcon.tsx index f406676..aead065 100644 --- a/lib/ui_kit/RadioIcon.tsx +++ b/lib/ui_kit/RadioIcon.tsx @@ -1,23 +1,21 @@ import { Box, useTheme } from "@mui/material"; - export default function RadioIcon() { - const theme = useTheme(); + const theme = useTheme(); - return ( - - - - - - ); -} \ No newline at end of file + return ( + + + + + + ); +} diff --git a/lib/utils/defineDomain.ts b/lib/utils/defineDomain.ts index 14095f1..b331d81 100644 --- a/lib/utils/defineDomain.ts +++ b/lib/utils/defineDomain.ts @@ -6,11 +6,12 @@ let domain = "https://hbpn.link"; const currentDomain = location.hostname; if ( - currentDomain === "s.hbpn.link" - //Исключение - туризм. Он на стейджинговом квизе и на чужом для публикации домене - || currentDomain === "tourism.pena.digital" - || currentDomain.includes("localhost") - || currentDomain.includes("127.0.0.1") -) domain = "https://s.hbpn.link"; + currentDomain === "s.hbpn.link" || + //Исключение - туризм. Он на стейджинговом квизе и на чужом для публикации домене + currentDomain === "tourism.pena.digital" || + currentDomain.includes("localhost") || + currentDomain.includes("127.0.0.1") +) + domain = "https://s.hbpn.link"; export { domain }; diff --git a/lib/utils/designList.ts b/lib/utils/designList.ts index 87d63be..17ef329 100644 --- a/lib/utils/designList.ts +++ b/lib/utils/designList.ts @@ -1,27 +1,26 @@ import type { QuizTheme } from "@model/settingsData"; import { domain } from "./defineDomain"; - export const DESIGN_LIST: Record = { - Design1: `${domain}/designs/design1.jpg`, - Design2: `${domain}/designs/design2.jpg`, - Design3: `${domain}/designs/design3.jpg`, - Design4: `${domain}/designs/design4.jpg`, - Design5: `${domain}/designs/design5.jpg`, - Design6: `${domain}/designs/design6.jpg`, - Design7: `${domain}/designs/design7.jpg`, - Design8: `${domain}/designs/design8.jpg`, - Design9: `${domain}/designs/design9.jpg`, - Design10: `${domain}/designs/design10.jpg`, - StandardTheme: ``, - StandardDarkTheme: ``, - PinkTheme: ``, - PinkDarkTheme: ``, - BlackWhiteTheme: ``, - OliveTheme: ``, - YellowTheme: ``, - GoldDarkTheme: ``, - PurpleTheme: ``, - BlueTheme: ``, - BlueDarkTheme: ``, + Design1: `${domain}/designs/design1.jpg`, + Design2: `${domain}/designs/design2.jpg`, + Design3: `${domain}/designs/design3.jpg`, + Design4: `${domain}/designs/design4.jpg`, + Design5: `${domain}/designs/design5.jpg`, + Design6: `${domain}/designs/design6.jpg`, + Design7: `${domain}/designs/design7.jpg`, + Design8: `${domain}/designs/design8.jpg`, + Design9: `${domain}/designs/design9.jpg`, + Design10: `${domain}/designs/design10.jpg`, + StandardTheme: ``, + StandardDarkTheme: ``, + PinkTheme: ``, + PinkDarkTheme: ``, + BlackWhiteTheme: ``, + OliveTheme: ``, + YellowTheme: ``, + GoldDarkTheme: ``, + PurpleTheme: ``, + BlueTheme: ``, + BlueDarkTheme: ``, }; diff --git a/lib/utils/emailRegexp.ts b/lib/utils/emailRegexp.ts index 35b859f..969c636 100644 --- a/lib/utils/emailRegexp.ts +++ b/lib/utils/emailRegexp.ts @@ -1,2 +1,2 @@ export const EMAIL_REGEXP = - /^(([^<>()[\].,:\s@"]+(\.[^<>()[\].,:\s@"]+)*)|(".+"))@(([^<>()[\].,:\s@"]+\.)+[^<>()[\].,:\s@"]{2,})$/iu; + /^(([^<>()[\].,:\s@"]+(\.[^<>()[\].,:\s@"]+)*)|(".+"))@(([^<>()[\].,:\s@"]+\.)+[^<>()[\].,:\s@"]{2,})$/iu; diff --git a/lib/utils/handleComponentError.ts b/lib/utils/handleComponentError.ts index 8d21fd7..b914b51 100644 --- a/lib/utils/handleComponentError.ts +++ b/lib/utils/handleComponentError.ts @@ -1,43 +1,42 @@ import { ErrorInfo } from "react"; - interface ComponentError { - timestamp: number; - message: string; - callStack: string | undefined; - componentStack: string | null | undefined; + timestamp: number; + message: string; + callStack: string | undefined; + componentStack: string | null | undefined; } export function handleComponentError(error: Error, info: ErrorInfo) { - const componentError: ComponentError = { - timestamp: Math.floor(Date.now() / 1000), - message: error.message, - callStack: error.stack, - componentStack: info.componentStack, - }; + const componentError: ComponentError = { + timestamp: Math.floor(Date.now() / 1000), + message: error.message, + callStack: error.stack, + componentStack: info.componentStack, + }; - queueErrorRequest(componentError); + queueErrorRequest(componentError); } let errorsQueue: ComponentError[] = []; let timeoutId: ReturnType; function queueErrorRequest(error: ComponentError) { - errorsQueue.push(error); + errorsQueue.push(error); - clearTimeout(timeoutId); - timeoutId = setTimeout(() => { - sendErrorsToServer(); - }, 1000); + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + sendErrorsToServer(); + }, 1000); } async function sendErrorsToServer() { - // makeRequest({ - // url: "", - // method: "POST", - // body: errorsQueue, - // useToken: true, - // }); - console.log(`Fake-sending ${errorsQueue.length} errors to server`, errorsQueue); - errorsQueue = []; + // makeRequest({ + // url: "", + // method: "POST", + // body: errorsQueue, + // useToken: true, + // }); + console.log(`Fake-sending ${errorsQueue.length} errors to server`, errorsQueue); + errorsQueue = []; } diff --git a/lib/utils/hooks/metrics/useVKMetrics.ts b/lib/utils/hooks/metrics/useVKMetrics.ts index fb31f0e..fa339c8 100644 --- a/lib/utils/hooks/metrics/useVKMetrics.ts +++ b/lib/utils/hooks/metrics/useVKMetrics.ts @@ -2,11 +2,7 @@ import { useEffect } from "react"; export const useVKMetrics = (vkMetricsNumber: number | undefined) => { useEffect(() => { - if ( - vkMetricsNumber && - typeof vkMetricsNumber === "number" && - !Number.isNaN(vkMetricsNumber) - ) { + if (vkMetricsNumber && typeof vkMetricsNumber === "number" && !Number.isNaN(vkMetricsNumber)) { const script = document.createElement("script"); script.type = "text/javascript"; script.innerHTML = ` diff --git a/lib/utils/hooks/metrics/useVkMetricsGoals.ts b/lib/utils/hooks/metrics/useVkMetricsGoals.ts index bc7e7c4..1e50a39 100644 --- a/lib/utils/hooks/metrics/useVkMetricsGoals.ts +++ b/lib/utils/hooks/metrics/useVkMetricsGoals.ts @@ -29,8 +29,7 @@ export const useVkMetricsGoals = (vkId: number | undefined) => { // Посетитель кликнул по email на стартовой странице emailOpened: () => sendMetrics(vkId, "penaquiz-email"), // Посетитель увидел определенный результат (id - айдишник вопроса с типом result) - resultIdShown: (resultId: string) => - sendMetrics(vkId, `penaquiz-result-${resultId}`), + resultIdShown: (resultId: string) => sendMetrics(vkId, `penaquiz-result-${resultId}`), // Посетитель нажал на ссылку в результате resultLink: () => sendMetrics(vkId, `penaquiz-resultlink`), // Посетитель дошёл до формы контактов @@ -38,13 +37,10 @@ export const useVkMetricsGoals = (vkId: number | undefined) => { // Посетитель заполнил форму контактов contactsFormFilled: () => sendMetrics(vkId, "penaquiz-contacts"), // Посетитель заполнил поле формы контактов - contactsFormField: (field: string) => - sendMetrics(vkId, `penaquiz-formfield-${field}`), + contactsFormField: (field: string) => sendMetrics(vkId, `penaquiz-formfield-${field}`), // Посетитель отправил заявку с мессенджером - messengerRequestSended: (messenger: MetricsMessengers) => - sendMetrics(vkId, `penaquiz-messengers-${messenger}`), + messengerRequestSended: (messenger: MetricsMessengers) => sendMetrics(vkId, `penaquiz-messengers-${messenger}`), // Посетитель прошёл вопрос - questionPassed: (questionId: string) => - sendMetrics(vkId, `penaquiz-step${questionId}`), + questionPassed: (questionId: string) => sendMetrics(vkId, `penaquiz-step${questionId}`), }; }; diff --git a/lib/utils/hooks/metrics/useYandexMetrics.ts b/lib/utils/hooks/metrics/useYandexMetrics.ts index b481661..35ecbf3 100644 --- a/lib/utils/hooks/metrics/useYandexMetrics.ts +++ b/lib/utils/hooks/metrics/useYandexMetrics.ts @@ -2,11 +2,7 @@ import { useEffect } from "react"; export const useYandexMetrics = (yandexMetricsNumber: number | undefined) => { useEffect(() => { - if ( - yandexMetricsNumber && - typeof yandexMetricsNumber === "number" && - !Number.isNaN(yandexMetricsNumber) - ) { + if (yandexMetricsNumber && typeof yandexMetricsNumber === "number" && !Number.isNaN(yandexMetricsNumber)) { const script = document.createElement("script"); script.type = "text/javascript"; script.innerHTML = ` diff --git a/lib/utils/hooks/metrics/useYandexMetricsGoals.ts b/lib/utils/hooks/metrics/useYandexMetricsGoals.ts index f0b2c98..c8d1582 100644 --- a/lib/utils/hooks/metrics/useYandexMetricsGoals.ts +++ b/lib/utils/hooks/metrics/useYandexMetricsGoals.ts @@ -21,8 +21,7 @@ export const useYandexMetricsGoals = (id: number | undefined) => { // Посетитель кликнул по email на стартовой странице emailOpened: () => sendMetrics(id, "penaquiz-email"), // Посетитель увидел определенный результат (id - айдишник вопроса с типом result) - resultIdShown: (resultId: string) => - sendMetrics(id, `penaquiz-result-${resultId}`), + resultIdShown: (resultId: string) => sendMetrics(id, `penaquiz-result-${resultId}`), // Посетитель нажал на ссылку в результате resultLink: () => sendMetrics(id, `penaquiz-resultlink`), // Посетитель дошёл до формы контактов @@ -30,13 +29,10 @@ export const useYandexMetricsGoals = (id: number | undefined) => { // Посетитель заполнил форму контактов contactsFormFilled: () => sendMetrics(id, "penaquiz-contacts"), // Посетитель заполнил поле формы контактов - contactsFormField: (field: string) => - sendMetrics(id, `penaquiz-formfield-${field}`), + contactsFormField: (field: string) => sendMetrics(id, `penaquiz-formfield-${field}`), // Посетитель отправил заявку с мессенджером - messengerRequestSended: (messenger: MetricsMessengers) => - sendMetrics(id, `penaquiz-messengers-${messenger}`), + messengerRequestSended: (messenger: MetricsMessengers) => sendMetrics(id, `penaquiz-messengers-${messenger}`), // Посетитель прошёл вопрос - questionPassed: (questionId: string) => - sendMetrics(id, `penaquiz-step${questionId}`), + questionPassed: (questionId: string) => sendMetrics(id, `penaquiz-step${questionId}`), }; }; diff --git a/lib/utils/hooks/useQuestionFlowControl.ts b/lib/utils/hooks/useQuestionFlowControl.ts index c7abc6d..d8e39e6 100644 --- a/lib/utils/hooks/useQuestionFlowControl.ts +++ b/lib/utils/hooks/useQuestionFlowControl.ts @@ -15,25 +15,18 @@ export function useQuestionFlowControl() { const sortedQuestions = useMemo(() => { return [...questions].sort((a, b) => a.page - b.page); }, [questions]); - const [currentQuestionId, setCurrentQuestionId] = useState( - getFirstQuestionId - ); + const [currentQuestionId, setCurrentQuestionId] = useState(getFirstQuestionId); const answers = useQuizViewStore((state) => state.answers); const pointsSum = useQuizViewStore((state) => state.pointsSum); - const setCurrentQuizStep = useQuizViewStore( - (state) => state.setCurrentQuizStep - ); + const setCurrentQuizStep = useQuizViewStore((state) => state.setCurrentQuizStep); const vkMetrics = useVkMetricsGoals(settings.cfg.vkMetricsNumber); const yandexMetrics = useYandexMetricsGoals(settings.cfg.yandexMetricsNumber); - const currentQuestion = - sortedQuestions.find((question) => question.id === currentQuestionId) ?? - sortedQuestions[0]; + const currentQuestion = sortedQuestions.find((question) => question.id === currentQuestionId) ?? sortedQuestions[0]; console.log("currentQuestion", currentQuestion); const linearQuestionIndex = - currentQuestion && - sortedQuestions.every(({ content }) => content.rule.parentId !== "root") // null when branching enabled + currentQuestion && sortedQuestions.every(({ content }) => content.rule.parentId !== "root") // null when branching enabled ? sortedQuestions.indexOf(currentQuestion) : null; @@ -42,9 +35,7 @@ export function useQuestionFlowControl() { if (settings.cfg.haveRoot) { const nextQuestion = sortedQuestions.find( - (question) => - question.id === settings.cfg.haveRoot || - question.content.id === settings.cfg.haveRoot + (question) => question.id === settings.cfg.haveRoot || question.content.id === settings.cfg.haveRoot ); if (!nextQuestion) return null; @@ -55,28 +46,17 @@ export function useQuestionFlowControl() { } const nextQuestionIdPointsLogic = useCallback(() => { - return sortedQuestions.find( - (question) => - question.type === "result" && question.content.rule.parentId === "line" - ); + return sortedQuestions.find((question) => question.type === "result" && question.content.rule.parentId === "line"); }, [sortedQuestions]); const nextQuestionIdMainLogic = useCallback(() => { - const questionAnswer = answers.find( - ({ questionId }) => questionId === currentQuestion.id - ); + const questionAnswer = answers.find(({ questionId }) => questionId === currentQuestion.id); if (questionAnswer && !moment.isMoment(questionAnswer.answer)) { - const userAnswers = Array.isArray(questionAnswer.answer) - ? questionAnswer.answer - : [questionAnswer.answer]; + const userAnswers = Array.isArray(questionAnswer.answer) ? questionAnswer.answer : [questionAnswer.answer]; for (const branchingRule of currentQuestion.content.rule.main) { - if ( - userAnswers.some((answer) => - branchingRule.rules[0].answers.includes(answer) - ) - ) { + if (userAnswers.some((answer) => branchingRule.rules[0].answers.includes(answer))) { return branchingRule.next; } } @@ -84,8 +64,7 @@ export function useQuestionFlowControl() { if (!currentQuestion.required) { const defaultNextQuestionId = currentQuestion.content.rule.default; - if (defaultNextQuestionId.length > 1 && defaultNextQuestionId !== " ") - return defaultNextQuestionId; + if (defaultNextQuestionId.length > 1 && defaultNextQuestionId !== " ") return defaultNextQuestionId; //Вопросы типа страница, ползунок, своё поле для ввода и дата не могут иметь больше 1 ребёнка. Пользователь не может настроить там дефолт //Кинуть на ребёнка надо даже если там нет дефолта if ( @@ -97,10 +76,7 @@ export function useQuestionFlowControl() { //ничё не нашли, ищем резулт return sortedQuestions.find((q) => { - return ( - q.type === "result" && - q.content.rule.parentId === currentQuestion.content.id - ); + return q.type === "result" && q.content.rule.parentId === currentQuestion.content.id; })?.id; }, [answers, currentQuestion, sortedQuestions]); @@ -116,21 +92,15 @@ export function useQuestionFlowControl() { ? sortedQuestions[linearQuestionIndex - 1] : sortedQuestions.find( (q) => - q.id === currentQuestion?.content.rule.parentId || - q.content.id === currentQuestion?.content.rule.parentId + q.id === currentQuestion?.content.rule.parentId || q.content.id === currentQuestion?.content.rule.parentId ); const findResultPointsLogic = useCallback(() => { const results = sortedQuestions.filter( - (e) => - e.type === "result" && - e.content.rule.minScore !== undefined && - e.content.rule.minScore <= pointsSum + (e) => e.type === "result" && e.content.rule.minScore !== undefined && e.content.rule.minScore <= pointsSum ); const numbers = results.map((e) => - e.type === "result" && e.content.rule.minScore !== undefined - ? e.content.rule.minScore - : 0 + e.type === "result" && e.content.rule.minScore !== undefined ? e.content.rule.minScore : 0 ); const indexOfNext = Math.max(...numbers); @@ -142,53 +112,31 @@ export function useQuestionFlowControl() { if (settings.cfg.score) { if (linearQuestionIndex !== null) { next = sortedQuestions[linearQuestionIndex + 1]; - if (next?.type === "result" || next == undefined) - next = findResultPointsLogic(); + if (next?.type === "result" || next == undefined) next = findResultPointsLogic(); } } else { if (linearQuestionIndex !== null) { next = sortedQuestions[linearQuestionIndex + 1] ?? - sortedQuestions.find( - (question) => - question.type === "result" && - question.content.rule.parentId === "line" - ); + sortedQuestions.find((question) => question.type === "result" && question.content.rule.parentId === "line"); } else { - next = sortedQuestions.find( - (q) => q.id === nextQuestionId || q.content.id === nextQuestionId - ); + next = sortedQuestions.find((q) => q.id === nextQuestionId || q.content.id === nextQuestionId); } } return next; - }, [ - nextQuestionId, - findResultPointsLogic, - linearQuestionIndex, - sortedQuestions, - settings.cfg.score, - ]); + }, [nextQuestionId, findResultPointsLogic, linearQuestionIndex, sortedQuestions, settings.cfg.score]); const showResult = useCallback(() => { - if (nextQuestion?.type !== "result") - throw new Error("Current question is not result"); + if (nextQuestion?.type !== "result") throw new Error("Current question is not result"); setCurrentQuestionId(nextQuestion.id); - if ( - settings.cfg.resultInfo.showResultForm === "after" || - isResultQuestionEmpty(nextQuestion) - ) + if (settings.cfg.resultInfo.showResultForm === "after" || isResultQuestionEmpty(nextQuestion)) setCurrentQuizStep("contactform"); - }, [ - nextQuestion, - setCurrentQuizStep, - settings.cfg.resultInfo.showResultForm, - ]); + }, [nextQuestion, setCurrentQuizStep, settings.cfg.resultInfo.showResultForm]); const showResultAfterContactForm = useCallback(() => { - if (currentQuestion?.type !== "result") - throw new Error("Current question is not result"); + if (currentQuestion?.type !== "result") throw new Error("Current question is not result"); if (isResultQuestionEmpty(currentQuestion)) { enqueueSnackbar("Данные отправлены"); return; @@ -228,14 +176,9 @@ export function useQuestionFlowControl() { const isPreviousButtonEnabled = Boolean(prevQuestion); const isNextButtonEnabled = useMemo(() => { - const hasAnswer = answers.some( - ({ questionId }) => questionId === currentQuestion.id - ); + const hasAnswer = answers.some(({ questionId }) => questionId === currentQuestion.id); - if ( - "required" in currentQuestion.content && - currentQuestion.content.required - ) { + if ("required" in currentQuestion.content && currentQuestion.content.required) { return hasAnswer; } @@ -251,8 +194,7 @@ export function useQuestionFlowControl() { return { currentQuestion, - currentQuestionStepNumber: - linearQuestionIndex === null ? null : linearQuestionIndex + 1, + currentQuestionStepNumber: linearQuestionIndex === null ? null : linearQuestionIndex + 1, isNextButtonEnabled, isPreviousButtonEnabled, moveToPrevQuestion, diff --git a/lib/utils/hooks/useUADevice.ts b/lib/utils/hooks/useUADevice.ts index d50db9e..f83b3df 100644 --- a/lib/utils/hooks/useUADevice.ts +++ b/lib/utils/hooks/useUADevice.ts @@ -1,13 +1,13 @@ import { useEffect, useState } from "react"; -export function useUADevice(): { isMobileDevice: boolean; } { - const [isMobileDevice, setIsMobileDevice] = useState(false); +export function useUADevice(): { isMobileDevice: boolean } { + const [isMobileDevice, setIsMobileDevice] = useState(false); - useEffect(() => { - const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + useEffect(() => { + const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); - setIsMobileDevice(isMobile); - }, [navigator.userAgent]); + setIsMobileDevice(isMobile); + }, [navigator.userAgent]); - return { isMobileDevice }; + return { isMobileDevice }; } diff --git a/lib/utils/notReachable.ts b/lib/utils/notReachable.ts index 4063c2a..dcd23df 100644 --- a/lib/utils/notReachable.ts +++ b/lib/utils/notReachable.ts @@ -1,3 +1,3 @@ export function notReachable(_: never): never { - throw new Error(`Shouldn't reach here: ${_}`); + throw new Error(`Shouldn't reach here: ${_}`); } diff --git a/lib/utils/parse-error.ts b/lib/utils/parse-error.ts index 518756a..e291716 100644 --- a/lib/utils/parse-error.ts +++ b/lib/utils/parse-error.ts @@ -9,24 +9,21 @@ export type ServerError = { const translateMessage: Record = { "user not found": "Пользователь не найден", "invalid password": "Неправильный пароль", - "field is empty": "Поле \"Пароль\" не заполнено", - "field is empty": "Поле \"Логин\" не заполнено", - "field is empty": "Поле \"E-mail\" не заполнено", - "field is empty": "Поле \"Номер телефона\" не заполнено", + "field is empty": 'Поле "Пароль" не заполнено', + "field is empty": 'Поле "Логин" не заполнено', + "field is empty": 'Поле "E-mail" не заполнено', + "field is empty": 'Поле "Номер телефона" не заполнено', "user with this email or login is exist": "Пользователь уже существует", - "user with this login is exist": "Пользователь с таким логином уже существует" + "user with this login is exist": "Пользователь с таким логином уже существует", }; export const parseAxiosError = (nativeError: unknown): [string, number?] => { const error = nativeError as AxiosError; - if ( - error.response?.data && - "statusCode" in (error.response.data as ServerError) - ) { + if (error.response?.data && "statusCode" in (error.response.data as ServerError)) { const serverError = error.response.data as ServerError; - const translatedMessage = translateMessage[serverError.message] - if (translatedMessage !== undefined) serverError.message = translatedMessage + const translatedMessage = translateMessage[serverError.message]; + if (translatedMessage !== undefined) serverError.message = translatedMessage; return [serverError.message, serverError.statusCode]; } diff --git a/lib/utils/phoneMasksByCountry.tsx b/lib/utils/phoneMasksByCountry.tsx index 83b3286..86be017 100644 --- a/lib/utils/phoneMasksByCountry.tsx +++ b/lib/utils/phoneMasksByCountry.tsx @@ -1,85 +1,84 @@ - type PhoneMasksByCountry = { - [countryCode: string]: [string, string]; + [countryCode: string]: [string, string]; }; -export const phoneMasksByCountry:PhoneMasksByCountry = { - // СНГ - 'RU': ['Russia +7', '+{7} (000) 000-00-00'], // Россия - 'UA': ['Ukraine +380', '+{380} (00) 000-00-00'], // Украина - 'BY': ['Belarus +375', '+{375} (00) 000-00-00'], // Беларусь - 'KZ': ['Kazakhstan +7', '+{7} (000) 000-00-00'], // Казахстан - 'UZ': ['Uzbekistan +998', '+{998} (00) 000-00-00'], // Узбекистан - 'TJ': ['Tajikistan +992', '+{992} (00) 000-00-00'], // Таджикистан - 'KG': ['Kyrgyzstan +996', '+{996} (000) 00-00-00'], // Кыргызстан - 'TM': ['Turkmenistan +993', '+{993} (00) 00-00-00'], // Туркменистан - 'AZ': ['Azerbaijan +994', '+{994} (00) 000-00-00'], // Азербайджан - 'AM': ['Armenia +374', '+{374} (00) 000-000'], // Армения - 'GE': ['Georgia +995', '+{995} (000) 00-00-00'], // Грузия +export const phoneMasksByCountry: PhoneMasksByCountry = { + // СНГ + RU: ["Russia +7", "+{7} (000) 000-00-00"], // Россия + UA: ["Ukraine +380", "+{380} (00) 000-00-00"], // Украина + BY: ["Belarus +375", "+{375} (00) 000-00-00"], // Беларусь + KZ: ["Kazakhstan +7", "+{7} (000) 000-00-00"], // Казахстан + UZ: ["Uzbekistan +998", "+{998} (00) 000-00-00"], // Узбекистан + TJ: ["Tajikistan +992", "+{992} (00) 000-00-00"], // Таджикистан + KG: ["Kyrgyzstan +996", "+{996} (000) 00-00-00"], // Кыргызстан + TM: ["Turkmenistan +993", "+{993} (00) 00-00-00"], // Туркменистан + AZ: ["Azerbaijan +994", "+{994} (00) 000-00-00"], // Азербайджан + AM: ["Armenia +374", "+{374} (00) 000-000"], // Армения + GE: ["Georgia +995", "+{995} (000) 00-00-00"], // Грузия - // Европа - 'DE': ['Germany +49', '+{49} 0000 0000000'], // Германия - 'FR': ['France +33', '+{33} 0 00 00 00 00'], // Франция - 'IT': ['Italy +39', '+{39} 000 000 0000'], // Италия - 'ES': ['Spain +34', '+{34} 000 00 00 00'], // Испания - 'GB': ['United Kingdom +44', '+{44} 0000 000000'], // Великобритания - 'PL': ['Poland +48', '+{48} 000 000 000'], // Польша - 'NL': ['Netherlands +31', '+{31} 00 000 0000'], // Нидерланды - 'BE': ['Belgium +32', '+{32} 00 00 00 00'], // Бельгия - 'CH': ['Switzerland +41', '+{41} 00 000 00 00'], // Швейцария - 'AT': ['Austria +43', '+{43} 000 000 0000'], // Австрия - 'DK': ['Denmark +45', '+{45} 00 00 00 00'], // Дания - 'SE': ['Sweden +46', '+{46} 00 000 00 00'], // Швеция - 'NO': ['Norway +47', '+{47} 000 00 000'], // Норвегия - 'FI': ['Finland +358', '+{358} 00 000 0000'], // Финляндия - 'CZ': ['Czech Republic +420', '+{420} 000 000 000'], // Чехия - 'SK': ['Slovakia +421', '+{421} 000 000 000'], // Словакия - 'HU': ['Hungary +36', '+{36} 00 000 0000'], // Венгрия - 'RO': ['Romania +40', '+{40} 000 000 000'], // Румыния - 'BG': ['Bulgaria +359', '+{359} 00 000 000'], // Болгария - 'GR': ['Greece +30', '+{30} 000 000 0000'], // Греция - 'PT': ['Portugal +351', '+{351} 000 000 000'], // Португалия - 'IE': ['Ireland +353', '+{353} 00 000 0000'], // Ирландия + // Европа + DE: ["Germany +49", "+{49} 0000 0000000"], // Германия + FR: ["France +33", "+{33} 0 00 00 00 00"], // Франция + IT: ["Italy +39", "+{39} 000 000 0000"], // Италия + ES: ["Spain +34", "+{34} 000 00 00 00"], // Испания + GB: ["United Kingdom +44", "+{44} 0000 000000"], // Великобритания + PL: ["Poland +48", "+{48} 000 000 000"], // Польша + NL: ["Netherlands +31", "+{31} 00 000 0000"], // Нидерланды + BE: ["Belgium +32", "+{32} 00 00 00 00"], // Бельгия + CH: ["Switzerland +41", "+{41} 00 000 00 00"], // Швейцария + AT: ["Austria +43", "+{43} 000 000 0000"], // Австрия + DK: ["Denmark +45", "+{45} 00 00 00 00"], // Дания + SE: ["Sweden +46", "+{46} 00 000 00 00"], // Швеция + NO: ["Norway +47", "+{47} 000 00 000"], // Норвегия + FI: ["Finland +358", "+{358} 00 000 0000"], // Финляндия + CZ: ["Czech Republic +420", "+{420} 000 000 000"], // Чехия + SK: ["Slovakia +421", "+{421} 000 000 000"], // Словакия + HU: ["Hungary +36", "+{36} 00 000 0000"], // Венгрия + RO: ["Romania +40", "+{40} 000 000 000"], // Румыния + BG: ["Bulgaria +359", "+{359} 00 000 000"], // Болгария + GR: ["Greece +30", "+{30} 000 000 0000"], // Греция + PT: ["Portugal +351", "+{351} 000 000 000"], // Португалия + IE: ["Ireland +353", "+{353} 00 000 0000"], // Ирландия - // Азия - 'CN': ['China +86', '+{86} 000 0000 0000'], // Китай - 'JP': ['Japan +81', '+{81} 000-000-0000'], // Япония - 'IN': ['India +91', '+{91} 00000 00000'], // Индия - 'KR': ['South Korea +82', '+{82} 00-0000-0000'], // Южная Корея - 'ID': ['Indonesia +62', '+{62} 0000 0000 0000'], // Индонезия - 'TR': ['Turkey +90', '+{90} 000 000 00 00'], // Турция - 'IL': ['Israel +972', '+{972} 00 000-0000'], // Израиль - 'SA': ['Saudi Arabia +966', '+{966} 00 000 0000'], // Саудовская Аравия - 'AE': ['United Arab Emirates +971', '+{971} 00 000 0000'], // ОАЭ - 'TH': ['Thailand +66', '+{66} 00 000 0000'], // Таиланд - 'VN': ['Vietnam +84', '+{84} 000 000 000'], // Вьетнам - 'MY': ['Malaysia +60', '+{60} 00-000 0000'], // Малайзия - 'PH': ['Philippines +63', '+{63} 000 000 0000'], // Филиппины + // Азия + CN: ["China +86", "+{86} 000 0000 0000"], // Китай + JP: ["Japan +81", "+{81} 000-000-0000"], // Япония + IN: ["India +91", "+{91} 00000 00000"], // Индия + KR: ["South Korea +82", "+{82} 00-0000-0000"], // Южная Корея + ID: ["Indonesia +62", "+{62} 0000 0000 0000"], // Индонезия + TR: ["Turkey +90", "+{90} 000 000 00 00"], // Турция + IL: ["Israel +972", "+{972} 00 000-0000"], // Израиль + SA: ["Saudi Arabia +966", "+{966} 00 000 0000"], // Саудовская Аравия + AE: ["United Arab Emirates +971", "+{971} 00 000 0000"], // ОАЭ + TH: ["Thailand +66", "+{66} 00 000 0000"], // Таиланд + VN: ["Vietnam +84", "+{84} 000 000 000"], // Вьетнам + MY: ["Malaysia +60", "+{60} 00-000 0000"], // Малайзия + PH: ["Philippines +63", "+{63} 000 000 0000"], // Филиппины - // Северная Америка - 'US': ['United States +1', '+{1} (000) 000-0000'], // США - 'CA': ['Canada +1', '+{1} (000) 000-0000'], // Канада - 'MX': ['Mexico +52', '+{52} 000 000 0000'], // Мексика + // Северная Америка + US: ["United States +1", "+{1} (000) 000-0000"], // США + CA: ["Canada +1", "+{1} (000) 000-0000"], // Канада + MX: ["Mexico +52", "+{52} 000 000 0000"], // Мексика - // Южная Америка - 'BR': ['Brazil +55', '+{55} (00) 0000-0000'], // Бразилия - 'AR': ['Argentina +54', '+{54} 000 000-0000'], // Аргентина - 'CO': ['Colombia +57', '+{57} 000 000 0000'], // Колумбия - 'PE': ['Peru +51', '+{51} 000 000 000'], // Перу - 'CL': ['Chile +56', '+{56} 00 000 0000'], // Чили - 'EC': ['Ecuador +593', '+{593} 00 000 0000'], // Эквадор - 'VE': ['Venezuela +58', '+{58} 000 000 0000'], // Венесуэла + // Южная Америка + BR: ["Brazil +55", "+{55} (00) 0000-0000"], // Бразилия + AR: ["Argentina +54", "+{54} 000 000-0000"], // Аргентина + CO: ["Colombia +57", "+{57} 000 000 0000"], // Колумбия + PE: ["Peru +51", "+{51} 000 000 000"], // Перу + CL: ["Chile +56", "+{56} 00 000 0000"], // Чили + EC: ["Ecuador +593", "+{593} 00 000 0000"], // Эквадор + VE: ["Venezuela +58", "+{58} 000 000 0000"], // Венесуэла - // Африка - 'EG': ['Egypt +20', '+{20} 000 000 0000'], // Египет - 'NG': ['Nigeria +234', '+{234} 000 0000 0000'], // Нигерия - 'ZA': ['South Africa +27', '+{27} 000 000 0000'], // Южная Африка - 'MA': ['Morocco +212', '+{212} 00 00 00 00'], // Марокко - 'DZ': ['Algeria +213', '+{213} 00 00 00 00'], // Алжир - 'KE': ['Kenya +254', '+{254} 000 000 000'], // Кения - 'ET': ['Ethiopia +251', '+{251} 00 000 0000'], // Эфиопия + // Африка + EG: ["Egypt +20", "+{20} 000 000 0000"], // Египет + NG: ["Nigeria +234", "+{234} 000 0000 0000"], // Нигерия + ZA: ["South Africa +27", "+{27} 000 000 0000"], // Южная Африка + MA: ["Morocco +212", "+{212} 00 00 00 00"], // Марокко + DZ: ["Algeria +213", "+{213} 00 00 00 00"], // Алжир + KE: ["Kenya +254", "+{254} 000 000 000"], // Кения + ET: ["Ethiopia +251", "+{251} 00 000 0000"], // Эфиопия - // Австралия и Океания - 'AU': ['Australia +61', '+{61} 0000 000 000'], // Австралия - 'NZ': ['New Zealand +64', '+{64} 00 000 0000'], // Новая Зеландия + // Австралия и Океания + AU: ["Australia +61", "+{61} 0000 000 000"], // Австралия + NZ: ["New Zealand +64", "+{64} 00 000 0000"], // Новая Зеландия }; diff --git a/lib/utils/themes/Publication/genericPublication.ts b/lib/utils/themes/Publication/genericPublication.ts index 1c64af7..cd93017 100644 --- a/lib/utils/themes/Publication/genericPublication.ts +++ b/lib/utils/themes/Publication/genericPublication.ts @@ -2,47 +2,44 @@ import { createTheme } from "@mui/material"; import theme from "../generic"; const themePublic = createTheme({ - ...theme, - components: { - MuiButton: { - variants: [ - { - props: { - variant: 'contained' - }, - style: { - padding: '13px 20px', - borderRadius: '8px', - boxShadow: "none", - "&:active": { - backgroundColor: "#000000", - color: "#FFFFFF" - }, - "&:disabled": { - color: "#9A9AAF", - border: "1px solid #9A9AAF", - }, - - }, - }, - { - props: { - variant: 'outlined' - }, - style: { - padding: '10px 20px', - borderRadius: '8px', - "&:hover": { - backgroundColor: "#581CA7", - border: '1px solid #581CA7', - } - - }, - }, - ], + ...theme, + components: { + MuiButton: { + variants: [ + { + props: { + variant: "contained", + }, + style: { + padding: "13px 20px", + borderRadius: "8px", + boxShadow: "none", + "&:active": { + backgroundColor: "#000000", + color: "#FFFFFF", }, + "&:disabled": { + color: "#9A9AAF", + border: "1px solid #9A9AAF", + }, + }, }, + { + props: { + variant: "outlined", + }, + style: { + padding: "10px 20px", + borderRadius: "8px", + "&:hover": { + backgroundColor: "#581CA7", + border: "1px solid #581CA7", + }, + }, + }, + ], }, -) + }, +}); -export default themePublic; \ No newline at end of file +export default themePublic; diff --git a/lib/utils/themes/Publication/themePublication.ts b/lib/utils/themes/Publication/themePublication.ts index f60610a..6a346a2 100644 --- a/lib/utils/themes/Publication/themePublication.ts +++ b/lib/utils/themes/Publication/themePublication.ts @@ -445,27 +445,26 @@ const Design10 = createTheme({ }, }); -export const quizThemes: Record = - { - StandardTheme: { theme: StandardTheme, isLight: true }, - StandardDarkTheme: { theme: StandardDarkTheme, isLight: false }, - PinkTheme: { theme: PinkTheme, isLight: true }, - PinkDarkTheme: { theme: PinkDarkTheme, isLight: false }, - BlackWhiteTheme: { theme: BlackWhiteTheme, isLight: true }, - OliveTheme: { theme: OliveTheme, isLight: true }, - YellowTheme: { theme: YellowTheme, isLight: true }, - GoldDarkTheme: { theme: GoldDarkTheme, isLight: false }, - PurpleTheme: { theme: PurpleTheme, isLight: true }, - BlueTheme: { theme: BlueTheme, isLight: true }, - BlueDarkTheme: { theme: BlueDarkTheme, isLight: false }, - Design1: { theme: Design1, isLight: false }, - Design2: { theme: Design2, isLight: false }, - Design3: { theme: Design3, isLight: true }, - Design4: { theme: Design4, isLight: false }, - Design5: { theme: Design5, isLight: false }, - Design6: { theme: Design6, isLight: false }, - Design7: { theme: Design7, isLight: false }, - Design8: { theme: Design8, isLight: false }, - Design9: { theme: Design9, isLight: false }, - Design10: { theme: Design10, isLight: false }, - }; +export const quizThemes: Record = { + StandardTheme: { theme: StandardTheme, isLight: true }, + StandardDarkTheme: { theme: StandardDarkTheme, isLight: false }, + PinkTheme: { theme: PinkTheme, isLight: true }, + PinkDarkTheme: { theme: PinkDarkTheme, isLight: false }, + BlackWhiteTheme: { theme: BlackWhiteTheme, isLight: true }, + OliveTheme: { theme: OliveTheme, isLight: true }, + YellowTheme: { theme: YellowTheme, isLight: true }, + GoldDarkTheme: { theme: GoldDarkTheme, isLight: false }, + PurpleTheme: { theme: PurpleTheme, isLight: true }, + BlueTheme: { theme: BlueTheme, isLight: true }, + BlueDarkTheme: { theme: BlueDarkTheme, isLight: false }, + Design1: { theme: Design1, isLight: false }, + Design2: { theme: Design2, isLight: false }, + Design3: { theme: Design3, isLight: true }, + Design4: { theme: Design4, isLight: false }, + Design5: { theme: Design5, isLight: false }, + Design6: { theme: Design6, isLight: false }, + Design7: { theme: Design7, isLight: false }, + Design8: { theme: Design8, isLight: false }, + Design9: { theme: Design9, isLight: false }, + Design10: { theme: Design10, isLight: false }, +}; diff --git a/lib/utils/themes/dark.ts b/lib/utils/themes/dark.ts index 35a2a02..87857e4 100644 --- a/lib/utils/themes/dark.ts +++ b/lib/utils/themes/dark.ts @@ -1,49 +1,48 @@ import { createTheme } from "@mui/material"; import theme from "./generic"; - const darkTheme = createTheme({ - ...theme, - palette: { - mode: "dark", - primary: { - main: "#ffffff", - }, - secondary: { - main: "#252734" - }, - text: { - primary: "#ffffff", - secondary: "#7E2AEA", - }, - background: { - default: "#333647", - }, - lightPurple: { - main: "#333647", - }, - darkPurple: { - main: "#252734", - }, - brightPurple: { - main: "#7E2AEA", - }, - grey1: { - main: "#434657", - }, - grey2: { - main: "#9A9AAF", - }, - grey3: { - main: "#4D4D4D", - }, - orange: { - main: "#FB5607", - }, - navbarbg: { - main: "#333647", - }, - } + ...theme, + palette: { + mode: "dark", + primary: { + main: "#ffffff", + }, + secondary: { + main: "#252734", + }, + text: { + primary: "#ffffff", + secondary: "#7E2AEA", + }, + background: { + default: "#333647", + }, + lightPurple: { + main: "#333647", + }, + darkPurple: { + main: "#252734", + }, + brightPurple: { + main: "#7E2AEA", + }, + grey1: { + main: "#434657", + }, + grey2: { + main: "#9A9AAF", + }, + grey3: { + main: "#4D4D4D", + }, + orange: { + main: "#FB5607", + }, + navbarbg: { + main: "#333647", + }, + }, }); -export default darkTheme; \ No newline at end of file +export default darkTheme; diff --git a/lib/utils/themes/fontFace.ts b/lib/utils/themes/fontFace.ts index cec89a4..e069d3d 100644 --- a/lib/utils/themes/fontFace.ts +++ b/lib/utils/themes/fontFace.ts @@ -1,6 +1,5 @@ import { domain } from "../defineDomain"; - export const fontFaces = ` @font-face { font-family: 'Lato'; diff --git a/lib/utils/themes/generic.ts b/lib/utils/themes/generic.ts index 77fb98b..79cf032 100644 --- a/lib/utils/themes/generic.ts +++ b/lib/utils/themes/generic.ts @@ -1,147 +1,150 @@ -import {createTheme} from "@mui/material"; -import {fontFaces} from "./fontFace"; +import { createTheme } from "@mui/material"; +import { fontFaces } from "./fontFace"; -declare module '@mui/material/Button' { - interface ButtonPropsVariantOverrides { - main: true; - outmain: true; - } +declare module "@mui/material/Button" { + interface ButtonPropsVariantOverrides { + main: true; + outmain: true; + } } const theme = createTheme({ - breakpoints: { - values: { - xs: 0, - sm: 560, - md: 900, - lg: 1200, - xl: 1536, - }, + breakpoints: { + values: { + xs: 0, + sm: 560, + md: 900, + lg: 1200, + xl: 1536, }, - components: { - MuiCssBaseline: { - styleOverrides: fontFaces, + }, + components: { + MuiCssBaseline: { + styleOverrides: fontFaces, + }, + MuiScopedCssBaseline: { + styleOverrides: { + root: fontFaces, + }, + }, + MuiTypography: { + defaultProps: { + variantMapping: { + p1: "p", }, - MuiScopedCssBaseline: { - styleOverrides: { - root: fontFaces, - } - }, - MuiTypography: { - defaultProps: { - variantMapping: { - p1: "p", - } + }, + }, + MuiButton: { + variants: [ + { + props: { + variant: "contained", + }, + style: { + backgroundColor: "#7E2AEA", + padding: "13px 20px", + borderRadius: "8px", + color: "#ffffff", + boxShadow: "none", + "&:hover": { + backgroundColor: "#581CA7", }, + }, }, - MuiButton: { - variants: [ - { - props: { - variant: 'contained' - }, - style: { - backgroundColor: "#7E2AEA", - padding: '13px 20px', - borderRadius: '8px', - color: '#ffffff', - boxShadow: "none", - "&:hover": { - backgroundColor: "#581CA7" - } - - }, - }, - { - props: { - variant: 'outlined' - }, - style: { - backgroundColor: "#F2F3F7", - padding: '10px 20px', - borderRadius: '8px', - border: '1px solid #7E2AEA', - color: '#9A9AAF', - "&:hover": { - backgroundColor: "#581CA7", - border: '1px solid #581CA7', - } - - }, - }, - ], + { + props: { + variant: "outlined", + }, + style: { + backgroundColor: "#F2F3F7", + padding: "10px 20px", + borderRadius: "8px", + border: "1px solid #7E2AEA", + color: "#9A9AAF", + "&:hover": { + backgroundColor: "#581CA7", + border: "1px solid #581CA7", + }, + }, }, + ], }, - typography: { - h5: { // H2 в макете - fontSize: "24px", - lineHeight: "28.44px", - fontWeight: 501, - }, - button: { - fontSize: "18px", - lineHeight: "24px", - fontWeight: 400, - textTransform: "none", - }, - body1: { // T1 в макете - fontSize: "18px", - lineHeight: "21.33px", - fontWeight: 400, - }, - body2: { // M1 в макете - fontSize: "16px", - lineHeight: "20px", - fontWeight: 500, - }, - p1: { // P1 в макете - fontSize: "20px", - lineHeight: "24px", - fontWeight: 500, - }, - fontFamily: [ - '"Lato"', - "Twemoji Country Flags", - "-apple-system", - "BlinkMacSystemFont", - "Arial", - "sans-serif", - '"Apple Color Emoji"', - '"Segoe UI Emoji"', - '"Segoe UI Symbol"', - ].join(","), + }, + typography: { + h5: { + // H2 в макете + fontSize: "24px", + lineHeight: "28.44px", + fontWeight: 501, }, + button: { + fontSize: "18px", + lineHeight: "24px", + fontWeight: 400, + textTransform: "none", + }, + body1: { + // T1 в макете + fontSize: "18px", + lineHeight: "21.33px", + fontWeight: 400, + }, + body2: { + // M1 в макете + fontSize: "16px", + lineHeight: "20px", + fontWeight: 500, + }, + p1: { + // P1 в макете + fontSize: "20px", + lineHeight: "24px", + fontWeight: 500, + }, + fontFamily: [ + '"Lato"', + "Twemoji Country Flags", + "-apple-system", + "BlinkMacSystemFont", + "Arial", + "sans-serif", + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + ].join(","), + }, }); theme.typography.h2 = { - fontSize: "70px", - lineHeight: "70px", - fontWeight: 500, - [theme.breakpoints.down("md")]: { - fontSize: "42px", - lineHeight: "50px", - } + fontSize: "70px", + lineHeight: "70px", + fontWeight: 500, + [theme.breakpoints.down("md")]: { + fontSize: "42px", + lineHeight: "50px", + }, }; -theme.typography.h4 = { // H1 в макете - fontSize: "36px", - lineHeight: "42.66px", - fontWeight: 500, - [theme.breakpoints.down("md")]: { - fontSize: "24px", - lineHeight: "28.44px", - } +theme.typography.h4 = { + // H1 в макете + fontSize: "36px", + lineHeight: "42.66px", + fontWeight: 500, + [theme.breakpoints.down("md")]: { + fontSize: "24px", + lineHeight: "28.44px", + }, }; theme.typography.infographic = { - fontSize: "80px", - lineHeight: "94.8px", + fontSize: "80px", + lineHeight: "94.8px", + fontWeight: 400, + [theme.breakpoints.down("md")]: { + fontSize: "50px", + lineHeight: "59px", fontWeight: 400, - [theme.breakpoints.down("md")]: { - fontSize: "50px", - lineHeight: "59px", - fontWeight: 400, - } + }, }; export default theme; diff --git a/lib/utils/themes/light.ts b/lib/utils/themes/light.ts index 57d32d5..7e41652 100644 --- a/lib/utils/themes/light.ts +++ b/lib/utils/themes/light.ts @@ -1,56 +1,55 @@ import { createTheme } from "@mui/material"; import theme from "./generic"; - const lightTheme = createTheme({ - ...theme, - palette: { - mode: "light", - primary: { - main: "#000000", - }, - secondary: { - main: "#252734" - }, - text: { - primary: "#000000", - secondary: "#7E2AEA", - }, + ...theme, + palette: { + mode: "light", + primary: { + main: "#000000", + }, + secondary: { + main: "#252734", + }, + text: { + primary: "#000000", + secondary: "#7E2AEA", + }, - background: { - default: "#F2F3F7", - }, - lightPurple: { - main: "#333647", - }, - darkPurple: { - main: "#252734", - }, - brightPurple: { - main: "#7E2AEA", - }, - fadePurple: { - main: "#C19AF5", - }, - grey1: { - main: "#434657", - }, - grey2: { - main: "#9A9AAF", - }, - grey3: { - main: "#4D4D4D", - }, - grey4: { - main: "#333647", - }, - orange: { - main: "#FB5607", - }, - navbarbg: { - main: "#FFFFFF", - }, - } + background: { + default: "#F2F3F7", + }, + lightPurple: { + main: "#333647", + }, + darkPurple: { + main: "#252734", + }, + brightPurple: { + main: "#7E2AEA", + }, + fadePurple: { + main: "#C19AF5", + }, + grey1: { + main: "#434657", + }, + grey2: { + main: "#9A9AAF", + }, + grey3: { + main: "#4D4D4D", + }, + grey4: { + main: "#333647", + }, + orange: { + main: "#FB5607", + }, + navbarbg: { + main: "#FFFFFF", + }, + }, }); -export default lightTheme; \ No newline at end of file +export default lightTheme; diff --git a/lib/utils/themes/mui.d.ts b/lib/utils/themes/mui.d.ts index f66d0c6..eb40eec 100644 --- a/lib/utils/themes/mui.d.ts +++ b/lib/utils/themes/mui.d.ts @@ -1,50 +1,50 @@ import "@material-ui/styles"; declare module "@mui/material/styles" { - interface Palette { - lightPurple: Palette["primary"], - darkPurple: Palette["primary"], - brightPurple: Palette["primary"], - fadePurple: Palette["primary"], - grey1: Palette["primary"], - grey2: Palette["primary"], - grey3: Palette["primary"], - grey4: Palette["primary"], - orange: Palette["primary"], - navbarbg: Palette["primary"], - } - interface PaletteOptions { - lightPurple?: PaletteOptions["primary"], - darkPurple?: PaletteOptions["primary"], - brightPurple?: PaletteOptions["primary"], - fadePurple?: PaletteOptions["primary"], - grey1?: PaletteOptions["primary"], - grey2?: PaletteOptions["primary"], - grey3?: PaletteOptions["primary"], - grey4?: PaletteOptions["primary"], - orange?: PaletteOptions["primary"], - navbarbg?: PaletteOptions["primary"], - } - interface TypographyVariants { - infographic: React.CSSProperties; - p1: React.CSSProperties; - } - interface TypographyVariantsOptions { - infographic?: React.CSSProperties; - p1?: React.CSSProperties; - } + interface Palette { + lightPurple: Palette["primary"]; + darkPurple: Palette["primary"]; + brightPurple: Palette["primary"]; + fadePurple: Palette["primary"]; + grey1: Palette["primary"]; + grey2: Palette["primary"]; + grey3: Palette["primary"]; + grey4: Palette["primary"]; + orange: Palette["primary"]; + navbarbg: Palette["primary"]; + } + interface PaletteOptions { + lightPurple?: PaletteOptions["primary"]; + darkPurple?: PaletteOptions["primary"]; + brightPurple?: PaletteOptions["primary"]; + fadePurple?: PaletteOptions["primary"]; + grey1?: PaletteOptions["primary"]; + grey2?: PaletteOptions["primary"]; + grey3?: PaletteOptions["primary"]; + grey4?: PaletteOptions["primary"]; + orange?: PaletteOptions["primary"]; + navbarbg?: PaletteOptions["primary"]; + } + interface TypographyVariants { + infographic: React.CSSProperties; + p1: React.CSSProperties; + } + interface TypographyVariantsOptions { + infographic?: React.CSSProperties; + p1?: React.CSSProperties; + } } declare module "@mui/material/Typography" { - interface TypographyPropsVariantOverrides { - infographic: true; - p1: true; - } + interface TypographyPropsVariantOverrides { + infographic: true; + p1: true; + } } type DataAttributeKey = `data-${string}`; -declare module 'react' { - interface HTMLAttributes extends AriaAttributes, DOMAttributes { - [dataAttribute: DataAttributeKey]: unknown; - } +declare module "react" { + interface HTMLAttributes extends AriaAttributes, DOMAttributes { + [dataAttribute: DataAttributeKey]: unknown; + } } diff --git a/public/site.webmanifest b/public/site.webmanifest index b20abb7..06d341a 100644 --- a/public/site.webmanifest +++ b/public/site.webmanifest @@ -1,19 +1,19 @@ -{ - "name": "", - "short_name": "", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/src/App.tsx b/src/App.tsx index 2b9c0ba..2ad143e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,15 +8,15 @@ const defaultQuizId = "3c49550d-8c77-4788-bc2d-42586a261514"; //тест виз // const defaultQuizId = "ad7f5a87-b833-4f5b-854e-453706ed655c"; // linear export default function App() { - const quizId = useParams().quizId || ""; + const quizId = useParams().quizId || ""; - return ( - - - - ); + return ( + + + + ); } diff --git a/src/WidgetDev.tsx b/src/WidgetDev.tsx index 0a0678f..2f8d11c 100644 --- a/src/WidgetDev.tsx +++ b/src/WidgetDev.tsx @@ -3,69 +3,68 @@ import { Box, ThemeProvider, Typography } from "@mui/material"; import { useEffect, useRef } from "react"; import { BannerWidget as Widget } from "./widgets"; - const widgetProps: ConstructorParameters[0] = { - quizId: "3c49550d-8c77-4788-bc2d-42586a261514", - position: "bottomright", + quizId: "3c49550d-8c77-4788-bc2d-42586a261514", + position: "bottomright", }; export default function WidgetDev() { - const widgetRef = useRef(null); + const widgetRef = useRef(null); - useEffect(() => { - if (!widgetRef.current) { - widgetRef.current = new Widget(widgetProps); - } else { - widgetRef.current.render(widgetProps); - } - }); + useEffect(() => { + if (!widgetRef.current) { + widgetRef.current = new Widget(widgetProps); + } else { + widgetRef.current.render(widgetProps); + } + }); - return ( - - - - - - - - - - - - - - - - - - - - - - ); + return ( + + + + + + + + + + + + + + + + + + + + + + ); } -const lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu"; +const lorem = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu"; function Lorem() { - - return ( - - {lorem} - - ); + return ( + + {lorem} + + ); } diff --git a/src/main.tsx b/src/main.tsx index 376161d..197859c 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -3,30 +3,29 @@ import { RouteObject, RouterProvider, createBrowserRouter } from "react-router-d import App from "./App"; import { StrictMode, lazy } from "react"; - const routes: RouteObject[] = [ - { - path: "/", - children: [ - { - index: true, - element: , - }, - { - path: ":quizId", - element: , - }, - ] - } + { + path: "/", + children: [ + { + index: true, + element: , + }, + { + path: ":quizId", + element: , + }, + ], + }, ]; if (import.meta.env.DEV) { - const WidgetDev = lazy(() => import("./WidgetDev")); + const WidgetDev = lazy(() => import("./WidgetDev")); - routes[0].children?.push({ - path: "widgetdev", - element: , - }); + routes[0].children?.push({ + path: "widgetdev", + element: , + }); } const router = createBrowserRouter(routes); @@ -34,7 +33,7 @@ const router = createBrowserRouter(routes); const root = createRoot(document.getElementById("root")!); root.render( - - - + + + ); diff --git a/src/widget.tsx b/src/widget.tsx index 548b864..653e35b 100644 --- a/src/widget.tsx +++ b/src/widget.tsx @@ -3,27 +3,24 @@ import { createRoot } from "react-dom/client"; // eslint-disable-next-line react-refresh/only-export-components export * from "./widgets"; - // old widget const widget = { - create({ selector, quizId, changeFaviconAndTitle = true }: { - selector: string; - quizId: string; - changeFaviconAndTitle: boolean; - }) { - const element = document.getElementById(selector); - if (!element) throw new Error("Element for widget doesn't exist"); + create({ + selector, + quizId, + changeFaviconAndTitle = true, + }: { + selector: string; + quizId: string; + changeFaviconAndTitle: boolean; + }) { + const element = document.getElementById(selector); + if (!element) throw new Error("Element for widget doesn't exist"); - const root = createRoot(element); + const root = createRoot(element); - root.render( - - ); - }, + root.render(); + }, }; export default widget; diff --git a/src/widgets/banner/BannerWidget.tsx b/src/widgets/banner/BannerWidget.tsx index 5e67f28..0f54dfd 100644 --- a/src/widgets/banner/BannerWidget.tsx +++ b/src/widgets/banner/BannerWidget.tsx @@ -2,31 +2,25 @@ import { BannerWidgetParams } from "@/model/widget/banner"; import { Root, createRoot } from "react-dom/client"; import QuizBanner from "./QuizBanner"; - export class BannerWidget { - root: Root | undefined; - element = document.createElement("div"); + root: Root | undefined; + element = document.createElement("div"); - constructor(props: BannerWidgetParams) { - this.element.style.setProperty("display", "none"); - document.body.appendChild(this.element); + constructor(props: BannerWidgetParams) { + this.element.style.setProperty("display", "none"); + document.body.appendChild(this.element); - this.root = createRoot(this.element); + this.root = createRoot(this.element); - this.render(props); - } + this.render(props); + } - render(props: BannerWidgetParams) { - this.root?.render( - this.destroy()} - /> - ); - } + render(props: BannerWidgetParams) { + this.root?.render( this.destroy()} />); + } - destroy() { - if (this.root) this.root.unmount(); - this.element.remove(); - } + destroy() { + if (this.root) this.root.unmount(); + this.element.remove(); + } } diff --git a/src/widgets/banner/QuizBanner.tsx b/src/widgets/banner/QuizBanner.tsx index cdda540..ba662d6 100644 --- a/src/widgets/banner/QuizBanner.tsx +++ b/src/widgets/banner/QuizBanner.tsx @@ -9,201 +9,216 @@ import RunningStripe from "../shared/RunningStripe"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; import { useAutoOpenTimer } from "../shared/useAutoOpenTimer"; - const PADDING = 10; export default function QuizBanner({ - quizId, - position, - onWidgetClose, - appealText = "Пройти тест", - quizHeaderText = "Заголовок квиза", - buttonTextColor, - buttonBackgroundColor, - autoShowQuizTime = null, - openOnLeaveAttempt, - buttonFlash = false, - hideOnMobile, - withShadow = false, - rounded = false, - bannerFullWidth = false, - pulsation = false, - autoShowWidgetTime = 0, - dialogDimensions, - fullScreen = false, + quizId, + position, + onWidgetClose, + appealText = "Пройти тест", + quizHeaderText = "Заголовок квиза", + buttonTextColor, + buttonBackgroundColor, + autoShowQuizTime = null, + openOnLeaveAttempt, + buttonFlash = false, + hideOnMobile, + withShadow = false, + rounded = false, + bannerFullWidth = false, + pulsation = false, + autoShowWidgetTime = 0, + dialogDimensions, + fullScreen = false, }: BannerWidgetComponentProps) { - const isMobile = useMediaQuery("(max-width: 600px)"); - const [isQuizShown, setIsQuizShown] = useState(false); - const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); - const isWidgetHidden = useAutoOpenTimer(autoShowWidgetTime); - const isQuizCompleted = useQuizCompletionStatus(quizId); - const preventQuizAutoShowRef = useRef(false); - const preventOpenOnLeaveAttemptRef = useRef(false); + const isMobile = useMediaQuery("(max-width: 600px)"); + const [isQuizShown, setIsQuizShown] = useState(false); + const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); + const isWidgetHidden = useAutoOpenTimer(autoShowWidgetTime); + const isQuizCompleted = useQuizCompletionStatus(quizId); + const preventQuizAutoShowRef = useRef(false); + const preventOpenOnLeaveAttemptRef = useRef(false); - useEffect(function setAutoShowQuizTimer() { - if (autoShowQuizTime === null || openOnLeaveAttempt) return; + useEffect( + function setAutoShowQuizTimer() { + if (autoShowQuizTime === null || openOnLeaveAttempt) return; - const timeout = setTimeout(() => { - setIsQuizShown(true); - }, autoShowQuizTime * 1000); - - return () => { - clearTimeout(timeout); - }; - }, [autoShowQuizTime, openOnLeaveAttempt]); - - useEffect(function attachLeaveListener() { - if (!openOnLeaveAttempt) return; - - const handleMouseLeave = () => { - if (!preventOpenOnLeaveAttemptRef.current) { - preventOpenOnLeaveAttemptRef.current = true; - setIsQuizShown(true); - } - }; - - document.addEventListener("mouseleave", handleMouseLeave); - - return () => { - document.removeEventListener("mouseleave", handleMouseLeave); - }; - }, [openOnLeaveAttempt]); - - function openQuiz() { - preventQuizAutoShowRef.current = true; + const timeout = setTimeout(() => { setIsQuizShown(true); - setIsFlashEnabled(false); - } + }, autoShowQuizTime * 1000); - if (hideOnMobile && isMobile) return null; + return () => { + clearTimeout(timeout); + }; + }, + [autoShowQuizTime, openOnLeaveAttempt] + ); - return createPortal( - - - - - - - - - - - - setIsQuizShown(false)} - disableScrollLock - paperSx={[ - (isMobile || fullScreen) ? { - width: "100%", - height: "100%", - maxHeight: "100%", - borderRadius: 0, - m: 0, - } : { - width: dialogDimensions?.width, - height: dialogDimensions?.height, - }, - ]} - /> - , - document.body - ); + useEffect( + function attachLeaveListener() { + if (!openOnLeaveAttempt) return; + + const handleMouseLeave = () => { + if (!preventOpenOnLeaveAttemptRef.current) { + preventOpenOnLeaveAttemptRef.current = true; + setIsQuizShown(true); + } + }; + + document.addEventListener("mouseleave", handleMouseLeave); + + return () => { + document.removeEventListener("mouseleave", handleMouseLeave); + }; + }, + [openOnLeaveAttempt] + ); + + function openQuiz() { + preventQuizAutoShowRef.current = true; + setIsQuizShown(true); + setIsFlashEnabled(false); + } + + if (hideOnMobile && isMobile) return null; + + return createPortal( + + + + + + + + + + + + setIsQuizShown(false)} + disableScrollLock + paperSx={[ + isMobile || fullScreen + ? { + width: "100%", + height: "100%", + maxHeight: "100%", + borderRadius: 0, + m: 0, + } + : { + width: dialogDimensions?.width, + height: dialogDimensions?.height, + }, + ]} + /> + , + document.body + ); } diff --git a/src/widgets/button/ButtonWidget.tsx b/src/widgets/button/ButtonWidget.tsx index d087a54..623b2a5 100644 --- a/src/widgets/button/ButtonWidget.tsx +++ b/src/widgets/button/ButtonWidget.tsx @@ -4,60 +4,59 @@ import { Root, createRoot } from "react-dom/client"; import { pollForSelector } from "../shared/pollForSelector"; import OpenQuizButton from "./OpenQuizButton"; - export class ButtonWidget { - root: Root | undefined; + root: Root | undefined; - constructor(props: ButtonWidgetParams) { - const { selector, selectorPollingTimeLimit = 60 } = props; + constructor(props: ButtonWidgetParams) { + const { selector, selectorPollingTimeLimit = 60 } = props; - const element = document.querySelector(selector); - if (element) { - this.root = createRoot(element); - this.render(props); + const element = document.querySelector(selector); + if (element) { + this.root = createRoot(element); + this.render(props); - return; - } - - if (!selectorPollingTimeLimit) { - console.error(`Не удалось найти элемент ${selector} для вставки виджета`); - return; - } - - pollForSelector(selector, selectorPollingTimeLimit, (element) => { - this.root = createRoot(element); - this.render(props); - }); + return; } - render(props: Omit) { - this.root?.render(); + if (!selectorPollingTimeLimit) { + console.error(`Не удалось найти элемент ${selector} для вставки виджета`); + return; } - destroy() { - if (this.root) this.root.unmount(); - } + pollForSelector(selector, selectorPollingTimeLimit, (element) => { + this.root = createRoot(element); + this.render(props); + }); + } + + render(props: Omit) { + this.root?.render(); + } + + destroy() { + if (this.root) this.root.unmount(); + } } export class ButtonWidgetFixed { - root: Root | undefined; - element = document.createElement("div"); + root: Root | undefined; + element = document.createElement("div"); - constructor(props: ButtonWidgetFixedParams) { - this.element.style.setProperty("display", "none"); - document.body.appendChild(this.element); + constructor(props: ButtonWidgetFixedParams) { + this.element.style.setProperty("display", "none"); + document.body.appendChild(this.element); - this.root = createRoot(this.element); + this.root = createRoot(this.element); - this.render(props); - } + this.render(props); + } - render(props: ButtonWidgetFixedParams) { - this.root?.render(createPortal(, document.body)); - } + render(props: ButtonWidgetFixedParams) { + this.root?.render(createPortal(, document.body)); + } - destroy() { - if (this.root) this.root.unmount(); - this.element.remove(); - } + destroy() { + if (this.root) this.root.unmount(); + this.element.remove(); + } } diff --git a/src/widgets/button/OpenQuizButton.tsx b/src/widgets/button/OpenQuizButton.tsx index 5a0ba39..1aae191 100644 --- a/src/widgets/button/OpenQuizButton.tsx +++ b/src/widgets/button/OpenQuizButton.tsx @@ -6,120 +6,129 @@ import QuizDialog from "../shared/QuizDialog"; import RunningStripe from "../shared/RunningStripe"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; - export default function OpenQuizButton({ - quizId, - fixedSide, - autoShowQuizTime = null, - dialogDimensions, - hideOnMobile, - openOnLeaveAttempt, - buttonFlash = false, - withShadow = false, - rounded = false, - buttonText = "Пройти квиз", - buttonTextColor, - buttonBackgroundColor, - fullScreen = false, + quizId, + fixedSide, + autoShowQuizTime = null, + dialogDimensions, + hideOnMobile, + openOnLeaveAttempt, + buttonFlash = false, + withShadow = false, + rounded = false, + buttonText = "Пройти квиз", + buttonTextColor, + buttonBackgroundColor, + fullScreen = false, }: ButtonWidgetComponentProps) { - const isMobile = useMediaQuery("(max-width: 600px)"); - const [isQuizShown, setIsQuizShown] = useState(false); - const isQuizCompleted = useQuizCompletionStatus(quizId); - const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); - const preventQuizAutoShowRef = useRef(false); - const preventOpenOnLeaveAttemptRef = useRef(false); + const isMobile = useMediaQuery("(max-width: 600px)"); + const [isQuizShown, setIsQuizShown] = useState(false); + const isQuizCompleted = useQuizCompletionStatus(quizId); + const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); + const preventQuizAutoShowRef = useRef(false); + const preventOpenOnLeaveAttemptRef = useRef(false); - useEffect(function setAutoShowQuizTimer() { - if (autoShowQuizTime === null || openOnLeaveAttempt) return; + useEffect( + function setAutoShowQuizTimer() { + if (autoShowQuizTime === null || openOnLeaveAttempt) return; - const timeout = setTimeout(() => { - setIsQuizShown(true); - }, autoShowQuizTime * 1000); - - return () => { - clearTimeout(timeout); - }; - }, [autoShowQuizTime, openOnLeaveAttempt]); - - useEffect(function attachLeaveListener() { - if (!openOnLeaveAttempt) return; - - const handleMouseLeave = () => { - if (!preventOpenOnLeaveAttemptRef.current) { - preventOpenOnLeaveAttemptRef.current = true; - setIsQuizShown(true); - } - }; - - document.addEventListener("mouseleave", handleMouseLeave); - - return () => { - document.removeEventListener("mouseleave", handleMouseLeave); - }; - }, [openOnLeaveAttempt]); - - function openQuiz() { - preventQuizAutoShowRef.current = true; + const timeout = setTimeout(() => { setIsQuizShown(true); - setIsFlashEnabled(false); - } + }, autoShowQuizTime * 1000); - if (hideOnMobile && isMobile) return null; + return () => { + clearTimeout(timeout); + }; + }, + [autoShowQuizTime, openOnLeaveAttempt] + ); - return ( - - - setIsQuizShown(false)} - paperSx={[ - (isMobile || fullScreen) ? { - width: "100%", - height: "100%", - maxHeight: "100%", - borderRadius: 0, - m: 0, - } : { - width: dialogDimensions?.width, - height: dialogDimensions?.height, - }, - ]} - /> - - ); + useEffect( + function attachLeaveListener() { + if (!openOnLeaveAttempt) return; + + const handleMouseLeave = () => { + if (!preventOpenOnLeaveAttemptRef.current) { + preventOpenOnLeaveAttemptRef.current = true; + setIsQuizShown(true); + } + }; + + document.addEventListener("mouseleave", handleMouseLeave); + + return () => { + document.removeEventListener("mouseleave", handleMouseLeave); + }; + }, + [openOnLeaveAttempt] + ); + + function openQuiz() { + preventQuizAutoShowRef.current = true; + setIsQuizShown(true); + setIsFlashEnabled(false); + } + + if (hideOnMobile && isMobile) return null; + + return ( + + + setIsQuizShown(false)} + paperSx={[ + isMobile || fullScreen + ? { + width: "100%", + height: "100%", + maxHeight: "100%", + borderRadius: 0, + m: 0, + } + : { + width: dialogDimensions?.width, + height: dialogDimensions?.height, + }, + ]} + /> + + ); } diff --git a/src/widgets/container/ContainerWidget.tsx b/src/widgets/container/ContainerWidget.tsx index 260cef3..960a29c 100644 --- a/src/widgets/container/ContainerWidget.tsx +++ b/src/widgets/container/ContainerWidget.tsx @@ -3,37 +3,36 @@ import { Root, createRoot } from "react-dom/client"; import { pollForSelector } from "../shared/pollForSelector"; import QuizContainer from "./QuizContainer"; - export class ContainerWidget { - root: Root | undefined; + root: Root | undefined; - constructor(props: ContainerWidgetParams) { - const { selector, selectorPollingTimeLimit = 60 } = props; + constructor(props: ContainerWidgetParams) { + const { selector, selectorPollingTimeLimit = 60 } = props; - const element = document.querySelector(selector); - if (element) { - this.root = createRoot(element); - this.render(props); + const element = document.querySelector(selector); + if (element) { + this.root = createRoot(element); + this.render(props); - return; - } - - if (!selectorPollingTimeLimit) { - console.error(`Не удалось найти элемент ${selector} для вставки виджета`); - return; - } - - pollForSelector(selector, selectorPollingTimeLimit, (element) => { - this.root = createRoot(element); - this.render(props); - }); + return; } - render(props: Omit) { - this.root?.render(); + if (!selectorPollingTimeLimit) { + console.error(`Не удалось найти элемент ${selector} для вставки виджета`); + return; } - destroy() { - if (this.root) this.root.unmount(); - } + pollForSelector(selector, selectorPollingTimeLimit, (element) => { + this.root = createRoot(element); + this.render(props); + }); + } + + render(props: Omit) { + this.root?.render(); + } + + destroy() { + if (this.root) this.root.unmount(); + } } diff --git a/src/widgets/container/QuizContainer.tsx b/src/widgets/container/QuizContainer.tsx index 45dda99..1e36c3d 100644 --- a/src/widgets/container/QuizContainer.tsx +++ b/src/widgets/container/QuizContainer.tsx @@ -3,27 +3,22 @@ import { ContainerWidgetComponentProps } from "@/model/widget/container"; import { Box, useMediaQuery } from "@mui/material"; import OpenQuizButton from "../button/OpenQuizButton"; - export default function QuizContainer(props: ContainerWidgetComponentProps) { - const { quizId, dimensions, showButtonOnMobile = false } = props; - const isMobile = useMediaQuery("(max-width: 600px)"); + const { quizId, dimensions, showButtonOnMobile = false } = props; + const isMobile = useMediaQuery("(max-width: 600px)"); - return showButtonOnMobile && isMobile ? ( - - ) : ( - - - - ); + return showButtonOnMobile && isMobile ? ( + + ) : ( + + + + ); } diff --git a/src/widgets/popup/PopupWidget.tsx b/src/widgets/popup/PopupWidget.tsx index 92e18e6..7d99332 100644 --- a/src/widgets/popup/PopupWidget.tsx +++ b/src/widgets/popup/PopupWidget.tsx @@ -2,26 +2,25 @@ import { PopupWidgetParams } from "@/model/widget/popup"; import { Root, createRoot } from "react-dom/client"; import QuizPopup from "./QuizPopup"; - export class PopupWidget { - root: Root | undefined; - element = document.createElement("div"); + root: Root | undefined; + element = document.createElement("div"); - constructor(props: PopupWidgetParams) { - this.element.style.setProperty("display", "none"); - document.body.appendChild(this.element); + constructor(props: PopupWidgetParams) { + this.element.style.setProperty("display", "none"); + document.body.appendChild(this.element); - this.root = createRoot(this.element); + this.root = createRoot(this.element); - this.render(props); - } + this.render(props); + } - render(props: PopupWidgetParams) { - this.root?.render(); - } + render(props: PopupWidgetParams) { + this.root?.render(); + } - destroy() { - if (this.root) this.root.unmount(); - this.element.remove(); - } -} + destroy() { + if (this.root) this.root.unmount(); + this.element.remove(); + } +} diff --git a/src/widgets/popup/QuizPopup.tsx b/src/widgets/popup/QuizPopup.tsx index bbcea4f..a8c8042 100644 --- a/src/widgets/popup/QuizPopup.tsx +++ b/src/widgets/popup/QuizPopup.tsx @@ -4,71 +4,78 @@ import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; import { useMediaQuery } from "@mui/material"; import { PopupWidgetComponentProps } from "@/model/widget/popup"; - export default function QuizPopup({ - quizId, - dialogDimensions, - autoShowQuizTime = null, - hideOnMobile = false, - openOnLeaveAttempt = false, - fullScreen = false, + quizId, + dialogDimensions, + autoShowQuizTime = null, + hideOnMobile = false, + openOnLeaveAttempt = false, + fullScreen = false, }: PopupWidgetComponentProps) { - const initialIsQuizShown = (autoShowQuizTime !== null || openOnLeaveAttempt) ? false : true; + const initialIsQuizShown = autoShowQuizTime !== null || openOnLeaveAttempt ? false : true; - const [isQuizShown, setIsQuizShown] = useState(initialIsQuizShown); - const isQuizCompleted = useQuizCompletionStatus(quizId); - const isMobile = useMediaQuery("(max-width: 600px)"); - const preventOpenOnLeaveAttemptRef = useRef(false); + const [isQuizShown, setIsQuizShown] = useState(initialIsQuizShown); + const isQuizCompleted = useQuizCompletionStatus(quizId); + const isMobile = useMediaQuery("(max-width: 600px)"); + const preventOpenOnLeaveAttemptRef = useRef(false); - useEffect(function setAutoShowQuizTimer() { - if (autoShowQuizTime === null || openOnLeaveAttempt) return; + useEffect( + function setAutoShowQuizTimer() { + if (autoShowQuizTime === null || openOnLeaveAttempt) return; - const timeout = setTimeout(() => { - setIsQuizShown(true); - }, autoShowQuizTime * 1000); + const timeout = setTimeout(() => { + setIsQuizShown(true); + }, autoShowQuizTime * 1000); - return () => { - clearTimeout(timeout); - }; - }, [autoShowQuizTime, openOnLeaveAttempt]); + return () => { + clearTimeout(timeout); + }; + }, + [autoShowQuizTime, openOnLeaveAttempt] + ); - useEffect(function attachLeaveListener() { - if (!openOnLeaveAttempt) return; + useEffect( + function attachLeaveListener() { + if (!openOnLeaveAttempt) return; - const handleMouseLeave = () => { - if (!preventOpenOnLeaveAttemptRef.current) { - preventOpenOnLeaveAttemptRef.current = true; - setIsQuizShown(true); + const handleMouseLeave = () => { + if (!preventOpenOnLeaveAttemptRef.current) { + preventOpenOnLeaveAttemptRef.current = true; + setIsQuizShown(true); + } + }; + + document.addEventListener("mouseleave", handleMouseLeave); + + return () => { + document.removeEventListener("mouseleave", handleMouseLeave); + }; + }, + [openOnLeaveAttempt] + ); + + if (isQuizCompleted) return null; + if (hideOnMobile && isMobile) return null; + + return ( + setIsQuizShown(false)} + paperSx={[ + isMobile || fullScreen + ? { + width: "100%", + height: "100%", + maxHeight: "100%", + borderRadius: 0, + m: 0, } - }; - - document.addEventListener("mouseleave", handleMouseLeave); - - return () => { - document.removeEventListener("mouseleave", handleMouseLeave); - }; - }, [openOnLeaveAttempt]); - - if (isQuizCompleted) return null; - if (hideOnMobile && isMobile) return null; - - return ( - setIsQuizShown(false)} - paperSx={[ - (isMobile || fullScreen) ? { - width: "100%", - height: "100%", - maxHeight: "100%", - borderRadius: 0, - m: 0, - } : { - width: dialogDimensions?.width, - height: dialogDimensions?.height, - }, - ]} - /> - ); + : { + width: dialogDimensions?.width, + height: dialogDimensions?.height, + }, + ]} + /> + ); } diff --git a/src/widgets/shared/QuizDialog.tsx b/src/widgets/shared/QuizDialog.tsx index fa3895c..829f4ec 100644 --- a/src/widgets/shared/QuizDialog.tsx +++ b/src/widgets/shared/QuizDialog.tsx @@ -1,79 +1,71 @@ import QuizAnswerer from "@/components/QuizAnswerer"; -import CloseIcon from '@mui/icons-material/Close'; +import CloseIcon from "@mui/icons-material/Close"; import { Dialog, IconButton, Slide, SlideProps, SxProps, Theme } from "@mui/material"; import { forwardRef } from "react"; - const SlideTransition = forwardRef((props, ref) => { - return ( - - ); + return ; }); interface Props { - open?: boolean; - quizId: string; - paperSx?: SxProps; - hideBackdrop?: boolean; - disableScrollLock?: boolean; - onClose?: () => void; + open?: boolean; + quizId: string; + paperSx?: SxProps; + hideBackdrop?: boolean; + disableScrollLock?: boolean; + onClose?: () => void; } export default function QuizDialog({ - open = true, - quizId, - paperSx = [], - hideBackdrop, - disableScrollLock, - onClose + open = true, + quizId, + paperSx = [], + hideBackdrop, + disableScrollLock, + onClose, }: Props) { - - return ( - - - - - - - ); + return ( + + + + + + + ); } diff --git a/src/widgets/shared/RunningStripe.tsx b/src/widgets/shared/RunningStripe.tsx index a52aed3..43bbb56 100644 --- a/src/widgets/shared/RunningStripe.tsx +++ b/src/widgets/shared/RunningStripe.tsx @@ -1,36 +1,34 @@ import { Box, SxProps, Theme } from "@mui/material"; - interface Props { - sx?: SxProps; + sx?: SxProps; } export default function RunningStripe({ sx = [] }: Props) { - - return ( - - ); + return ( + + ); } diff --git a/src/widgets/shared/pollForSelector.ts b/src/widgets/shared/pollForSelector.ts index fa4505f..69fed1f 100644 --- a/src/widgets/shared/pollForSelector.ts +++ b/src/widgets/shared/pollForSelector.ts @@ -1,25 +1,25 @@ const SELECTOR_POLLING_INTERVAL = 5000; export function pollForSelector( - selector: string, - selectorPollingTimeLimit: number, - onSuccess: (element: Element) => void, + selector: string, + selectorPollingTimeLimit: number, + onSuccess: (element: Element) => void ) { - const deadline = Date.now() + selectorPollingTimeLimit * 1000; + const deadline = Date.now() + selectorPollingTimeLimit * 1000; - const interval = setInterval(() => { - const element = document.querySelector(selector); + const interval = setInterval(() => { + const element = document.querySelector(selector); - if (Date.now() > deadline) { - clearInterval(interval); - console.error(`Не удалось найти элемент ${selector} для вставки виджета`); - return; - } - if (!element) { - return; - } + if (Date.now() > deadline) { + clearInterval(interval); + console.error(`Не удалось найти элемент ${selector} для вставки виджета`); + return; + } + if (!element) { + return; + } - clearInterval(interval); - onSuccess(element); - }, SELECTOR_POLLING_INTERVAL); + clearInterval(interval); + onSuccess(element); + }, SELECTOR_POLLING_INTERVAL); } diff --git a/src/widgets/shared/useAutoOpenTimer.ts b/src/widgets/shared/useAutoOpenTimer.ts index 1eb839e..7fedf2d 100644 --- a/src/widgets/shared/useAutoOpenTimer.ts +++ b/src/widgets/shared/useAutoOpenTimer.ts @@ -1,18 +1,20 @@ import { useEffect, useState } from "react"; - export function useAutoOpenTimer(autoOpenTime: number) { - const [isWidgetHidden, setIsWidgetHidden] = useState(autoOpenTime ? true : false); + const [isWidgetHidden, setIsWidgetHidden] = useState(autoOpenTime ? true : false); - useEffect(function setAutoOpenTimer() { - if (!autoOpenTime) return; + useEffect( + function setAutoOpenTimer() { + if (!autoOpenTime) return; - const timeout = setTimeout(() => setIsWidgetHidden(false), autoOpenTime * 1000); + const timeout = setTimeout(() => setIsWidgetHidden(false), autoOpenTime * 1000); - return () => { - clearTimeout(timeout); - }; - }, [autoOpenTime]); + return () => { + clearTimeout(timeout); + }; + }, + [autoOpenTime] + ); - return isWidgetHidden; + return isWidgetHidden; } diff --git a/src/widgets/shared/useQuizCompletionStatus.ts b/src/widgets/shared/useQuizCompletionStatus.ts index 0d6ee8a..986a230 100644 --- a/src/widgets/shared/useQuizCompletionStatus.ts +++ b/src/widgets/shared/useQuizCompletionStatus.ts @@ -1,17 +1,13 @@ import { useMemo } from "react"; - export function useQuizCompletionStatus(quizId: string): boolean { - return useMemo(() => { - const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); + return useMemo(() => { + const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); - if ( - typeof sessions[quizId] === "number" - && Date.now() - sessions[quizId] < 86400000 - ) { - return true; - } + if (typeof sessions[quizId] === "number" && Date.now() - sessions[quizId] < 86400000) { + return true; + } - return false; - }, [quizId]); + return false; + }, [quizId]); } diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index 5af8d9a..1b14685 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -8,111 +8,115 @@ import { useAutoOpenTimer } from "../shared/useAutoOpenTimer"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; import { SideWidgetComponentProps } from "@/model/widget/side"; - const PADDING = 10; const WIDGET_DEFAULT_WIDTH = "600px"; const WIDGET_DEFAULT_HEIGHT = "800px"; export default function QuizSideButton({ - quizId, - position, - buttonBackgroundColor, - buttonTextColor, - dialogDimensions, - fullScreen = false, - buttonFlash = false, - autoShowWidgetTime = 0, - autoShowQuizTime = null, - hideOnMobile = false, + quizId, + position, + buttonBackgroundColor, + buttonTextColor, + dialogDimensions, + fullScreen = false, + buttonFlash = false, + autoShowWidgetTime = 0, + autoShowQuizTime = null, + hideOnMobile = false, }: SideWidgetComponentProps) { - const [isQuizShown, setIsQuizShown] = useState(false); - const isMobile = useMediaQuery("(max-width: 600px)"); - const isQuizCompleted = useQuizCompletionStatus(quizId); - const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); - const isWidgetHidden = useAutoOpenTimer(autoShowWidgetTime); - const preventQuizAutoShowRef = useRef(false); + const [isQuizShown, setIsQuizShown] = useState(false); + const isMobile = useMediaQuery("(max-width: 600px)"); + const isQuizCompleted = useQuizCompletionStatus(quizId); + const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); + const isWidgetHidden = useAutoOpenTimer(autoShowWidgetTime); + const preventQuizAutoShowRef = useRef(false); - useEffect(function setAutoShowQuizTimer() { - if (autoShowQuizTime === null) return; + useEffect( + function setAutoShowQuizTimer() { + if (autoShowQuizTime === null) return; - const timeout = setTimeout(() => { - if (!preventQuizAutoShowRef.current) setIsQuizShown(true); - }, autoShowQuizTime * 1000); + const timeout = setTimeout(() => { + if (!preventQuizAutoShowRef.current) setIsQuizShown(true); + }, autoShowQuizTime * 1000); - return () => { - clearTimeout(timeout); - }; - }, [autoShowQuizTime]); + return () => { + clearTimeout(timeout); + }; + }, + [autoShowQuizTime] + ); - function openQuiz() { - preventQuizAutoShowRef.current = true; - setIsQuizShown(true); - setIsFlashEnabled(false); - } + function openQuiz() { + preventQuizAutoShowRef.current = true; + setIsQuizShown(true); + setIsFlashEnabled(false); + } - if (hideOnMobile && isMobile) return null; + if (hideOnMobile && isMobile) return null; - return createPortal( - - setIsQuizShown(false)} - hideBackdrop - disableScrollLock - paperSx={[ - { - m: 0, - }, - (isMobile || fullScreen) ? { - width: "100%", - height: "100%", - maxHeight: "100%", - borderRadius: 0, - } : { - position: "absolute", - bottom: PADDING, - right: position === "right" ? PADDING : undefined, - left: position === "left" ? PADDING : undefined, - width: dialogDimensions?.width ?? WIDGET_DEFAULT_WIDTH, - maxWidth: `calc(100% - ${PADDING * 2}px)`, - height: dialogDimensions?.height ?? WIDGET_DEFAULT_HEIGHT, - maxHeight: `calc(100% - ${PADDING * 2}px)`, - }, - ]} - /> - - - - , - document.body - ); + return createPortal( + + setIsQuizShown(false)} + hideBackdrop + disableScrollLock + paperSx={[ + { + m: 0, + }, + isMobile || fullScreen + ? { + width: "100%", + height: "100%", + maxHeight: "100%", + borderRadius: 0, + } + : { + position: "absolute", + bottom: PADDING, + right: position === "right" ? PADDING : undefined, + left: position === "left" ? PADDING : undefined, + width: dialogDimensions?.width ?? WIDGET_DEFAULT_WIDTH, + maxWidth: `calc(100% - ${PADDING * 2}px)`, + height: dialogDimensions?.height ?? WIDGET_DEFAULT_HEIGHT, + maxHeight: `calc(100% - ${PADDING * 2}px)`, + }, + ]} + /> + + + + , + document.body + ); } diff --git a/src/widgets/side/SideWidget.tsx b/src/widgets/side/SideWidget.tsx index c1c1863..9469c3d 100644 --- a/src/widgets/side/SideWidget.tsx +++ b/src/widgets/side/SideWidget.tsx @@ -2,26 +2,25 @@ import { SideWidgetParams } from "@/model/widget/side"; import { Root, createRoot } from "react-dom/client"; import QuizSideButton from "./QuizSideButton"; - export class SideWidget { - root: Root | undefined; - element = document.createElement("div"); + root: Root | undefined; + element = document.createElement("div"); - constructor(props: SideWidgetParams) { - this.element.style.setProperty("display", "none"); - document.body.appendChild(this.element); + constructor(props: SideWidgetParams) { + this.element.style.setProperty("display", "none"); + document.body.appendChild(this.element); - this.root = createRoot(this.element); + this.root = createRoot(this.element); - this.render(props); - } + this.render(props); + } - render(props: SideWidgetParams) { - this.root?.render(); - } + render(props: SideWidgetParams) { + this.root?.render(); + } - destroy() { - if (this.root) this.root.unmount(); - this.element.remove(); - } + destroy() { + if (this.root) this.root.unmount(); + this.element.remove(); + } } diff --git a/tsconfig.json b/tsconfig.json index c506081..4f90474 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,63 +1,36 @@ { - "compilerOptions": { - "target": "ES2021", - "useDefineForClassFields": true, - "lib": [ - "ES2021", - "DOM", - "DOM.Iterable" - ], - "module": "ESNext", - "skipLibCheck": true, - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - /* Linting */ - "strict": true, - "noFallthroughCasesInSwitch": true, - "paths": { - "@/*": [ - "./lib/*" - ], - "@ui_kit/*": [ - "./lib/ui_kit/*" - ], - "@icons/*": [ - "./lib/assets/icons/*" - ], - "@stores/*": [ - "./lib/stores/*" - ], - "@api/*": [ - "./lib/api/*" - ], - "@model/*": [ - "./lib/model/*" - ], - "@utils/*": [ - "./lib/utils/*" - ], - "@contexts/*": [ - "./lib/contexts/*" - ] - } - }, - "include": [ - "lib", - "src" - ], - "exclude": [ - "cypress.config.ts", - "cypress", - "node_modules", - ], - "references": [ - { - "path": "./tsconfig.node.json" - } - ] + "compilerOptions": { + "target": "ES2021", + "useDefineForClassFields": true, + "lib": ["ES2021", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + /* Linting */ + "strict": true, + "noFallthroughCasesInSwitch": true, + "paths": { + "@/*": ["./lib/*"], + "@ui_kit/*": ["./lib/ui_kit/*"], + "@icons/*": ["./lib/assets/icons/*"], + "@stores/*": ["./lib/stores/*"], + "@api/*": ["./lib/api/*"], + "@model/*": ["./lib/model/*"], + "@utils/*": ["./lib/utils/*"], + "@contexts/*": ["./lib/contexts/*"] + } + }, + "include": ["lib", "src"], + "exclude": ["cypress.config.ts", "cypress", "node_modules"], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] } diff --git a/vite.config.package.ts b/vite.config.package.ts index eb39c78..b14783f 100644 --- a/vite.config.package.ts +++ b/vite.config.package.ts @@ -6,42 +6,42 @@ import { alias } from "./vite.config"; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react(), dts({ include: ["lib"] })], - resolve: { - alias, + plugins: [react(), dts({ include: ["lib"] })], + resolve: { + alias, + }, + build: { + lib: { + entry: resolve(__dirname, "lib/index.ts"), + formats: ["es"], + fileName: "index", }, - build: { - lib: { - entry: resolve(__dirname, "lib/index.ts"), - formats: ["es"], - fileName: "index" - }, - outDir: "dist-package", - copyPublicDir: false, - minify: false, - rollupOptions: { - external: [ - "@emoji-mart/data", - "@emoji-mart/react", - "@emotion/react", - "@emotion/styled", - "@mui/icons-material", - "@mui/material", - "@mui/x-date-pickers", - "axios", - "emoji-mart", - "immer", - "moment", - "nanoid", - "notistack", - "react-dom", - "react-error-boundary", - "react-router-dom", - "react", - "swr", - "use-debounce", - "zustand", - ], - }, + outDir: "dist-package", + copyPublicDir: false, + minify: false, + rollupOptions: { + external: [ + "@emoji-mart/data", + "@emoji-mart/react", + "@emotion/react", + "@emotion/styled", + "@mui/icons-material", + "@mui/material", + "@mui/x-date-pickers", + "axios", + "emoji-mart", + "immer", + "moment", + "nanoid", + "notistack", + "react-dom", + "react-error-boundary", + "react-router-dom", + "react", + "swr", + "use-debounce", + "zustand", + ], }, + }, }); diff --git a/vite.config.ts b/vite.config.ts index 6446f31..ee76c62 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,23 +3,23 @@ import { resolve } from "path"; import { defineConfig } from "vite"; export const alias = { - "@": resolve(__dirname, "./lib/"), - "@ui_kit": resolve(__dirname, "./lib/ui_kit"), - "@icons": resolve(__dirname, "./lib/assets/icons"), - "@stores": resolve(__dirname, "./lib/stores"), - "@api": resolve(__dirname, "./lib/api"), - "@model": resolve(__dirname, "./lib/model"), - "@utils": resolve(__dirname, "./lib/utils"), - "@contexts": resolve(__dirname, "./lib/contexts"), + "@": resolve(__dirname, "./lib/"), + "@ui_kit": resolve(__dirname, "./lib/ui_kit"), + "@icons": resolve(__dirname, "./lib/assets/icons"), + "@stores": resolve(__dirname, "./lib/stores"), + "@api": resolve(__dirname, "./lib/api"), + "@model": resolve(__dirname, "./lib/model"), + "@utils": resolve(__dirname, "./lib/utils"), + "@contexts": resolve(__dirname, "./lib/contexts"), }; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], - resolve: { - alias, - }, - define: { - "process.env": process.env, - }, + plugins: [react()], + resolve: { + alias, + }, + define: { + "process.env": process.env, + }, }); diff --git a/vite.config.widget.ts b/vite.config.widget.ts index cc089ae..a5dddc6 100644 --- a/vite.config.widget.ts +++ b/vite.config.widget.ts @@ -1,28 +1,28 @@ -import react from "@vitejs/plugin-react"; -import { defineConfig } from "vite"; -import { alias } from "./vite.config"; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], - resolve: { - alias, - }, - build: { - copyPublicDir: false, - rollupOptions: { - input: "src/widget.tsx", - preserveEntrySignatures: "allow-extension", - output: { - manualChunks: undefined, - entryFileNames: "[name].js", - chunkFileNames: "[name].js", - assetFileNames: "[name].[ext]", - }, - }, - outDir: "widget", - }, - define: { - "process.env": process.env, - }, -}); +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; +import { alias } from "./vite.config"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias, + }, + build: { + copyPublicDir: false, + rollupOptions: { + input: "src/widget.tsx", + preserveEntrySignatures: "allow-extension", + output: { + manualChunks: undefined, + entryFileNames: "[name].js", + chunkFileNames: "[name].js", + assetFileNames: "[name].[ext]", + }, + }, + outDir: "widget", + }, + define: { + "process.env": process.env, + }, +}); diff --git a/widget-test.html b/widget-test.html index f73731f..febad7c 100644 --- a/widget-test.html +++ b/widget-test.html @@ -1,81 +1,108 @@ - - + Quiz - + - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qu +

- - +