From f839ec9b8a8ec0ee9f22489ab7d47d5c31a7c925 Mon Sep 17 00:00:00 2001 From: aleksandr-raw <104529174+aleksandr-raw@users.noreply.github.com> Date: Wed, 17 Apr 2024 20:08:40 +0400 Subject: [PATCH 01/27] refactored TS --- lib/api/quizRelase.ts | 55 ++++++++++++++++--- lib/assets/icons/Info.tsx | 2 +- .../ViewPublicationPage/ContactForm.tsx | 52 ++++++++++++------ .../ViewPublicationPage/questions/File.tsx | 2 +- .../ViewPublicationPage/questions/Text.tsx | 4 +- lib/model/settingsData.ts | 2 +- lib/stores/quizView.ts | 6 +- lib/ui_kit/CustomTextField.tsx | 3 +- 8 files changed, 94 insertions(+), 32 deletions(-) diff --git a/lib/api/quizRelase.ts b/lib/api/quizRelase.ts index d0ab59e..059eb6d 100644 --- a/lib/api/quizRelase.ts +++ b/lib/api/quizRelase.ts @@ -39,7 +39,13 @@ const DeviceType = device.type; let Device = md.mobile(); if (Device === null) { Device = userAgent; } -export const publicationMakeRequest = ({ url, body }: any) => { +type PublicationMakeRequestParams = { + url: string; + body: FormData; + method: "POST"; +} + +export const publicationMakeRequest = ({ url, body }: PublicationMakeRequestParams) => { return axios(url, { data: body, headers: { @@ -57,7 +63,7 @@ export const publicationMakeRequest = ({ url, body }: any) => { export async function getData(quizId: string): Promise<{ data: GetQuizDataResponse | null; isRecentlyCompleted: boolean; - error?: any; + error?: AxiosError; }> { try { const { data, headers } = await axios( @@ -117,7 +123,14 @@ export async function getQuizData(quizId: string) { return res; } -export function sendAnswer({ questionId, body, qid, preview }: any) { +type SendAnswerProps = { + questionId: string; + body: string | string[]; + qid: string; + preview: boolean; +} + +export function sendAnswer({ questionId, body, qid, preview }: SendAnswerProps) { if (preview) return; const formData = new FormData(); @@ -139,11 +152,26 @@ export function sendAnswer({ questionId, body, qid, preview }: any) { } //body ={file, filename} -export function sendFile({ questionId, body, qid, preview }: any) { - if (preview) return; +type SendFileParams = { + questionId: string; + body: { + name: string; + file: File; + preview: boolean; + }; + qid: string; +} + +type Answer = { + question_id: string; + content: string; +}; + +export function sendFile({ questionId, body, qid }: SendFileParams) { + if (body.preview) return; const formData = new FormData(); - const answers: any = [ + const answers: Answer[] = [ { question_id: questionId, content: "file:" + body.name, @@ -163,7 +191,20 @@ export function sendFile({ questionId, body, qid, preview }: any) { } //форма контактов -export function sendFC({ questionId, body, qid, preview }: any) { +export type SendFCParams = { + 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(); diff --git a/lib/assets/icons/Info.tsx b/lib/assets/icons/Info.tsx index b56f67f..f319760 100644 --- a/lib/assets/icons/Info.tsx +++ b/lib/assets/icons/Info.tsx @@ -4,7 +4,7 @@ type InfoProps = { width?: number; height?: number; sx?: SxProps; - onClick?: any; + onClick?: () => void; className?: string; color?: string }; diff --git a/lib/components/ViewPublicationPage/ContactForm.tsx b/lib/components/ViewPublicationPage/ContactForm.tsx index 1fb9c2f..3a1b579 100644 --- a/lib/components/ViewPublicationPage/ContactForm.tsx +++ b/lib/components/ViewPublicationPage/ContactForm.tsx @@ -18,7 +18,7 @@ import CustomCheckbox from "@ui_kit/CustomCheckbox"; import { FC, useRef, useState } from "react"; import { DESIGN_LIST } from "@/utils/designList"; -import { sendFC } from "@api/quizRelase"; +import {sendFC, SendFCParams} from "@api/quizRelase"; import { useQuizData } from "@contexts/QuizDataContext"; import { NameplateLogo } from "@icons/NameplateLogo"; import { QuizQuestionResult } from "@model/questionTypes/result"; @@ -26,7 +26,31 @@ import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; import { quizThemes } from "@utils/themes/Publication/themePublication"; import { enqueueSnackbar } from "notistack"; import { useRootContainerSize } from "../../contexts/RootContainerWidthContext"; +import { + FormContactFieldData, + FormContactFieldName, +} from "@model/settingsData.ts"; +type InputProps = { + title: string; + desc: string; + Icon: FC<{ color: string; backgroundColor: string; }>; + onChange: TextFieldProps["onChange"]; + id: string; +}; + +type InputsProps = { + name: string; + setName: React.Dispatch>; + email: string; + setEmail: React.Dispatch>; + phone: string; + setPhone: React.Dispatch>; + text: string; + setText: React.Dispatch>; + adress: string; + setAdress: React.Dispatch>; +}; const TextField = MuiTextField as unknown as FC; // temporary fix ts(2590) const EMAIL_REGEXP = /^(([^<>()[\].,:\s@"]+(\.[^<>()[\].,:\s@"]+)*)|(".+"))@(([^<>()[\].,:\s@"]+\.)+[^<>()[\].,:\s@"]{2,})$/iu; @@ -71,7 +95,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { const inputHC = async () => { const FC = settings.cfg.formContact.fields || settings.cfg.formContact; - const body = {} as any; + const body:SendFCParams["body"] = {} if (name.length > 0) body.name = name; if (email.length > 0) body.email = email; if (phone.length > 0) body.phone = phone; @@ -98,19 +122,19 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { } }; - const FCcopy: any = settings.cfg.formContact.fields || settings.cfg.formContact; + const FCcopy: Record = settings.cfg.formContact.fields || settings.cfg.formContact; - const filteredFC: any = {}; + const filteredFC: Partial> = {}; for (const i in FCcopy) { - const field = FCcopy[i]; + const field = FCcopy[i as keyof typeof FCcopy]; if (field.used) { - filteredFC[i] = field; + filteredFC[i as FormContactFieldName] = field; } } - + async function handleShowResultsClick() { - const FC: any = settings.cfg.formContact.fields; + const FC = settings.cfg.formContact.fields; if (FC["email"].used !== EMAIL_REGEXP.test(email)) { return enqueueSnackbar("введена некорректная почта"); } @@ -130,7 +154,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { try { await inputHC(); fireOnce.current = false; - const sessions: any = JSON.parse( + const sessions = JSON.parse( localStorage.getItem("sessions") || "{}" ); sessions[quizId] = Date.now(); @@ -370,7 +394,7 @@ const Inputs = ({ setText, adress, setAdress, -}: any) => { +}: InputsProps) => { const { settings } = useQuizData(); const FC = settings.cfg.formContact.fields; @@ -444,13 +468,7 @@ const Inputs = ({ } }; -const CustomInput = ({ title, desc, Icon, onChange, id }: { - id: string; - title: string; - desc: string; - Icon: FC<{ color: string; backgroundColor: string; }>; - onChange: TextFieldProps["onChange"]; -}) => { +const CustomInput = ({ title, desc, Icon, onChange, id }: InputProps) => { const theme = useTheme(); const isMobile = useRootContainerSize() < 600; const { settings } = useQuizData(); diff --git a/lib/components/ViewPublicationPage/questions/File.tsx b/lib/components/ViewPublicationPage/questions/File.tsx index e48c08a..4c046db 100644 --- a/lib/components/ViewPublicationPage/questions/File.tsx +++ b/lib/components/ViewPublicationPage/questions/File.tsx @@ -21,7 +21,7 @@ import { useRootContainerSize } from "../../../contexts/RootContainerWidthContex import type { QuizQuestionFile } from "../../../model/questionTypes/file"; import { ACCEPT_SEND_FILE_TYPES_MAP, MAX_FILE_SIZE, UPLOAD_FILE_DESCRIPTIONS_MAP } from "../tools/fileUpload"; -type ModalWarningType = "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | null; +export type ModalWarningType = "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | null; type FileProps = { currentQuestion: QuizQuestionFile; diff --git a/lib/components/ViewPublicationPage/questions/Text.tsx b/lib/components/ViewPublicationPage/questions/Text.tsx index 3f3844a..bb6435d 100644 --- a/lib/components/ViewPublicationPage/questions/Text.tsx +++ b/lib/components/ViewPublicationPage/questions/Text.tsx @@ -8,7 +8,7 @@ import { import CustomTextField from "@ui_kit/CustomTextField"; -import { useQuizViewStore } from "@stores/quizView"; +import {Answer, useQuizViewStore} from "@stores/quizView"; import { sendAnswer } from "@api/quizRelase"; import { useQuizData } from "@contexts/QuizDataContext"; @@ -114,7 +114,7 @@ export const Text = ({ currentQuestion, stepNumber }: TextProps) => { interface Props { currentQuestion: QuizQuestionText; - answer: any; + answer?: Answer; inputHC: (a: string) => void; stepNumber?: number | null; } diff --git a/lib/model/settingsData.ts b/lib/model/settingsData.ts index e0d0428..d9dcd5e 100644 --- a/lib/model/settingsData.ts +++ b/lib/model/settingsData.ts @@ -117,7 +117,7 @@ export type FormContactFieldName = | "text" | "address"; -type FormContactFieldData = { +export type FormContactFieldData = { text: string; innerText: string; key: string; diff --git a/lib/stores/quizView.ts b/lib/stores/quizView.ts index abf9181..07f87db 100644 --- a/lib/stores/quizView.ts +++ b/lib/stores/quizView.ts @@ -6,9 +6,11 @@ import { createContext, useContext } from "react"; import { createStore, useStore } from "zustand"; import { immer } from "zustand/middleware/immer"; +export type Answer = string | string[] | Moment; + type QuestionAnswer = { questionId: string; - answer: string | string[] | Moment; + answer: Answer }; type OwnVariant = { @@ -99,4 +101,4 @@ export const createQuizViewStore = () => createStore; // temporary fix ts(2590) interface CustomTextFieldProps { placeholder: string; - value?: string; + value?: Answer; error?: string; onChange?: (event: ChangeEvent) => void; onKeyDown?: (event: KeyboardEvent) => void; From 2ee1a722591d315f8d20dad08987fa66e82e80a3 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 19 Apr 2024 13:22:07 +0300 Subject: [PATCH 02/27] add changeFaviconAndTitle param to widget --- src/WidgetApp.tsx | 13 ------------- src/widget.tsx | 7 ++++--- 2 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 src/WidgetApp.tsx diff --git a/src/WidgetApp.tsx b/src/WidgetApp.tsx deleted file mode 100644 index fc21412..0000000 --- a/src/WidgetApp.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import QuizAnswerer from "../lib/components/QuizAnswerer"; - - -interface Props { - quizId: string; -} - -export default function WidgetApp({ quizId }: Props) { - - return ( - - ); -} diff --git a/src/widget.tsx b/src/widget.tsx index 020b58b..2364cec 100644 --- a/src/widget.tsx +++ b/src/widget.tsx @@ -1,20 +1,21 @@ +import QuizAnswerer from "@/components/QuizAnswerer"; import { Root, createRoot } from "react-dom/client"; -import WidgetApp from "./WidgetApp"; let root: Root | undefined = undefined; const widget = { - create({ selector, quizId }: { + 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"); root = createRoot(element); - root.render(); + root.render(); }, unmount() { if (root) root.unmount(); From 21ee9746807d36101ec036d9142d152bd7cde674 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Mon, 22 Apr 2024 11:55:19 +0300 Subject: [PATCH 03/27] fix: useYandexMetrics --- .../ViewPublicationPage.tsx | 220 ++++++++++-------- ...eYandexMetrica.tsx => useYandexMetrics.ts} | 27 ++- 2 files changed, 134 insertions(+), 113 deletions(-) rename lib/utils/hooks/{useYandexMetrica.tsx => useYandexMetrics.ts} (50%) diff --git a/lib/components/ViewPublicationPage/ViewPublicationPage.tsx b/lib/components/ViewPublicationPage/ViewPublicationPage.tsx index f1b2090..5756519 100644 --- a/lib/components/ViewPublicationPage/ViewPublicationPage.tsx +++ b/lib/components/ViewPublicationPage/ViewPublicationPage.tsx @@ -14,115 +14,133 @@ import { StartPageViewPublication } from "./StartPageViewPublication"; import NextButton from "./tools/NextButton"; import PrevButton from "./tools/PrevButton"; import QuestionSelect from "./QuestionSelect"; -import {useYandexMetrica} from "@utils/hooks/useYandexMetrica.tsx"; +import { useYandexMetrics } from "@/utils/hooks/useYandexMetrics"; export default function ViewPublicationPage() { - const { settings, recentlyCompleted, quizId, preview, changeFaviconAndTitle } = useQuizData(); - const answers = useQuizViewStore(state => state.answers); - let currentQuizStep = useQuizViewStore((state) => state.currentQuizStep); - const { - currentQuestion, - currentQuestionStepNumber, - isNextButtonEnabled, - isPreviousButtonEnabled, - moveToPrevQuestion, - moveToNextQuestion, - showResultAfterContactForm, - setQuestion, - } = useQuestionFlowControl(); + const { + settings, + recentlyCompleted, + quizId, + preview, + changeFaviconAndTitle, + } = useQuizData(); + const answers = useQuizViewStore((state) => state.answers); + let currentQuizStep = useQuizViewStore((state) => state.currentQuizStep); + const { + currentQuestion, + currentQuestionStepNumber, + isNextButtonEnabled, + isPreviousButtonEnabled, + moveToPrevQuestion, + moveToNextQuestion, + showResultAfterContactForm, + setQuestion, + } = useQuestionFlowControl(); + useYandexMetrics(settings?.cfg?.yandexMetricNumber); - const isAnswer = answers.some(ans => ans.questionId === currentQuestion?.id); + const isAnswer = answers.some( + (ans) => ans.questionId === currentQuestion?.id + ); - const yandexMetricNumber = settings?.cfg?.yandexMetricNumber - useYandexMetrica(yandexMetricNumber); + useEffect( + function setFaviconAndTitle() { + if (!changeFaviconAndTitle) return; - useEffect(function setFaviconAndTitle() { - if (!changeFaviconAndTitle) return; + const link = document.querySelector('link[rel="icon"]'); + if (link && settings.cfg.startpage.favIcon) { + link.setAttribute("href", settings.cfg.startpage.favIcon); + } - const link = document.querySelector('link[rel="icon"]'); - if (link && settings.cfg.startpage.favIcon) { - link.setAttribute("href", settings.cfg.startpage.favIcon); - } + document.title = settings.name; + }, + [changeFaviconAndTitle, settings.cfg.startpage.favIcon, settings.name] + ); - document.title = settings.name; - }, [changeFaviconAndTitle, settings.cfg.startpage.favIcon, settings.name]); - - if (recentlyCompleted) throw new Error("Quiz already completed"); - if (currentQuizStep === "startpage" && settings.cfg.noStartPage) currentQuizStep = "question"; - - if (!currentQuestion) return ( - - Вопрос не выбран - - ); - - let quizStepElement: ReactElement; - switch (currentQuizStep) { - case "startpage": { - quizStepElement = ; - break; - } - case "question": { - if (currentQuestion.type === "result") { - quizStepElement = ; - break; - } - - quizStepElement = ( - } - nextButton={ - { - if (!isAnswer) { - try { - await sendAnswer({ - questionId: currentQuestion.id, - body: "", - qid: quizId, - preview - }); - } catch (e) { - enqueueSnackbar("ответ не был засчитан"); - } - } - moveToNextQuestion(); - }} - /> - } - questionSelect={ - - } - /> - ); - break; - } - case "contactform": { - quizStepElement = ( - - ); - break; - } - default: - notReachable(currentQuizStep); - } + if (recentlyCompleted) throw new Error("Quiz already completed"); + if (currentQuizStep === "startpage" && settings.cfg.noStartPage) + currentQuizStep = "question"; + if (!currentQuestion) return ( - - {quizStepElement} - + + + Вопрос не выбран + + ); + + let quizStepElement: ReactElement; + switch (currentQuizStep) { + case "startpage": { + quizStepElement = ; + break; + } + case "question": { + if (currentQuestion.type === "result") { + quizStepElement = ; + break; + } + + quizStepElement = ( + + } + nextButton={ + { + if (!isAnswer) { + try { + await sendAnswer({ + questionId: currentQuestion.id, + body: "", + qid: quizId, + preview, + }); + } catch (e) { + enqueueSnackbar("ответ не был засчитан"); + } + } + moveToNextQuestion(); + }} + /> + } + questionSelect={ + + } + /> + ); + break; + } + case "contactform": { + quizStepElement = ( + + ); + break; + } + default: + notReachable(currentQuizStep); + } + + return ( + + {quizStepElement} + + ); } diff --git a/lib/utils/hooks/useYandexMetrica.tsx b/lib/utils/hooks/useYandexMetrics.ts similarity index 50% rename from lib/utils/hooks/useYandexMetrica.tsx rename to lib/utils/hooks/useYandexMetrics.ts index 0a0e974..6ff5e26 100644 --- a/lib/utils/hooks/useYandexMetrica.tsx +++ b/lib/utils/hooks/useYandexMetrics.ts @@ -1,11 +1,14 @@ import { useEffect } from "react"; -export const useYandexMetrica = (yandexMetricNumber: number | undefined) => { - useEffect(() => { - if (yandexMetricNumber) { - const script = document.createElement("script"); - script.type = "text/javascript"; - script.innerHTML = ` +export const useYandexMetrics = (yandexMetricNumber: number | undefined) => { + useEffect(() => { + if ( + typeof yandexMetricNumber === "number" && + !Number.isNaN(yandexMetricNumber) + ) { + const script = document.createElement("script"); + script.type = "text/javascript"; + script.innerHTML = ` (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; m[i].l=1*new Date(); for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }} @@ -19,11 +22,11 @@ export const useYandexMetrica = (yandexMetricNumber: number | undefined) => { webvisor:true }); `; - document.body.appendChild(script); + document.body.appendChild(script); - const noscript = document.createElement("noscript"); - noscript.innerHTML = `
`; - document.body.appendChild(noscript); - } - }, [yandexMetricNumber]); + const noscript = document.createElement("noscript"); + noscript.innerHTML = `
`; + document.body.appendChild(noscript); + } + }, [yandexMetricNumber]); }; From e833fa2aa670805ea583f6cc344fd1294e5a3d58 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Mon, 22 Apr 2024 17:13:43 +0300 Subject: [PATCH 04/27] feat: useVkMetricGoals --- .../ViewPublicationPage.tsx | 2 + .../ViewPublicationPage/questions/Date.tsx | 182 +++++++++--------- lib/model/settingsData.ts | 1 + lib/utils/hooks/useVKMetrics.ts | 30 +++ lib/utils/hooks/useVkMetricGoals.ts | 61 ++++++ lib/utils/hooks/useYandexMetrics.ts | 2 + 6 files changed, 187 insertions(+), 91 deletions(-) create mode 100644 lib/utils/hooks/useVKMetrics.ts create mode 100644 lib/utils/hooks/useVkMetricGoals.ts diff --git a/lib/components/ViewPublicationPage/ViewPublicationPage.tsx b/lib/components/ViewPublicationPage/ViewPublicationPage.tsx index 5756519..b7cd666 100644 --- a/lib/components/ViewPublicationPage/ViewPublicationPage.tsx +++ b/lib/components/ViewPublicationPage/ViewPublicationPage.tsx @@ -15,6 +15,7 @@ import NextButton from "./tools/NextButton"; import PrevButton from "./tools/PrevButton"; import QuestionSelect from "./QuestionSelect"; import { useYandexMetrics } from "@/utils/hooks/useYandexMetrics"; +import { useVKMetrics } from "@/utils/hooks/useVKMetrics"; export default function ViewPublicationPage() { const { @@ -37,6 +38,7 @@ export default function ViewPublicationPage() { setQuestion, } = useQuestionFlowControl(); useYandexMetrics(settings?.cfg?.yandexMetricNumber); + useVKMetrics(settings?.cfg?.vkMetricNumber); const isAnswer = answers.some( (ans) => ans.questionId === currentQuestion?.id diff --git a/lib/components/ViewPublicationPage/questions/Date.tsx b/lib/components/ViewPublicationPage/questions/Date.tsx index e355738..3517d45 100644 --- a/lib/components/ViewPublicationPage/questions/Date.tsx +++ b/lib/components/ViewPublicationPage/questions/Date.tsx @@ -13,104 +13,104 @@ import { useState } from "react"; import { useQuizViewStore } from "@/stores/quizView"; type DateProps = { - currentQuestion: QuizQuestionDate; + currentQuestion: QuizQuestionDate; }; export const Date = ({ currentQuestion }: DateProps) => { - const theme = useTheme(); - const { settings, quizId, preview } = useQuizData(); - const answers = useQuizViewStore(state => state.answers); - const updateAnswer = useQuizViewStore(state => state.updateAnswer); - const answer = answers.find( - ({ questionId }) => questionId === currentQuestion.id - )?.answer as string; - const currentAnswer = moment(answer) || moment(); - const [isSending, setIsSending] = useState(false); + const theme = useTheme(); + const { settings, quizId, preview } = useQuizData(); + const answers = useQuizViewStore((state) => state.answers); + const updateAnswer = useQuizViewStore((state) => state.updateAnswer); + const answer = answers.find( + ({ questionId }) => questionId === currentQuestion.id + )?.answer as string; + const currentAnswer = moment(answer) || moment(); + const [isSending, setIsSending] = useState(false); - return ( - - - {currentQuestion.title} - - + + {currentQuestion.title} + + + ( + - ( - - ), - }} - value={currentAnswer} - onChange={async (date) => { - if (isSending || !date) return; + /> + ), + }} + value={currentAnswer} + onChange={async (date) => { + if (isSending || !date) return; - setIsSending(true); - try { - await sendAnswer({ - questionId: currentQuestion.id, - body: moment(date).format("YYYY.MM.DD"), - qid: quizId, - preview - }); + setIsSending(true); + try { + await sendAnswer({ + questionId: currentQuestion.id, + body: moment(date).format("YYYY.MM.DD"), + qid: quizId, + preview, + }); - updateAnswer(currentQuestion.id, date, 0); - } catch (e) { - enqueueSnackbar("ответ не был засчитан"); - } + updateAnswer(currentQuestion.id, date, 0); + } catch (e) { + enqueueSnackbar("ответ не был засчитан"); + } - setIsSending(false); - }} - slotProps={{ - openPickerButton: { - sx: { - p: 0, - }, - "data-cy": "open-datepicker", - }, - layout: { - sx: { backgroundColor: theme.palette.background.default }, - }, - }} - sx={{ - "& .MuiInputBase-root": { - backgroundColor: settings.cfg.design - ? quizThemes[settings.cfg.theme].isLight - ? "#F2F3F7" - : "rgba(154,154,175, 0.2)" - : quizThemes[settings.cfg.theme].isLight - ? "white" - : theme.palette.background.default, - borderRadius: "10px", - maxWidth: "250px", - pr: "30px", - "& input": { - py: "11px", - pl: "20px", - lineHeight: "19px", - }, - "& fieldset": { - borderColor: "#9A9AAF", - }, - }, - }} - /> - - - ); + setIsSending(false); + }} + slotProps={{ + openPickerButton: { + sx: { + p: 0, + }, + "data-cy": "open-datepicker", + }, + layout: { + sx: { backgroundColor: theme.palette.background.default }, + }, + }} + sx={{ + "& .MuiInputBase-root": { + backgroundColor: settings.cfg.design + ? quizThemes[settings.cfg.theme].isLight + ? "#F2F3F7" + : "rgba(154,154,175, 0.2)" + : quizThemes[settings.cfg.theme].isLight + ? "white" + : theme.palette.background.default, + borderRadius: "10px", + maxWidth: "250px", + pr: "30px", + "& input": { + py: "11px", + pl: "20px", + lineHeight: "19px", + }, + "& fieldset": { + borderColor: "#9A9AAF", + }, + }, + }} + /> + + + ); }; diff --git a/lib/model/settingsData.ts b/lib/model/settingsData.ts index cc5e851..4a0985c 100644 --- a/lib/model/settingsData.ts +++ b/lib/model/settingsData.ts @@ -109,6 +109,7 @@ export interface QuizConfig { }; meta: string; yandexMetricNumber: number | undefined; + vkMetricNumber: number | undefined; } export type FormContactFieldName = diff --git a/lib/utils/hooks/useVKMetrics.ts b/lib/utils/hooks/useVKMetrics.ts new file mode 100644 index 0000000..bea48ee --- /dev/null +++ b/lib/utils/hooks/useVKMetrics.ts @@ -0,0 +1,30 @@ +import { useEffect } from "react"; + +export const useVKMetrics = (vkMetricNumber: number | undefined) => { + useEffect(() => { + if ( + vkMetricNumber && + typeof vkMetricNumber === "number" && + !Number.isNaN(vkMetricNumber) + ) { + const script = document.createElement("script"); + script.type = "text/javascript"; + script.innerHTML = ` + var _tmr = window._tmr || (window._tmr = []); + _tmr.push({id: "${vkMetricNumber}", type: "pageView", start: (new Date()).getTime()}); + (function (d, w, id) { + if (d.getElementById(id)) return; + var ts = d.createElement("script"); ts.type = "text/javascript"; ts.async = true; ts.id = id; + ts.src = "https://top-fwz1.mail.ru/js/code.js"; + var f = function () {var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ts, s);}; + if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); } + })(document, window, "tmr-code"); + `; + document.body.appendChild(script); + + const noscript = document.createElement("noscript"); + noscript.innerHTML = `
Top.Mail.Ru
`; + document.body.appendChild(noscript); + } + }, [vkMetricNumber]); +}; diff --git a/lib/utils/hooks/useVkMetricGoals.ts b/lib/utils/hooks/useVkMetricGoals.ts new file mode 100644 index 0000000..41c3b89 --- /dev/null +++ b/lib/utils/hooks/useVkMetricGoals.ts @@ -0,0 +1,61 @@ +import { useEffect, useState } from "react"; + +type VkMetric = { + type: "reachGoal"; + id: number; + goal: string; +}; + +type ExtendedWindow = Window & { _tmp?: VkMetric[] }; + +type Messenger = + | "telegram" + | "viber" + | "whatsapp" + | "vkontakte" + | "messenger" + | "skype" + | "instagram"; + +const sendMetrics = (vkPixelId: number | undefined, goal: string) => { + if (vkPixelId) { + (window as ExtendedWindow)._tmp?.push({ + type: "reachGoal", + id: vkPixelId, + goal, + }); + } +}; + +export const useVkMetricGoals = (vkPixelId: number | undefined) => { + const [vkId, setVkId] = useState(undefined); + + useEffect(() => { + if (vkPixelId) { + setVkId(vkPixelId); + } + }, [vkPixelId]); + + return { + // Посетитель открыл квиз + quizOpened: () => sendMetrics(vkId, "penaquiz-start"), + // Посетитель нажал на кнопку стартовой страницы + firstPageOpened: () => sendMetrics(vkId, "penaquiz-startquiz"), + // Посетитель кликнул по номеру телефона на стартовой странице + phoneNumberOpened: () => sendMetrics(vkId, "penaquiz-phone"), + // Посетитель кликнул по email на стартовой странице + emailOpened: () => sendMetrics(vkId, "penaquiz-email"), + // Посетитель увидел определенный результат (id - айдишник вопроса с типом result) + resultShown: (resultId: string) => sendMetrics(vkId, `penaquiz-result-${resultId}`), + // Посетитель дошёл до формы контактов + contactsFormOpened: () => sendMetrics(vkId, "penaquiz-form"), + // Посетитель заполнил форму контактов + contactsFormFilled: () => sendMetrics(vkId, "penaquiz-contacts"), + // Посетитель отправил заявку с мессенджером + messengerRequestSended: (messenger: Messenger) => + sendMetrics(vkId, `marquiz-messengers-${messenger}`), + // Посетитель прошёл вопрос + questionPassed: (questionId: string) => + sendMetrics(vkId, `marquiz-step${questionId}`), + }; +}; diff --git a/lib/utils/hooks/useYandexMetrics.ts b/lib/utils/hooks/useYandexMetrics.ts index 6ff5e26..ba747a8 100644 --- a/lib/utils/hooks/useYandexMetrics.ts +++ b/lib/utils/hooks/useYandexMetrics.ts @@ -3,6 +3,7 @@ import { useEffect } from "react"; export const useYandexMetrics = (yandexMetricNumber: number | undefined) => { useEffect(() => { if ( + yandexMetricNumber && typeof yandexMetricNumber === "number" && !Number.isNaN(yandexMetricNumber) ) { @@ -22,6 +23,7 @@ export const useYandexMetrics = (yandexMetricNumber: number | undefined) => { webvisor:true }); `; + document.body.appendChild(script); const noscript = document.createElement("noscript"); From 40037b38ce92435c8d544442f11ccb13b974580a Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Tue, 23 Apr 2024 17:45:49 +0300 Subject: [PATCH 05/27] refactor: questions --- .../ViewPublicationPage/Question.tsx | 254 +++++----- .../questions/{Date.tsx => Date/index.tsx} | 78 ++- .../ViewPublicationPage/questions/Emoji.tsx | 221 -------- .../questions/Emoji/EmojiVariant.tsx | 179 +++++++ .../questions/Emoji/index.tsx | 70 +++ .../ViewPublicationPage/questions/File.tsx | 292 ----------- .../questions/File/UploadFile.tsx | 176 +++++++ .../questions/File/UploadedFile.tsx | 75 +++ .../questions/File/index.tsx | 134 +++++ .../ViewPublicationPage/questions/Images.tsx | 195 ------- .../questions/Images/ImageVariant.tsx | 156 ++++++ .../questions/Images/index.tsx | 73 +++ .../ViewPublicationPage/questions/Number.tsx | 477 ------------------ .../questions/Number/index.tsx | 477 ++++++++++++++++++ .../ViewPublicationPage/questions/Page.tsx | 54 -- .../questions/Page/index.tsx | 74 +++ .../ViewPublicationPage/questions/Rating.tsx | 148 ------ .../questions/Rating/index.tsx | 165 ++++++ .../ViewPublicationPage/questions/Select.tsx | 99 ---- .../questions/Select/index.tsx | 102 ++++ .../ViewPublicationPage/questions/Text.tsx | 286 ----------- .../questions/Text/TextNormal.tsx | 91 ++++ .../questions/Text/TextSpecial.tsx | 152 ++++++ .../questions/Text/index.tsx | 77 +++ .../ViewPublicationPage/questions/Variant.tsx | 307 ----------- .../questions/Variant/VariantItem.tsx | 186 +++++++ .../questions/Variant/index.tsx | 124 +++++ .../ViewPublicationPage/questions/Varimg.tsx | 226 --------- .../questions/Varimg/VarimgVariant.tsx | 129 +++++ .../questions/Varimg/index.tsx | 130 +++++ 30 files changed, 2733 insertions(+), 2474 deletions(-) rename lib/components/ViewPublicationPage/questions/{Date.tsx => Date/index.tsx} (69%) delete mode 100644 lib/components/ViewPublicationPage/questions/Emoji.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Emoji/EmojiVariant.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Emoji/index.tsx delete mode 100644 lib/components/ViewPublicationPage/questions/File.tsx create mode 100644 lib/components/ViewPublicationPage/questions/File/UploadFile.tsx create mode 100644 lib/components/ViewPublicationPage/questions/File/UploadedFile.tsx create mode 100644 lib/components/ViewPublicationPage/questions/File/index.tsx delete mode 100644 lib/components/ViewPublicationPage/questions/Images.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Images/ImageVariant.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Images/index.tsx delete mode 100644 lib/components/ViewPublicationPage/questions/Number.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Number/index.tsx delete mode 100644 lib/components/ViewPublicationPage/questions/Page.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Page/index.tsx delete mode 100644 lib/components/ViewPublicationPage/questions/Rating.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Rating/index.tsx delete mode 100644 lib/components/ViewPublicationPage/questions/Select.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Select/index.tsx delete mode 100644 lib/components/ViewPublicationPage/questions/Text.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Text/TextNormal.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Text/TextSpecial.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Text/index.tsx delete mode 100644 lib/components/ViewPublicationPage/questions/Variant.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Variant/VariantItem.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Variant/index.tsx delete mode 100644 lib/components/ViewPublicationPage/questions/Varimg.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Varimg/VarimgVariant.tsx create mode 100644 lib/components/ViewPublicationPage/questions/Varimg/index.tsx diff --git a/lib/components/ViewPublicationPage/Question.tsx b/lib/components/ViewPublicationPage/Question.tsx index c26ffea..5a9f7bb 100644 --- a/lib/components/ViewPublicationPage/Question.tsx +++ b/lib/components/ViewPublicationPage/Question.tsx @@ -25,147 +25,147 @@ import { DESIGN_LIST } from "@/utils/designList"; import type { ReactNode } from "react"; type Props = { - currentQuestion: RealTypedQuizQuestion; - currentQuestionStepNumber: number | null; - nextButton: ReactNode; - prevButton: ReactNode; - questionSelect: ReactNode; + currentQuestion: RealTypedQuizQuestion; + currentQuestionStepNumber: number | null; + nextButton: ReactNode; + prevButton: ReactNode; + questionSelect: ReactNode; }; export const Question = ({ - currentQuestion, - currentQuestionStepNumber, - nextButton, - prevButton, - questionSelect, + currentQuestion, + currentQuestionStepNumber, + nextButton, + prevButton, + questionSelect, }: Props) => { - const theme = useTheme(); - const { settings, show_badge } = useQuizData(); + const theme = useTheme(); + const { settings, show_badge } = useQuizData(); - return ( + return ( + + - + + {show_badge && ( + - + {quizThemes[settings.cfg.theme].isLight ? ( + - - - {show_badge && ( - - {quizThemes[settings.cfg.theme].isLight ? ( - - ) : ( - - )} - - )} - - - {questionSelect} -