From 44905362764f5082119b1f06777fe04be65a7e02 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 27 Oct 2023 20:38:06 +0300 Subject: [PATCH] wrap components with error boundaries, minox fixes --- src/components/Drawers.tsx | 17 +- src/pages/AccountSettings/AccountSettings.tsx | 477 +++++++++--------- src/pages/Cart/Cart.tsx | 9 +- src/pages/SavedTariffs/index.tsx | 9 +- src/pages/Support/SupportChat.tsx | 11 +- src/pages/Support/TicketList/TicketList.tsx | 10 +- .../TariffConstructor/TariffConstructor.tsx | 11 +- src/pages/Tariffs/TariffsPage.tsx | 9 +- src/utils/handleComponentError.ts | 2 +- 9 files changed, 310 insertions(+), 245 deletions(-) diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index 3749922..66ca6e0 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -30,8 +30,11 @@ import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg"; import { payCart } from "@root/api/cart"; import { enqueueSnackbar } from "notistack"; import { Link, useNavigate } from "react-router-dom"; +import { withErrorBoundary } from "react-error-boundary"; +import { handleComponentError } from "@root/utils/handleComponentError"; +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; -export default function Drawers() { +function Drawers() { const [openNotificationsModal, setOpenNotificationsModal] = useState(false); const [loading, setLoading] = useState(false); @@ -324,3 +327,15 @@ export default function Drawers() { ); } + +export default withErrorBoundary(Drawers, { + fallback: ( + + + + ), + onError: handleComponentError, +}) diff --git a/src/pages/AccountSettings/AccountSettings.tsx b/src/pages/AccountSettings/AccountSettings.tsx index 8d87795..4baa69f 100644 --- a/src/pages/AccountSettings/AccountSettings.tsx +++ b/src/pages/AccountSettings/AccountSettings.tsx @@ -13,256 +13,263 @@ import { getMessageFromFetchError } from "@frontend/kitui"; import { enqueueSnackbar } from "notistack"; import { VerificationStatus } from "@root/model/account"; import { verify } from "./helper"; +import { withErrorBoundary } from "react-error-boundary"; +import { handleComponentError } from "@root/utils/handleComponentError"; -export default function AccountSettings() { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const upSm = useMediaQuery(theme.breakpoints.up("sm")); - const isTablet = useMediaQuery(theme.breakpoints.down(1000)); - const isMobile = useMediaQuery(theme.breakpoints.down(600)); +function AccountSettings() { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const isTablet = useMediaQuery(theme.breakpoints.down(1000)); + const isMobile = useMediaQuery(theme.breakpoints.down(600)); - const fields = useUserStore((state) => state.settingsFields); - const verificationStatus = useUserStore((state) => state.verificationStatus); - const verificationType = useUserStore((state) => state.verificationType); - const comment = useUserStore((state) => state.comment); - const userId = useUserStore((state) => state.userId) ?? ""; + const fields = useUserStore((state) => state.settingsFields); + const verificationStatus = useUserStore((state) => state.verificationStatus); + const verificationType = useUserStore((state) => state.verificationType); + const comment = useUserStore((state) => state.comment); + const userId = useUserStore((state) => state.userId) ?? ""; - useEffect(() => { - verify(userId); - }, []); + useEffect(() => { + verify(userId); + }, []); - const textFieldProps = { - gap: upMd ? "16px" : "10px", - color: "#F2F3F7", - bold: true, - }; + const textFieldProps = { + gap: upMd ? "16px" : "10px", + color: "#F2F3F7", + bold: true, + }; - const verificationStatusData: Record = { + function handleSendDataClick() { + sendUserData() + .then(() => { + enqueueSnackbar("Информация обновлена"); + }) + .catch((error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); + }); + } + + return ( + + + + Настройки аккаунта + + + + + setSettingsField("firstname", e.target.value)} + id="firstname" + label="Имя" + {...textFieldProps} + /> + setSettingsField("secondname", e.target.value)} + id="secondname" + label="Фамилия" + {...textFieldProps} + /> + setSettingsField("middlename", e.target.value)} + id="middlename" + label="Отчество" + {...textFieldProps} + /> + setSettingsField("orgname", e.target.value)} + id="orgname" + label="Название компании" + {...textFieldProps} + /> + setSettingsField("email", e.target.value)} + id="email" + label="E-mail" + {...textFieldProps} + /> + setSettingsField("phoneNumber", e.target.value)} + id="phoneNumber" + label="Телефон" + {...textFieldProps} + /> + setSettingsField("password", e.target.value)} + id="password" + label="Пароль" + {...textFieldProps} + /> + + + Статус + + {verificationStatus === VerificationStatus.NOT_VERIFICATED && ( + <> + } + sx={{ mt: "55px" }} + ButtonProps={{ + onClick: () => openDocumentsDialog("juridical"), + }} + > + Загрузить документы для юр лиц + + } + sx={{ mt: "15px" }} + ButtonProps={{ + onClick: () => openDocumentsDialog("nko"), + }} + > + Загрузить документы для НКО + + + )} + {verificationStatus === VerificationStatus.VERIFICATED && ( + } + sx={{ mt: "55px" }} + ButtonProps={{ + onClick: () => openDocumentsDialog(verificationType), + }} + > + Посмотреть свою верификацию + + )} + {comment &&

