From 3001b87bdf09073384f7d63863874de7d58f84ec Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Mon, 15 Apr 2024 17:32:00 +0300 Subject: [PATCH 1/2] refactor: analytics --- src/pages/Analytics/Analytics.tsx | 22 ++-- src/pages/Analytics/Answers/Funnel.tsx | 1 + src/pages/Analytics/Devices.tsx | 1 + src/pages/Analytics/General.tsx | 137 +++++++++---------------- 4 files changed, 61 insertions(+), 100 deletions(-) diff --git a/src/pages/Analytics/Analytics.tsx b/src/pages/Analytics/Analytics.tsx index 37327d9f..f385b85a 100644 --- a/src/pages/Analytics/Analytics.tsx +++ b/src/pages/Analytics/Analytics.tsx @@ -1,4 +1,5 @@ -import { ReactNode, useEffect, useLayoutEffect, useState } from "react"; +import moment from "moment"; +import { useEffect, useLayoutEffect, useState } from "react"; import { Box, Button, @@ -10,10 +11,6 @@ import { import { redirect } from "react-router-dom"; import { LocalizationProvider, DatePicker } from "@mui/x-date-pickers"; import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment"; -import moment from "moment"; -import { useQuizStore } from "@root/quizes/store"; -import { useCurrentQuiz } from "@root/quizes/hooks"; -import { useAnalytics } from "@utils/hooks/useAnalytics"; import HeaderFull from "@ui_kit/Header/HeaderFull"; import SectionWrapper from "@ui_kit/SectionWrapper"; @@ -22,13 +19,19 @@ import { General } from "./General"; import { AnswersStatistics } from "./Answers/AnswersStatistics"; import { Devices } from "./Devices"; +import { setQuizes } from "@root/quizes/actions"; +import { useQuizStore } from "@root/quizes/store"; + +import { useAnalytics } from "@utils/hooks/useAnalytics"; + +import { quizApi } from "@api/quiz"; + import CalendarIcon from "@icons/CalendarIcon"; import { ReactComponent as ResetIcon } from "@icons/Analytics/reset.svg"; import type { Moment } from "moment"; -import { setQuizes } from "@root/quizes/actions"; -import { quizApi } from "@api/quiz"; -import { Quiz } from "@model/quiz/quiz"; +import type { ReactNode } from "react"; +import type { Quiz } from "@model/quiz/quiz"; export default function Analytics() { const { quizes, editQuizId } = useQuizStore(); @@ -43,7 +46,6 @@ export default function Analytics() { console.log(86400 - (moment(to).unix() - moment(from).unix()) > 0); const theme = useTheme(); - const isTablet = useMediaQuery(theme.breakpoints.down(1000)); const isMobile = useMediaQuery(theme.breakpoints.down(600)); const { devices, general, questions } = useAnalytics({ @@ -116,8 +118,6 @@ export default function Analytics() { } }; - console.log(general); - const now = moment(); return ( diff --git a/src/pages/Analytics/Answers/Funnel.tsx b/src/pages/Analytics/Answers/Funnel.tsx index d255e3f2..a15b6409 100644 --- a/src/pages/Analytics/Answers/Funnel.tsx +++ b/src/pages/Analytics/Answers/Funnel.tsx @@ -112,6 +112,7 @@ export const Funnel: FC = ({ data, funnelData }) => { const theme = useTheme(); const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1150)); const isMobile = useMediaQuery(theme.breakpoints.down(850)); + useEffect(() => { // const requestFunnel = async () => { // const [funnelResponse, funnelError] = await getGeneral("14761"); diff --git a/src/pages/Analytics/Devices.tsx b/src/pages/Analytics/Devices.tsx index 86dc7692..8e7bc0c4 100644 --- a/src/pages/Analytics/Devices.tsx +++ b/src/pages/Analytics/Devices.tsx @@ -22,6 +22,7 @@ const COLORS: Record = { const Device = ({ title, devices }: DeviceProps) => { const theme = useTheme(); + if (!devices || !Object.keys(devices ?? {}).length) { return {title} - нет данных; } diff --git a/src/pages/Analytics/General.tsx b/src/pages/Analytics/General.tsx index b26e85fc..d8bf3ebf 100644 --- a/src/pages/Analytics/General.tsx +++ b/src/pages/Analytics/General.tsx @@ -1,9 +1,9 @@ +import moment from "moment"; import { Box, Paper, Typography, useMediaQuery, useTheme } from "@mui/material"; import { LineChart } from "@mui/x-charts"; -import moment from "moment"; +import type { FC } from "react"; import type { GeneralResponse } from "@api/statistic"; -import { FC } from "react"; type GeneralItemsProps = { title: string; @@ -20,22 +20,6 @@ type GeneralProps = { day: boolean; }; -const COLORS: Record = { - 0: "#61BB1A", - 1: "#7E2AEA", - 2: "#FB5607", - 3: "#0886FB", -}; - -const dateParser = (object: Record): Record => { - const result = {} as Record; - for (var key in object) { - result[moment.unix(Number(key) * 1000).format("DD/MM/YYYY")] = object[key]; - console.log(result); - } - return result; -}; - const getCalculatedTime = (time: number) => { const hours = String(Math.floor(time / 3600)).padStart(2, "0"); const minutes = String(Math.floor((time % 3600) / 60)).padStart(2, "0"); @@ -43,6 +27,7 @@ const getCalculatedTime = (time: number) => { return `${hours}:${minutes}:${seconds}`; }; + const GeneralItem = ({ title, general, @@ -89,7 +74,7 @@ const GeneralItem = ({ > {title} - {numberType === "percent" ? `${numberValue.toFixed(2)}%` : numberValue} + {numberType === "percent" ? `${numberValue?.toFixed(2)}%` : numberValue} ); }; + const GeneralItemTimeConv = ({ title, general, @@ -131,38 +117,19 @@ const GeneralItemTimeConv = ({ const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(700)); - console.log("day", day); - - const data = Object.entries(general).sort((a, b) => a[0] - b[0]); - - const days = [...data].map((e) => e[0]); - - let buffer = 0; - - const time = [...data].map((e) => { - if (e[1] > 0) { - buffer = e[1]; - } - return buffer; - }); - - console.log("data", data); - console.log( - "time", - time.reduce((a, b) => Number(a) + Number(b), 0), + const data = Object.entries(general).sort( + ([nextValue], [currentValue]) => Number(nextValue) - Number(currentValue), ); - console.log( - "time", - getCalculatedTime(time.reduce((a, b) => Number(a) + Number(b), 0)), - ); - console.log("days", days.length); + const days = data.map(([value]) => value); + const time = data.map(([_, value]) => value); + const numberValue = calculateTime - ? time.reduce((a, b) => Number(a) + Number(b), 0) / days.length || 0 + ? time.reduce((total, value) => total + value, 0) / days.length : conversionValue; if ( Object.keys(general).length === 0 || - Object.values(general).every((x) => x === 0) + Object.values(general).every((item) => item === 0) ) { return ( {`${title} - нет данных`} @@ -180,8 +147,8 @@ const GeneralItemTimeConv = ({ {title} {calculateTime - ? `${getCalculatedTime(numberValue)} с` - : `${numberValue.toFixed(2)}%`} + ? `${getCalculatedTime(numberValue ?? 0)} с` + : `${numberValue?.toFixed(2) ?? 0}%`} { - console.log("log", value); - return calculateTime + valueFormatter: (value) => + calculateTime ? getCalculatedTime(value) - : String((value * 100).toFixed(2)) + "%"; - }, + : String((value * 100).toFixed(2)) + "%", }, ]} // dataset={Object.entries(general).map(([, v]) => moment.unix(v).format("ss:mm:HH")).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})} @@ -227,39 +192,33 @@ export const General: FC = ({ data, day }) => { ); } - const DATA = {} as GeneralResponse; const currentDate = moment().unix(); - const futureCutter = ({ - fatherKey, - values, - }: { - fatherKey: string; - values: Record; - }) => { - const buffer = {} as Record; - for (let key in values) { - console.log(Number(key)); - console.log(currentDate); - console.log(Number(key) - currentDate); - console.log(Number(key) - currentDate < 0); - console.log(moment.unix(key).format("HH")); - if (Number(key) - currentDate < 0) buffer[key] = values[key]; - } - DATA[fatherKey] = buffer; - }; - for (let key in data) { - futureCutter({ fatherKey: key, values: data[key] }); - } - console.log(DATA); + const generalResponse = Object.entries(data).reduce( + (total, [fatherKey, values]) => { + const value = Object.keys(values).reduce((totalValue, key) => { + if (Number(key) - currentDate < 0) { + return { ...totalValue, [key]: values[key] }; + } - const conversionValue = - (Object.values(DATA.Result).reduce((total, item) => total + item, 0) / - Object.values(DATA.Open).reduce((total, item) => total + item, 0)) * - 100; + return totalValue; + }, {}); + + return { ...total, [fatherKey]: value }; + }, + {} as GeneralResponse, + ); + + const resultSum = Object.values(generalResponse.Result).reduce( + (total, item) => total + item, + 0, + ); + const openSum = Object.values(generalResponse.Open).reduce( + (total, item) => total + item, + 0, + ); + const conversionValue = (resultSum / openSum) * 100; - console.log(conversionValue); - // console.log(DATA.Result) return ( = ({ data, day }) => { From dc2a09dd83ff8af7977bf6b7993280ac2242a807 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Tue, 16 Apr 2024 16:43:22 +0300 Subject: [PATCH 2/2] fix: password validation --- src/pages/auth/RecoverPassword.tsx | 5 ++++- src/pages/auth/Signup.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/auth/RecoverPassword.tsx b/src/pages/auth/RecoverPassword.tsx index 3291779c..5fd905a1 100644 --- a/src/pages/auth/RecoverPassword.tsx +++ b/src/pages/auth/RecoverPassword.tsx @@ -32,7 +32,10 @@ const initialValues: Values = { const validationSchema = object({ password: string() .min(8, "Минимум 8 символов") - .matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы") + .matches( + /^[.,:;\-_+!&()*<>\[\]\{\}`@"#$\%\^\=?\d\w]+$/, + "Некорректные символы", + ) .required("Поле обязательно"), }); diff --git a/src/pages/auth/Signup.tsx b/src/pages/auth/Signup.tsx index dce27e3c..97cc5d12 100644 --- a/src/pages/auth/Signup.tsx +++ b/src/pages/auth/Signup.tsx @@ -39,7 +39,7 @@ const validationSchema = object({ password: string() .min(8, "Минимум 8 символов") .matches( - /^[.,:;\-_+!&()<>\[\]{}№`@"'~*|#$₽%^=?\d\w]+$/, + /^[.,:;\-_+!&()*<>\[\]\{\}`@"#$\%\^\=?\d\w]+$/, "Некорректные символы", ) .required("Поле обязательно"),