{comment}

} +
+
+ +
+
+ ); +} + +export default withErrorBoundary(AccountSettings, { + fallback: Ошибка при отображении настроек аккаунта, + onError: handleComponentError, +}) + +const verificationStatusData: Record = { verificated: { text: "Верификация пройдена", color: "#0D9F00" }, waiting: { text: "В ожидании верификации", color: "#F18956" }, notVerificated: { text: "Не верифицирован", color: "#E02C2C" }, - }; +}; - function handleSendDataClick() { - sendUserData() - .then(() => { - enqueueSnackbar("Информация обновлена"); - }) - .catch((error) => { - const message = getMessageFromFetchError(error); - if (message) enqueueSnackbar(message); - }); - } - - function VerificationIndicator({ +function VerificationIndicator({ verificationStatus, sx, - }: { +}: { verificationStatus: VerificationStatus; sx?: SxProps; - }) { +}) { return ( - - {verificationStatusData[verificationStatus].text} - - ); - } - - return ( - - - - Настройки аккаунта - - - - setSettingsField("firstname", e.target.value)} - id="firstname" - label="Имя" - {...textFieldProps} - /> - setSettingsField("secondname", e.target.value)} - id="secondname" - label="Фамилия" - {...textFieldProps} - /> - setSettingsField("middlename", e.target.value)} - id="middlename" - label="Отчество" - {...textFieldProps} - /> - setSettingsField("orgname", e.target.value)} - id="orgname" - label="Название компании" - {...textFieldProps} - /> - setSettingsField("email", e.target.value)} - id="email" - label="E-mail" - {...textFieldProps} - /> - setSettingsField("phoneNumber", e.target.value)} - id="phoneNumber" - label="Телефон" - {...textFieldProps} - /> - setSettingsField("password", e.target.value)} - id="password" - label="Пароль" - {...textFieldProps} - /> - - - Статус - - {verificationStatus === VerificationStatus.NOT_VERIFICATED && ( - <> - } - sx={{ mt: "55px" }} - ButtonProps={{ - onClick: () => openDocumentsDialog("juridical"), - }} - > - Загрузить документы для юр лиц - - } - sx={{ mt: "15px" }} - ButtonProps={{ - onClick: () => openDocumentsDialog("nko"), - }} - > - Загрузить документы для НКО - - - )} - {verificationStatus === VerificationStatus.VERIFICATED && ( - } - sx={{ mt: "55px" }} - ButtonProps={{ - onClick: () => openDocumentsDialog(verificationType), - }} - > - Посмотреть свою верификацию - - )} - {comment &&

{comment}

} -
+ {verificationStatusData[verificationStatus].text}
- -
-
- ); + ); } diff --git a/src/pages/Cart/Cart.tsx b/src/pages/Cart/Cart.tsx index 84216c3..fb744bb 100644 --- a/src/pages/Cart/Cart.tsx +++ b/src/pages/Cart/Cart.tsx @@ -6,8 +6,10 @@ import CustomWrapper from "./CustomWrapper"; import { useCart } from "@root/utils/hooks/useCart"; import { useLocation } from "react-router-dom"; import { usePrevLocation } from "@root/utils/hooks/handleCustomBackNavigation"; +import { handleComponentError } from "@root/utils/handleComponentError"; +import { withErrorBoundary } from "react-error-boundary"; -export default function Cart() { +function Cart() { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const isMobile = useMediaQuery(theme.breakpoints.down(550)); @@ -71,3 +73,8 @@ export default function Cart() { ); } + +export default withErrorBoundary(Cart, { + fallback: Ошибка при отображении корзины, + onError: handleComponentError, +}) diff --git a/src/pages/SavedTariffs/index.tsx b/src/pages/SavedTariffs/index.tsx index 4b37a2b..fada753 100644 --- a/src/pages/SavedTariffs/index.tsx +++ b/src/pages/SavedTariffs/index.tsx @@ -5,8 +5,10 @@ import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker"; import SaveWrapper from "./SaveWrapper"; import { useTariffStore } from "@root/stores/tariffs"; import { type Tariff } from "@frontend/kitui"; +import { withErrorBoundary } from "react-error-boundary"; +import { handleComponentError } from "@root/utils/handleComponentError"; -export default function SavedTariffs() { +function SavedTariffs() { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const isMobile = useMediaQuery(theme.breakpoints.down(550)); @@ -65,3 +67,8 @@ export default function SavedTariffs() { ); } + +export default withErrorBoundary(SavedTariffs, { + fallback: Ошибка при отображении сохраненных тарифов, + onError: handleComponentError, +}) diff --git a/src/pages/Support/SupportChat.tsx b/src/pages/Support/SupportChat.tsx index d275a7b..88409a9 100644 --- a/src/pages/Support/SupportChat.tsx +++ b/src/pages/Support/SupportChat.tsx @@ -35,8 +35,10 @@ import { useTicketMessages, } from "@frontend/kitui"; import { shownMessage, sendTicketMessage } from "@root/api/ticket"; +import { withErrorBoundary } from "react-error-boundary"; +import { handleComponentError } from "@root/utils/handleComponentError"; -export default function SupportChat() { +function SupportChat() { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const isMobile = useMediaQuery(theme.breakpoints.up(460)); @@ -128,7 +130,7 @@ export default function SupportChat() { async function handleSendMessage() { if (!ticket || !messageField) return; - const [_, sendTicketMessageError] = await sendTicketMessage( + const [, sendTicketMessageError] = await sendTicketMessage( ticket.id, messageField ); @@ -318,3 +320,8 @@ export default function SupportChat() { ); } + +export default withErrorBoundary(SupportChat, { + fallback: Не удалось отобразить чат, + onError: handleComponentError, +}) diff --git a/src/pages/Support/TicketList/TicketList.tsx b/src/pages/Support/TicketList/TicketList.tsx index 22ad915..98035c3 100644 --- a/src/pages/Support/TicketList/TicketList.tsx +++ b/src/pages/Support/TicketList/TicketList.tsx @@ -5,13 +5,16 @@ import { Box, useTheme, Pagination, + Typography, } from "@mui/material"; import TicketCard from "./TicketCard"; import { setTicketApiPage, useTicketStore } from "@root/stores/tickets"; import { Ticket } from "@frontend/kitui"; +import { withErrorBoundary } from "react-error-boundary"; +import { handleComponentError } from "@root/utils/handleComponentError"; -export default function TicketList() { +function TicketList() { const theme = useTheme(); const tickets = useTicketStore((state) => state.tickets); const ticketCount = useTicketStore((state) => state.ticketCount); @@ -87,3 +90,8 @@ function sortTicketsByUpdateTime(ticket1: Ticket, ticket2: Ticket) { const date2 = new Date(ticket2.updated_at).getTime(); return date2 - date1; } + +export default withErrorBoundary(TicketList, { + fallback: Ошибка загрузки тикетов, + onError: handleComponentError, +}); diff --git a/src/pages/TariffConstructor/TariffConstructor.tsx b/src/pages/TariffConstructor/TariffConstructor.tsx index c007bbf..d36c667 100644 --- a/src/pages/TariffConstructor/TariffConstructor.tsx +++ b/src/pages/TariffConstructor/TariffConstructor.tsx @@ -1,4 +1,4 @@ -import { Box, IconButton, useMediaQuery, useTheme } from "@mui/material"; +import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material"; import { Link } from "react-router-dom"; import SectionWrapper from "@components/SectionWrapper"; import { useCustomTariffsStore } from "@root/stores/customTariffs"; @@ -8,8 +8,10 @@ import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import TotalPrice from "@root/components/TotalPrice"; import { serviceNameByKey } from "@root/utils/serviceKeys"; import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker"; +import { withErrorBoundary } from "react-error-boundary"; +import { handleComponentError } from "@root/utils/handleComponentError"; -export default function TariffConstructor() { +function TariffConstructor() { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const isTablet = useMediaQuery(theme.breakpoints.down(1000)); @@ -84,3 +86,8 @@ export default function TariffConstructor() { ); } + +export default withErrorBoundary(TariffConstructor, { + fallback: Ошибка при отображении кастомных тарифов, + onError: handleComponentError, +}) diff --git a/src/pages/Tariffs/TariffsPage.tsx b/src/pages/Tariffs/TariffsPage.tsx index 6248ec0..93b6832 100644 --- a/src/pages/Tariffs/TariffsPage.tsx +++ b/src/pages/Tariffs/TariffsPage.tsx @@ -18,6 +18,8 @@ import { Slider } from "./slider"; import { useCartStore } from "@root/stores/cart"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import { usePrevLocation } from "@root/utils/hooks/handleCustomBackNavigation"; +import { withErrorBoundary } from "react-error-boundary"; +import { handleComponentError } from "@root/utils/handleComponentError"; const subPages = ["Шаблонизатор", "Опросник", "Сокращатель ссылок"]; @@ -26,7 +28,7 @@ const StepperText: Record = { time: "Тарифы на время", }; -export default function TariffPage() { +function TariffPage() { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const isMobile = useMediaQuery(theme.breakpoints.down(600)); @@ -178,3 +180,8 @@ export default function TariffPage() { ); } + +export default withErrorBoundary(TariffPage, { + fallback: Ошибка загрузки тарифов, + onError: handleComponentError, +}) diff --git a/src/utils/handleComponentError.ts b/src/utils/handleComponentError.ts index e53049a..e6ae5bd 100644 --- a/src/utils/handleComponentError.ts +++ b/src/utils/handleComponentError.ts @@ -39,6 +39,6 @@ async function sendErrorsToServer() { // body: errorsQueue, // useToken: true, // }); - console.log(`Sending ${errorsQueue.length} errors to server`, errorsQueue); + console.log(`Fake-sending ${errorsQueue.length} errors to server`, errorsQueue); errorsQueue = []; }