From 3b5ee6602cd5e50a705724ac766927f7a3cadadb Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Tue, 7 May 2024 17:44:55 +0300 Subject: [PATCH 01/27] fix: back button --- src/pages/Payment/Payment.tsx | 5 ++- src/utils/hooks/useHistoryTracker.ts | 60 +++++++++++++++++----------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/pages/Payment/Payment.tsx b/src/pages/Payment/Payment.tsx index 5087491..c9d299e 100644 --- a/src/pages/Payment/Payment.tsx +++ b/src/pages/Payment/Payment.tsx @@ -158,7 +158,8 @@ export default function Payment() { enqueueSnackbar(response); mutate("discounts"); }) - .catch((error) => {if (error.message !== "" && typeof(error.message)==="string") + .catch((error) => { + if (error.message !== "" && typeof error.message === "string") enqueueSnackbar(error.message); }); } @@ -180,7 +181,7 @@ export default function Payment() { gap: "10px", }} > - {!upMd && ( + {!upMd && location.key !== "default" && ( { - const location = useLocation() - const navigate = useNavigate() + const location = useLocation(); + const navigate = useNavigate(); - useEffect(() => { - if (location.state?.previousUrl) { - localStorage.setItem("history", JSON.stringify([location.state.previousUrl])) - } - }, [location]) + useEffect(() => { + if (location.state?.previousUrl) { + localStorage.setItem( + "history", + JSON.stringify([location.state.previousUrl]) + ); + } + }, [location]); - const handleCustomBackNavigation = () => { - const historyJSON = localStorage.getItem("history") + const handleCustomBackNavigation = () => { + const historyJSON = localStorage.getItem("history"); + // debugger; - if (historyJSON) { - const history = JSON.parse(historyJSON) + if (historyJSON) { + const history = JSON.parse(historyJSON); - if (history.length > 0) { - if (history[history.length - 1].includes("/support/")) { - navigate(-2) - } else { - navigate(-1) - } - } - } - } + if (history.length > 0) { + if (history[history.length - 1].includes("/support/")) { + navigate(-2); - return handleCustomBackNavigation -} + return; + } + + if (location.search.includes("squizpay")) { + navigate(-3); + + return; + } + + navigate(-1); + } + } + }; + + return handleCustomBackNavigation; +}; From 651a4f4370c97d0632cb8e49fa86d424153da75b Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Thu, 9 May 2024 12:36:47 +0300 Subject: [PATCH 02/27] fix: arrow visibillity --- src/pages/Payment/Payment.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pages/Payment/Payment.tsx b/src/pages/Payment/Payment.tsx index 729c075..52feb00 100644 --- a/src/pages/Payment/Payment.tsx +++ b/src/pages/Payment/Payment.tsx @@ -184,12 +184,14 @@ export default function Payment() { gap: "10px", }} > - - - + {window.history.length > 1 && ( + + + + )} Способ оплаты {!upMd && ( From 155d6f327b8da4e3cb194d6e4c3ed355a29f024e Mon Sep 17 00:00:00 2001 From: Tamara Date: Fri, 17 May 2024 21:47:57 +0300 Subject: [PATCH 03/27] =?UTF-8?q?=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B0?= =?UTF-8?q?=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/History/AccordionWrapper2.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/History/AccordionWrapper2.tsx b/src/pages/History/AccordionWrapper2.tsx index 5b3576a..ed08cf1 100644 --- a/src/pages/History/AccordionWrapper2.tsx +++ b/src/pages/History/AccordionWrapper2.tsx @@ -52,7 +52,8 @@ export default function AccordionWrapper2({ tariff, price, last, first, createdA async function sendBillByEmail(tariffId: string) { try { await makeRequest({ - url: process.env.REACT_APP_DOMAIN + `/customer/sendReport/${tariffId}`, + url: process.env.REACT_APP_DOMAIN + `/customer/sendReport`, + body: {id: tariffId}, method: "POST", }); enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); @@ -184,7 +185,7 @@ export default function AccordionWrapper2({ tariff, price, last, first, createdA {!isMobile && <> - {/* { e.stopPropagation(); sendBillByEmail(tariff._id); @@ -205,7 +206,7 @@ export default function AccordionWrapper2({ tariff, price, last, first, createdA }} > - */} + { From 3ce8cd6f7b4e2b59912b6723e90d1fcd6279703d Mon Sep 17 00:00:00 2001 From: Tamara Date: Sat, 18 May 2024 02:19:32 +0300 Subject: [PATCH 04/27] =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B2=D0=B5=D1=80=D0=B8=D1=84=D0=B8=D0=BA=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=BE=D1=80=D0=B3=20=D0=B8=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=20=D0=BF=D1=80=D0=B8=20=D0=B3=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B0=D0=BA=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/AccountSettings/UserFields.tsx | 2 +- src/pages/History/AccordionWrapper.tsx | 50 ++++++++++++------- src/pages/History/AccordionWrapper2.tsx | 63 ++++++++++++++---------- 3 files changed, 72 insertions(+), 43 deletions(-) diff --git a/src/pages/AccountSettings/UserFields.tsx b/src/pages/AccountSettings/UserFields.tsx index 0f29d8b..946d69d 100644 --- a/src/pages/AccountSettings/UserFields.tsx +++ b/src/pages/AccountSettings/UserFields.tsx @@ -78,7 +78,7 @@ export default function UserFields({ state.verificationStatus) + const OrgName = useUserStore((state) => state.userAccount?.name.orgname) const valuesByKey: any = {} if (Array.isArray(content[0].Value) && Array.isArray(content[0].Value[0])) { @@ -61,14 +66,25 @@ export default function AccordionWrapper({ content, last, first, createdAt, onCl } async function sendBillByEmail(tariffId: string) { - try { - await makeRequest({ - url: process.env.REACT_APP_DOMAIN + `/customer/sendReport/${tariffId}`, - method: "POST", - }); - enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); - } catch (e) { - enqueueSnackbar("Извините, произошла ошибка"); + if(verificationStatus === VerificationStatus.VERIFICATED && OrgName){ + try { + await makeRequest({ + url: process.env.REACT_APP_DOMAIN + `/customer/sendReport`, + body: {id: tariffId}, + method: "POST", + }); + enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); + } catch (e) { + enqueueSnackbar("Извините, произошла ошибка"); + } + } + navigate("/settings") + if(verificationStatus !== VerificationStatus.VERIFICATED && !OrgName){ + enqueueSnackbar("Пройдите верификацию и заполните название организации"); + } else if(!OrgName){ + enqueueSnackbar("Заполните поле название организации"); + }else if(verificationStatus !== VerificationStatus.VERIFICATED) { + enqueueSnackbar("Пройдите верификацию"); } } @@ -208,19 +224,19 @@ export default function AccordionWrapper({ content, last, first, createdAt, onCl sx={{ ml: "20px", bgcolor: "#EEE4FC", - stroke: "#7E2AEA", + color: "#7E2AEA", borderRadius: 2, "&:hover": { bgcolor: "#7E2AEA", - stroke: "white", + color: "white", }, "&:active": { bgcolor: "black", - stroke: "white", + color: "white", } }} > - + - + state.verificationStatus) + const OrgName = useUserStore((state) => state.userAccount?.name.orgname) async function handleTariffItemClick(tariffId: string) { const { patchCartError } = await addTariffToCart(tariffId); if (patchCartError) { @@ -47,18 +50,28 @@ export default function AccordionWrapper2({ tariff, price, last, first, createdA enqueueSnackbar("Тариф добавлен в корзину"); } } - +console.log(OrgName) async function sendBillByEmail(tariffId: string) { - try { - await makeRequest({ - url: process.env.REACT_APP_DOMAIN + `/customer/sendReport`, - body: {id: tariffId}, - method: "POST", - }); - enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); - } catch (e) { - enqueueSnackbar("Извините, произошла ошибка"); + if(verificationStatus === VerificationStatus.VERIFICATED && OrgName){ + try { + await makeRequest({ + url: process.env.REACT_APP_DOMAIN + `/customer/sendReport`, + body: {id: tariffId}, + method: "POST", + }); + enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); + } catch (e) { + enqueueSnackbar("Извините, произошла ошибка"); + } + } + navigate("/settings") + if(verificationStatus !== VerificationStatus.VERIFICATED && !OrgName){ + enqueueSnackbar("Пройдите верификацию и заполните название организации"); + } else if(!OrgName){ + enqueueSnackbar("Заполните поле название организации"); + }else if(verificationStatus !== VerificationStatus.VERIFICATED) { + enqueueSnackbar("Пройдите верификацию"); } } @@ -193,19 +206,19 @@ export default function AccordionWrapper2({ tariff, price, last, first, createdA sx={{ ml: "20px", bgcolor: "#EEE4FC", - stroke: "#7E2AEA", + color: "#7E2AEA", borderRadius: 2, "&:hover": { - bgcolor: "#7E2AEA", - stroke: "white", + bgcolor: "#7E2AEA", + color: "white", }, "&:active": { bgcolor: "black", - stroke: "white", + color: "white", } }} > - + {isMobile && <> - {/* { e.stopPropagation(); sendBillByEmail(tariff._id); @@ -245,20 +258,20 @@ export default function AccordionWrapper2({ tariff, price, last, first, createdA sx={{ m: "0 10px", bgcolor: "#EEE4FC", - stroke: "#7E2AEA", + color: "#7E2AEA", borderRadius: 2, "&:hover": { bgcolor: "#7E2AEA", - stroke: "white", + color: "white", }, "&:active": { bgcolor: "black", - stroke: "white", + color: "white", } }} > - - */} + + { From 89ada4688275d1a45c42701ae89e84c643444a26 Mon Sep 17 00:00:00 2001 From: Nastya Date: Tue, 21 May 2024 22:03:35 +0300 Subject: [PATCH 05/27] =?UTF-8?q?=D0=B0=D0=BA=D1=82=D1=8B=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D1=8F=D1=8E=D1=82=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D1=8C=20=D0=BE=20=D0=BF=D0=BE=D0=BA=D1=83?= =?UTF-8?q?=D0=BF=D0=BA=D0=B5,=20=D0=B0=20=D0=BD=D0=B5=20=D1=82=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D1=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/History/AccordionWrapper.tsx | 14 ++++++++------ src/pages/History/AccordionWrapper2.tsx | 14 +++++++------- src/pages/History/index.tsx | 2 ++ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/pages/History/AccordionWrapper.tsx b/src/pages/History/AccordionWrapper.tsx index 8317aff..70ee3f6 100644 --- a/src/pages/History/AccordionWrapper.tsx +++ b/src/pages/History/AccordionWrapper.tsx @@ -32,9 +32,10 @@ interface AccordionWrapperProps { first?: boolean; createdAt: string; onClickMail?: any + mainId: string } -export default function AccordionWrapper({ content, last, first, createdAt, onClickMail }: AccordionWrapperProps) { +export default function AccordionWrapper({ content, last, first, createdAt, onClickMail, mainId }: AccordionWrapperProps) { const theme = useTheme() const upMd = useMediaQuery(theme.breakpoints.up("md")) const upSm = useMediaQuery(theme.breakpoints.up("sm")) @@ -65,15 +66,16 @@ export default function AccordionWrapper({ content, last, first, createdAt, onCl } } - async function sendBillByEmail(tariffId: string) { + async function sendBillByEmail(logId: string) { + if(verificationStatus === VerificationStatus.VERIFICATED && OrgName){ try { await makeRequest({ url: process.env.REACT_APP_DOMAIN + `/customer/sendReport`, - body: {id: tariffId}, + body: {id: logId}, method: "POST", }); - enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); + return enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); } catch (e) { enqueueSnackbar("Извините, произошла ошибка"); } @@ -219,7 +221,7 @@ export default function AccordionWrapper({ content, last, first, createdAt, onCl { e.stopPropagation(); - sendBillByEmail(valuesByKey.id); + sendBillByEmail(mainId); }} sx={{ ml: "20px", @@ -271,7 +273,7 @@ export default function AccordionWrapper({ content, last, first, createdAt, onCl { e.stopPropagation(); - sendBillByEmail(valuesByKey.id); + sendBillByEmail(mainId); }} sx={{ m: "0 10px", diff --git a/src/pages/History/AccordionWrapper2.tsx b/src/pages/History/AccordionWrapper2.tsx index 740ac57..9b7ef72 100644 --- a/src/pages/History/AccordionWrapper2.tsx +++ b/src/pages/History/AccordionWrapper2.tsx @@ -31,9 +31,10 @@ interface AccordionWrapperProps { last?: boolean; first?: boolean; createdAt: string; + mainId: string } -export default function AccordionWrapper2({ tariff, price, last, first, createdAt }: AccordionWrapperProps) { +export default function AccordionWrapper2({ tariff, price, last, first, createdAt, mainId }: AccordionWrapperProps) { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const upSm = useMediaQuery(theme.breakpoints.up("sm")); @@ -50,17 +51,16 @@ export default function AccordionWrapper2({ tariff, price, last, first, createdA enqueueSnackbar("Тариф добавлен в корзину"); } } -console.log(OrgName) - async function sendBillByEmail(tariffId: string) { + async function sendBillByEmail(logId: string) { if(verificationStatus === VerificationStatus.VERIFICATED && OrgName){ try { await makeRequest({ url: process.env.REACT_APP_DOMAIN + `/customer/sendReport`, - body: {id: tariffId}, + body: {id: logId}, method: "POST", }); - enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); + return enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); } catch (e) { enqueueSnackbar("Извините, произошла ошибка"); } @@ -201,7 +201,7 @@ console.log(OrgName) { e.stopPropagation(); - sendBillByEmail(tariff._id); + sendBillByEmail(mainId); }} sx={{ ml: "20px", @@ -253,7 +253,7 @@ console.log(OrgName) { e.stopPropagation(); - sendBillByEmail(tariff._id); + sendBillByEmail(mainId); }} sx={{ m: "0 10px", diff --git a/src/pages/History/index.tsx b/src/pages/History/index.tsx index 33daf21..3060f45 100644 --- a/src/pages/History/index.tsx +++ b/src/pages/History/index.tsx @@ -113,6 +113,7 @@ export default function History() { first={index === 0} last={index === historyData?.length - 1} content={(e as HistoryRecord).rawDetails} + mainId={(e as HistoryRecord).id} key={e.id} createdAt={e.createdAt} onClickMail={(event: any) => { @@ -139,6 +140,7 @@ export default function History() { key={e.id} first={index === 0} last={index === historyData?.length - 1} + mainId={(e as HistoryRecord2).id} createdAt={e.createdAt} tariff={e.rawDetails.tariffs[0]} price={e.rawDetails.price/100} From 22aef5c1f80591bedad347655a7d2d5aab70a431 Mon Sep 17 00:00:00 2001 From: Tamara Date: Wed, 22 May 2024 05:19:20 +0300 Subject: [PATCH 06/27] =?UTF-8?q?=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D1=83=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2(=D0=BF?= =?UTF-8?q?=D0=BE=D0=BA=D0=B0=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20?= =?UTF-8?q?=D1=83=20=D1=8E=D1=80.=D0=BB=D0=B8=D1=86)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DocumentsDialog/DocumentItem.tsx | 85 ++++++++++++++++--- .../JuridicalDocumentsDialog.tsx | 9 +- .../DocumentsDialog/NkoDocumentsDialog.tsx | 3 + 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx b/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx index 191aae6..51aab90 100644 --- a/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx @@ -1,18 +1,38 @@ import axios from "axios" -import { Box, SxProps, Theme, Typography, useTheme } from "@mui/material" +import { Box, IconButton, SxProps, Theme, Typography, useTheme } from "@mui/material" import { Document, Page } from "react-pdf" import { Buffer } from "buffer" import { downloadFileToDevice } from "@root/utils/downloadFileToDevice" +import EditIcon from "@mui/icons-material/Edit" +import { ChangeEvent, useRef } from "react" +import { SendDocumentsArgs, Verification } from "@root/model/auth" +import makeRequest from "@api/makeRequest" +import { jsonToFormdata } from "@utils/jsonToFormdata" +import { parseAxiosError } from "@utils/parse-error" +import { readFile } from "@root/utils/readFile" +import { enqueueSnackbar } from "notistack" +type KeyNames = + "inn" | + "rule" | + "certificate" interface Props { - text: string; - documentUrl: string; - sx?: SxProps; + text: string; + documentUrl: string; + sx?: SxProps; + keyName: KeyNames } -export default function DocumentItem({ text, documentUrl = "", sx }: Props) { + +export default function DocumentItem({ text, documentUrl = "", sx, keyName }: Props) { const theme = useTheme() + const fileInputRef = useRef(null) + + function handleChooseFileClick() { + fileInputRef.current?.click() + } + const downloadFile = async () => { const { data } = await axios.get(documentUrl, { responseType: "arraybuffer", @@ -30,6 +50,31 @@ export default function DocumentItem({ text, documentUrl = "", sx }: Props) { return } + async function sendDocument( + e: ChangeEvent + ) { + const target = e.target as HTMLInputElement; + const file = target?.files?.[0] || null; + if (file !== null) { + const readedFile = await readFile(file, "binary") + try { + await makeRequest({ + url: process.env.REACT_APP_DOMAIN + "/verification" + "/verification", + method: "PATCH", + useToken: true, + withCredentials: true, + body: jsonToFormdata({ [keyName]: readedFile }), + }) + + enqueueSnackbar("Данные обновлены") + } catch (nativeError) { + const [error] = parseAxiosError(nativeError) + + enqueueSnackbar(`Ошибка отправки документов. ${error}`) + } + } + } + return ( {documentUrl && ( <> - - {documentUrl.split("/").pop()?.split(".")?.[0]} - + + + {documentUrl.split("/").pop()?.split(".")?.[0]} + + + + + + + + ) : ( diff --git a/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx b/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx index 810f68f..82d9b85 100644 --- a/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx @@ -130,15 +130,18 @@ export default function NkoDocumentsDialog() { <> ) : ( From 8e79f16640c2d7ab3c19d28dc7db7b8bcda3bb76 Mon Sep 17 00:00:00 2001 From: Nastya Date: Wed, 22 May 2024 23:41:34 +0300 Subject: [PATCH 07/27] verification/v1.0.0 --- src/api/verification.ts | 2 +- src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx | 2 +- .../AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/verification.ts b/src/api/verification.ts index 8dcdc5f..5374eca 100644 --- a/src/api/verification.ts +++ b/src/api/verification.ts @@ -10,7 +10,7 @@ import type { } from "@root/model/auth" import { AxiosError } from "axios" -const apiUrl = process.env.REACT_APP_DOMAIN + "/verification" +const apiUrl = process.env.REACT_APP_DOMAIN + "/verification/v1.0.0" export async function verification( userId: string diff --git a/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx b/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx index 51aab90..05262ed 100644 --- a/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx @@ -59,7 +59,7 @@ export default function DocumentItem({ text, documentUrl = "", sx, keyName }: Pr const readedFile = await readFile(file, "binary") try { await makeRequest({ - url: process.env.REACT_APP_DOMAIN + "/verification" + "/verification", + url: `${process.env.REACT_APP_DOMAIN}/verification/v1.0.0/verification`, method: "PATCH", useToken: true, withCredentials: true, diff --git a/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx b/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx index 82d9b85..325ebf2 100644 --- a/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx @@ -86,7 +86,7 @@ export default function NkoDocumentsDialog() { const [_, updateDocumentsError] = await updateDocuments( deleteEmptyKeys({ - status: "org", + status: "nko", inn, rule, certificate, From 9261f3e797e10af7864ffe96bd8eb91abba85da8 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Mon, 27 May 2024 18:43:38 +0300 Subject: [PATCH 08/27] refactor: makeRequests decomposed --- src/api/auth.ts | 18 +- src/api/cart.ts | 113 ++-- src/api/history.ts | 162 +++-- src/api/makeRequest.ts | 56 +- src/api/price.ts | 4 +- src/api/promocode.ts | 21 +- src/api/recentlyPurchasedTariffs.ts | 35 +- src/api/tariff.ts | 186 +++--- src/api/ticket.ts | 36 +- src/api/user.ts | 40 +- src/api/verification.ts | 149 +++-- src/components/FloatingSupportChat/Chat.tsx | 49 +- .../DocumentsDialog/DocumentItem.tsx | 237 ++++--- src/pages/History/AccordionWrapper.tsx | 624 +++++++++--------- src/pages/History/AccordionWrapper2.tsx | 567 ++++++++-------- src/pages/History/index.tsx | 284 ++++---- src/pages/QuizPayment/QuizPayment.tsx | 14 - src/pages/Support/SupportChat.tsx | 23 +- src/pages/auth/Recover.tsx | 1 - src/pages/auth/RecoverPassword.tsx | 335 +++++----- src/utils/handleComponentError.ts | 47 +- 21 files changed, 1586 insertions(+), 1415 deletions(-) diff --git a/src/api/auth.ts b/src/api/auth.ts index 603e72d..7cc5010 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -9,7 +9,7 @@ import type { RegisterResponse, } from "@frontend/kitui"; -const apiUrl = process.env.REACT_APP_DOMAIN + "/auth"; +const API_URL = `${process.env.REACT_APP_DOMAIN}/auth`; export async function register( login: string, @@ -21,7 +21,7 @@ export async function register( RegisterRequest, RegisterResponse >({ - url: apiUrl + "/register", + url: `${API_URL}/register`, body: { login, password, phoneNumber }, useToken: false, withCredentials: true, @@ -41,7 +41,7 @@ export async function login( ): Promise<[LoginResponse | null, string?]> { try { const loginResponse = await makeRequest({ - url: apiUrl + "/login", + url: `${API_URL}/login`, body: { login, password }, useToken: false, withCredentials: true, @@ -60,14 +60,20 @@ export async function recover( ): Promise<[unknown | null, string?]> { try { const formData = new FormData(); + formData.append("email", email); - formData.append("RedirectionURL", process.env.REACT_APP_DOMAIN + "/changepwd") + formData.append( + "RedirectionURL", + `${process.env.REACT_APP_DOMAIN}/changepwd` + ); + const recoverResponse = await makeRequest({ - url: process.env.REACT_APP_DOMAIN + "/codeword/recover", + url: `${process.env.REACT_APP_DOMAIN}/codeword/recover`, body: formData, useToken: false, withCredentials: true, }); + return [recoverResponse]; } catch (nativeError) { const [error] = parseAxiosError(nativeError); @@ -79,7 +85,7 @@ export async function recover( export async function logout(): Promise<[unknown, string?]> { try { const logoutResponse = await makeRequest({ - url: apiUrl + "/logout", + url: `${API_URL}/logout`, method: "POST", useToken: true, withCredentials: true, diff --git a/src/api/cart.ts b/src/api/cart.ts index 299b932..c4c3dcb 100644 --- a/src/api/cart.ts +++ b/src/api/cart.ts @@ -1,83 +1,84 @@ -import { UserAccount } from "@frontend/kitui" -import makeRequest from "@api/makeRequest" +import { UserAccount } from "@frontend/kitui"; +import makeRequest from "@api/makeRequest"; -import { parseAxiosError } from "@root/utils/parse-error" +import { parseAxiosError } from "@root/utils/parse-error"; -const apiUrl = process.env.REACT_APP_DOMAIN + "/customer" +const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; export async function patchCart( - tariffId: string + tariffId: string ): Promise<[string[], string?]> { - try { - const patchCartResponse = await makeRequest({ - url: apiUrl + `/cart?id=${tariffId}`, - method: "PATCH", - useToken: true, - }) + try { + const patchCartResponse = await makeRequest({ + url: `${API_URL}/cart?id=${tariffId}`, + method: "PATCH", + useToken: true, + }); - return [patchCartResponse.cart] - } catch (nativeError) { - let [error, status] = parseAxiosError(nativeError) - if (status === 400 && error.indexOf("invalid id") !== -1) error = "Данный тариф более недоступен" + return [patchCartResponse.cart]; + } catch (nativeError) { + let [error, status] = parseAxiosError(nativeError); + if (status === 400 && error.indexOf("invalid id") !== -1) + error = "Данный тариф более недоступен"; - return [[], `Не удалось добавить товар в корзину. ${error}`] - } + return [[], `Не удалось добавить товар в корзину. ${error}`]; + } } export async function deleteCart( - tariffId: string + tariffId: string ): Promise<[string[], string?]> { - try { - const deleteCartResponse = await makeRequest({ - url: apiUrl + `/cart?id=${tariffId}`, - method: "DELETE", - useToken: true, - }) + try { + const deleteCartResponse = await makeRequest({ + url: `${API_URL}/cart?id=${tariffId}`, + method: "DELETE", + useToken: true, + }); - return [deleteCartResponse.cart] - } catch (nativeError) { - const [error] = parseAxiosError(nativeError) + return [deleteCartResponse.cart]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [[], `Не удалось удалить товар из корзины. ${error}`] - } + return [[], `Не удалось удалить товар из корзины. ${error}`]; + } } export async function payCart(): Promise<[UserAccount | null, string?]> { - try { - const payCartResponse = await makeRequest({ - url: apiUrl + "/cart/pay", - method: "POST", - useToken: true, - }) + try { + const payCartResponse = await makeRequest({ + url: `${API_URL}/cart/pay`, + method: "POST", + useToken: true, + }); - return [payCartResponse] - } catch (nativeError) { - const [error] = parseAxiosError(nativeError) + return [payCartResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Не удалось оплатить товар из корзины. ${error}`] - } + return [null, `Не удалось оплатить товар из корзины. ${error}`]; + } } export async function patchCurrency( - currency: string + currency: string ): Promise<[UserAccount | null, string?]> { - try { - const patchCurrencyResponse = await makeRequest< + try { + const patchCurrencyResponse = await makeRequest< { currency: string }, UserAccount >({ - url: apiUrl + "/wallet", - method: "PATCH", - useToken: true, - body: { - currency, - }, - }) + url: `${API_URL}/wallet`, + method: "PATCH", + useToken: true, + body: { + currency, + }, + }); - return [patchCurrencyResponse] - } catch (nativeError) { - const [error] = parseAxiosError(nativeError) + return [patchCurrencyResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Не удалось изменить валюту. ${error}`] - } -} \ No newline at end of file + return [null, `Не удалось изменить валюту. ${error}`]; + } +} diff --git a/src/api/history.ts b/src/api/history.ts index df70b7f..42f5fff 100644 --- a/src/api/history.ts +++ b/src/api/history.ts @@ -1,84 +1,128 @@ -import {Tariff} from "@frontend/kitui" -import {parseAxiosError} from "@root/utils/parse-error" -import makeRequest from "@api/makeRequest" +import { Tariff } from "@frontend/kitui"; +import { parseAxiosError } from "@root/utils/parse-error"; +import makeRequest from "@api/makeRequest"; export interface GetHistoryResponse { - totalPages: number; - records: HistoryRecord[]; + totalPages: number; + records: HistoryRecord[]; } export type HistoryRecord = { - comment: string; - createdAt: string; - id: string; - isDeleted: boolean; - key: string; - rawDetails: [RawDetails, KeyValue]; - updatedAt: string; - userId: string; + comment: string; + createdAt: string; + id: string; + isDeleted: boolean; + key: string; + rawDetails: [RawDetails, KeyValue]; + updatedAt: string; + userId: string; }; export interface GetHistoryResponse2 { - totalPages: number; - records: HistoryRecord2[]; + totalPages: number; + records: HistoryRecord2[]; } export type HistoryRecord2 = { - comment: string; - createdAt: string; - id: string; - isDeleted: boolean; - key: string; - rawDetails: { - price: number; - tariffs: Tariff[]; - }; - updatedAt: string; - userId: string; + comment: string; + createdAt: string; + id: string; + isDeleted: boolean; + key: string; + rawDetails: { + price: number; + tariffs: Tariff[]; + }; + updatedAt: string; + userId: string; }; - export type KeyValue = { Key: string; Value: string | number }; export type RawDetails = { - Key: "tariffs" | "price"; - Value: string | number | KeyValue[][]; -} + Key: "tariffs" | "price"; + Value: string | number | KeyValue[][]; +}; -export async function getHistory(): Promise<[GetHistoryResponse | GetHistoryResponse2 | null, string?]> { - try { - const historyResponse = await makeRequest({ - url: process.env.REACT_APP_DOMAIN + "/customer/history?page=1&limit=100&type=payCart", - method: "get", - useToken: true, - }) +const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; - if (!Array.isArray(historyResponse.records[0]?.rawDetails)) { - return [historyResponse] as [GetHistoryResponse2] - } +export async function getHistory(): Promise< + [GetHistoryResponse | GetHistoryResponse2 | null, string?] +> { + try { + const historyResponse = await makeRequest< + never, + GetHistoryResponse | GetHistoryResponse2 + >({ + url: `${API_URL}/history?page=1&limit=100&type=payCart`, + method: "GET", + useToken: true, + }); - const checked = historyResponse.records.map((data) => { - //const buffer:RawDetails[] = []; - /*(data.rawDetails as HistoryRecord["rawDetails"]).forEach((slot) => { + if (!Array.isArray(historyResponse.records[0]?.rawDetails)) { + return [historyResponse] as [GetHistoryResponse2]; + } + + const checked = historyResponse.records.map((data) => { + //const buffer:RawDetails[] = []; + /*(data.rawDetails as HistoryRecord["rawDetails"]).forEach((slot) => { const index = regList[slot.Key] buffer[index] = { ...slot } })*/ - //Чистим дыры с помощью .filter(() => true) - //@ts-ignore - //data.rawDetails = buffer - const checkedRowDetails = [ - (data.rawDetails as HistoryRecord["rawDetails"]).find((details) => details.Key === "tariffs") as RawDetails, - (data.rawDetails as HistoryRecord["rawDetails"]).find((details) => details.Key === "price") as KeyValue - ] - return {...data, rawDetails: checkedRowDetails} as HistoryRecord - }) + //Чистим дыры с помощью .filter(() => true) + //@ts-ignore + //data.rawDetails = buffer + const checkedRowDetails = [ + (data.rawDetails as HistoryRecord["rawDetails"]).find( + (details) => details.Key === "tariffs" + ) as RawDetails, + (data.rawDetails as HistoryRecord["rawDetails"]).find( + (details) => details.Key === "price" + ) as KeyValue, + ]; + return { ...data, rawDetails: checkedRowDetails } as HistoryRecord; + }); + historyResponse.records = checked || []; + return [historyResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - historyResponse.records = checked || [] - return [historyResponse] - } catch (nativeError) { - const [error] = parseAxiosError(nativeError) - - return [null, `Не удалось получить историю. ${error}`] - } + return [null, `Не удалось получить историю. ${error}`]; + } } + +export const sendReport = async ( + id: string +): Promise<[unknown | null, string?]> => { + try { + const sendReportResponse = await makeRequest<{ id: string }, unknown>({ + url: `${API_URL}/sendReport`, + method: "POST", + body: { id }, + }); + + return [sendReportResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + + return [[], `Не удалось отправить отчёт. ${error}`]; + } +}; + +export const sendReportById = async ( + tariffId: string +): Promise<[unknown | null, string?]> => { + try { + const sendReportResponse = await makeRequest({ + url: `${API_URL}/sendReport/${tariffId}`, + method: "POST", + }); + + return [sendReportResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + + return [[], `Не удалось отправить отчёт. ${error}`]; + } +}; diff --git a/src/api/makeRequest.ts b/src/api/makeRequest.ts index f76e479..a356f5c 100644 --- a/src/api/makeRequest.ts +++ b/src/api/makeRequest.ts @@ -5,30 +5,44 @@ import { clearUserData } from "@root/stores/user"; import { clearCustomTariffs } from "@root/stores/customTariffs"; import { clearTickets } from "@root/stores/tickets"; import { redirect } from "react-router-dom"; -import {setNotEnoughMoneyAmount} from "@stores/cart" +import { setNotEnoughMoneyAmount } from "@stores/cart"; -interface MakeRequest { method?: Method | undefined; url: string; body?: unknown; useToken?: boolean | undefined; contentType?: boolean | undefined; responseType?: ResponseType | undefined; signal?: AbortSignal | undefined; withCredentials?: boolean | undefined; } +interface MakeRequest { + method?: Method | undefined; + url: string; + body?: unknown; + useToken?: boolean | undefined; + contentType?: boolean | undefined; + responseType?: ResponseType | undefined; + signal?: AbortSignal | undefined; + withCredentials?: boolean | undefined; +} interface ErrorResponseData { - message?: string; + message?: string; } -async function makeRequest(data: MakeRequest): Promise { - try { - const response = await KIT.makeRequest(data) +async function makeRequest( + data: MakeRequest +): Promise { + try { + const response = await KIT.makeRequest(data); - return response as TResponse - } catch (e) { - const error = e as AxiosError; - if (error.response?.status === 400 && (error.response?.data as ErrorResponseData)?.message === "refreshToken is empty") { - - clearAuthToken(); - clearUserData(); - clearCustomTariffs(); - clearTickets(); - setNotEnoughMoneyAmount(0) - redirect("/"); - } - throw e - }; -}; + return response as TResponse; + } catch (e) { + const error = e as AxiosError; + if ( + error.response?.status === 400 && + (error.response?.data as ErrorResponseData)?.message === + "refreshToken is empty" + ) { + clearAuthToken(); + clearUserData(); + clearCustomTariffs(); + clearTickets(); + setNotEnoughMoneyAmount(0); + redirect("/"); + } + throw e; + } +} export default makeRequest; diff --git a/src/api/price.ts b/src/api/price.ts index e50eb39..fe8c8dc 100644 --- a/src/api/price.ts +++ b/src/api/price.ts @@ -5,7 +5,7 @@ import { parseAxiosError } from "@root/utils/parse-error"; import { enqueueSnackbar } from "notistack"; import useSWR from "swr"; -const apiUrl = process.env.REACT_APP_DOMAIN + "/price"; +const API_URL = `${process.env.REACT_APP_DOMAIN}/price`; export async function getDiscounts(userId: string | null) { if (userId === null) { @@ -14,7 +14,7 @@ export async function getDiscounts(userId: string | null) { try { const discountsResponse = await makeRequest({ - url: `${apiUrl}/discount/user/${userId}`, + url: `${API_URL}/discount/user/${userId}`, method: "get", useToken: true, }); diff --git a/src/api/promocode.ts b/src/api/promocode.ts index 1644bf2..3234f47 100644 --- a/src/api/promocode.ts +++ b/src/api/promocode.ts @@ -1,28 +1,19 @@ -import makeRequest from "@api/makeRequest" +import makeRequest from "@api/makeRequest"; import { parseAxiosError } from "@utils/parse-error"; -const apiUrl = process.env.REACT_APP_DOMAIN + "/codeword/promocode"; +const API_URL = `${process.env.REACT_APP_DOMAIN}/codeword/promocode`; export async function activatePromocode(promocode: string) { try { const response = await makeRequest< - | { - codeword: string; - } - | { - fastLink: string; - }, - { - greetings: string; - } + { codeword: string } | { fastLink: string }, + { greetings: string } >({ - url: apiUrl + "/activate", + url: `${API_URL}/activate`, method: "POST", contentType: true, - body: { - codeword: promocode, - }, + body: { codeword: promocode }, }); return response.greetings; diff --git a/src/api/recentlyPurchasedTariffs.ts b/src/api/recentlyPurchasedTariffs.ts index d29f24c..e9b4758 100644 --- a/src/api/recentlyPurchasedTariffs.ts +++ b/src/api/recentlyPurchasedTariffs.ts @@ -1,17 +1,22 @@ -import makeRequest from "@api/makeRequest" -import { parseAxiosError } from "@root/utils/parse-error" +import makeRequest from "@api/makeRequest"; +import { parseAxiosError } from "@root/utils/parse-error"; -export async function getRecentlyPurchasedTariffs(): Promise<[any | null, string?]> { - try { - const recentlyPurchased = await makeRequest({ - url: process.env.REACT_APP_DOMAIN + "/customer/recent", - method: "get", - useToken: true, - }) - return [recentlyPurchased] - } catch (nativeError) { - const [error] = parseAxiosError(nativeError) +const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; - return [null, `Не удалось получить историю. ${error}`] - } -} \ No newline at end of file +export async function getRecentlyPurchasedTariffs(): Promise< + [any | null, string?] +> { + try { + const recentlyPurchased = await makeRequest({ + url: `${API_URL}/recent`, + method: "GET", + useToken: true, + }); + + return [recentlyPurchased]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + + return [null, `Не удалось получить историю. ${error}`]; + } +} diff --git a/src/api/tariff.ts b/src/api/tariff.ts index 88668d5..218b236 100644 --- a/src/api/tariff.ts +++ b/src/api/tariff.ts @@ -1,124 +1,136 @@ -import makeRequest from "@api/makeRequest" +import makeRequest from "@api/makeRequest"; import { Tariff } from "@frontend/kitui"; import { parseAxiosError } from "@root/utils/parse-error"; -import type { PrivilegeWithoutPrice, ServiceKeyToPrivilegesMap } from "@root/model/privilege"; +import type { + PrivilegeWithoutPrice, + ServiceKeyToPrivilegesMap, +} from "@root/model/privilege"; import type { GetTariffsResponse } from "@root/model/tariff"; import { removeTariffFromCart } from "@root/stores/user"; -const apiUrl = process.env.REACT_APP_DOMAIN + "/strator" +const API_URL = `${process.env.REACT_APP_DOMAIN}/strator`; export async function getTariffs( - apiPage: number, - tariffsPerPage: number, - signal: AbortSignal | undefined + apiPage: number, + tariffsPerPage: number, + signal: AbortSignal | undefined ): Promise<[GetTariffsResponse | null, string?]> { - try { - const tariffsResponse = await makeRequest({ - url: apiUrl + `/tariff?page=${apiPage}&limit=${tariffsPerPage}`, - method: "get", - useToken: true, - signal, - }); + try { + const tariffsResponse = await makeRequest({ + url: `${API_URL}/tariff?page=${apiPage}&limit=${tariffsPerPage}`, + method: "get", + useToken: true, + signal, + }); - return [tariffsResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [tariffsResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Не удалось получить список тарифов. ${error}`]; - } + return [null, `Не удалось получить список тарифов. ${error}`]; + } } interface CreateTariffBody { - name: string; - price?: number; - isCustom: boolean; - privileges: PrivilegeWithoutPrice[]; + name: string; + price?: number; + isCustom: boolean; + privileges: PrivilegeWithoutPrice[]; } -export async function createTariff(tariff: CreateTariffBody): Promise<[Tariff | null, string?]> { - try { - const createTariffResponse = await makeRequest({ - url: `${apiUrl}/tariff`, - method: "post", - useToken: true, - body: tariff, - }); +export async function createTariff( + tariff: CreateTariffBody +): Promise<[Tariff | null, string?]> { + try { + const createTariffResponse = await makeRequest({ + url: `${API_URL}/tariff`, + method: "post", + useToken: true, + body: tariff, + }); - return [createTariffResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [createTariffResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Не удалось создать тариф. ${error}`]; - } + return [null, `Не удалось создать тариф. ${error}`]; + } } -export async function getTariffById(tariffId: string): Promise<[Tariff | null, string?, number?]> { - try { - const getTariffByIdResponse = await makeRequest({ - url: `${apiUrl}/tariff/${tariffId}`, - method: "get", - useToken: true, - }); +export async function getTariffById( + tariffId: string +): Promise<[Tariff | null, string?, number?]> { + try { + const getTariffByIdResponse = await makeRequest({ + url: `${API_URL}/tariff/${tariffId}`, + method: "get", + useToken: true, + }); - return [getTariffByIdResponse]; - } catch (nativeError) { - const [error, status] = parseAxiosError(nativeError); + return [getTariffByIdResponse]; + } catch (nativeError) { + const [error, status] = parseAxiosError(nativeError); - return [null, `Не удалось получить тарифы. ${error}`, status]; - } + return [null, `Не удалось получить тарифы. ${error}`, status]; + } } export async function getCustomTariffs( - signal: AbortSignal | undefined + signal: AbortSignal | undefined ): Promise<[ServiceKeyToPrivilegesMap | null, string?]> { - try { - const customTariffsResponse = await makeRequest({ - url: apiUrl + "/privilege/service", - signal, - method: "get", - useToken: true, - }); + try { + const customTariffsResponse = await makeRequest< + null, + ServiceKeyToPrivilegesMap + >({ + url: `${API_URL}/privilege/service`, + signal, + method: "get", + useToken: true, + }); - const tempCustomTariffsResponse = { - ...customTariffsResponse, - squiz: customTariffsResponse.squiz - }; + const tempCustomTariffsResponse = { + ...customTariffsResponse, + squiz: customTariffsResponse.squiz, + }; - return [tempCustomTariffsResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [tempCustomTariffsResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Не удалось получить мои тарифы. ${error}`]; - } + return [null, `Не удалось получить мои тарифы. ${error}`]; + } } export async function getTariffArray(tariffIds: string[] | undefined) { - if (!tariffIds) return null; + if (!tariffIds) return null; - const responses = await Promise.allSettled(tariffIds.map(tariffId => - makeRequest({ - url: `${apiUrl}/tariff/${tariffId}`, - method: "get", - useToken: true, - }) - )); + const responses = await Promise.allSettled( + tariffIds.map((tariffId) => + makeRequest({ + url: `${API_URL}/tariff/${tariffId}`, + method: "get", + useToken: true, + }) + ) + ); - const tariffs: Tariff[] = []; + const tariffs: Tariff[] = []; - responses.forEach((response, index) => { - switch (response.status) { - case "fulfilled": { - tariffs.push(response.value); - break; - } - case "rejected": { - const [, status] = parseAxiosError(response.reason); - if (status === 404) removeTariffFromCart(tariffIds[index]); - break; - } - } - }); + responses.forEach((response, index) => { + switch (response.status) { + case "fulfilled": { + tariffs.push(response.value); + break; + } + case "rejected": { + const [, status] = parseAxiosError(response.reason); + if (status === 404) removeTariffFromCart(tariffIds[index]); + break; + } + } + }); - return tariffs; + return tariffs; } diff --git a/src/api/ticket.ts b/src/api/ticket.ts index a5a9138..85059b1 100644 --- a/src/api/ticket.ts +++ b/src/api/ticket.ts @@ -1,9 +1,13 @@ -import makeRequest from "@api/makeRequest" +import makeRequest from "@api/makeRequest"; import { parseAxiosError } from "@root/utils/parse-error"; import { SendTicketMessageRequest } from "@frontend/kitui"; -const apiUrl = process.env.REACT_APP_DOMAIN + "/heruvym"; +type SendFileResponse = { + message: string; +}; + +const API_URL = `${process.env.REACT_APP_DOMAIN}/heruvym`; export async function sendTicketMessage( ticketId: string, @@ -14,7 +18,7 @@ export async function sendTicketMessage( SendTicketMessageRequest, null >({ - url: `${apiUrl}/send`, + url: `${API_URL}/send`, method: "POST", useToken: true, body: { ticket: ticketId, message: message, lang: "ru", files: [] }, @@ -31,7 +35,7 @@ export async function sendTicketMessage( export async function shownMessage(id: string): Promise<[null, string?]> { try { const shownMessageResponse = await makeRequest<{ id: string }, null>({ - url: apiUrl + "/shown", + url: `${API_URL}/shown`, method: "POST", useToken: true, body: { id }, @@ -44,3 +48,27 @@ export async function shownMessage(id: string): Promise<[null, string?]> { return [null, `Не удалось прочесть сообщение. ${error}`]; } } + +export const sendFile = async ( + ticketId: string, + file: File +): Promise<[SendFileResponse | null, string?]> => { + try { + const body = new FormData(); + + body.append(file.name, file); + body.append("ticket", ticketId); + + const sendResponse = await makeRequest({ + method: "POST", + url: `${process.env.REACT_APP_DOMAIN}/sendFiles`, + body, + }); + + return [sendResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + + return [null, `Не удалось отправить файл. ${error}`]; + } +}; diff --git a/src/api/user.ts b/src/api/user.ts index 13a3358..4ee472f 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -1,27 +1,27 @@ -import { User } from "@frontend/kitui" -import makeRequest from "@api/makeRequest" -import { PatchUserRequest } from "@root/model/user" -import { parseAxiosError } from "@root/utils/parse-error" +import { User } from "@frontend/kitui"; +import makeRequest from "@api/makeRequest"; +import { PatchUserRequest } from "@root/model/user"; +import { parseAxiosError } from "@root/utils/parse-error"; -const apiUrl = process.env.REACT_APP_DOMAIN + "/user" +const API_URL = `${process.env.REACT_APP_DOMAIN}/user`; export async function patchUser( - user: PatchUserRequest + user: PatchUserRequest ): Promise<[User | null, string?]> { - try { - const patchUserResponse = await makeRequest({ - url: apiUrl+"/", - contentType: true, - method: "PATCH", - useToken: true, - withCredentials: false, - body: user, - }) + try { + const patchUserResponse = await makeRequest({ + url: `${API_URL}/`, + contentType: true, + method: "PATCH", + useToken: true, + withCredentials: false, + body: user, + }); - return [patchUserResponse] - } catch (nativeError) { - const [error] = parseAxiosError(nativeError) + return [patchUserResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Не удалось изменить пользователя. ${error}`] - } + return [null, `Не удалось изменить пользователя. ${error}`]; + } } diff --git a/src/api/verification.ts b/src/api/verification.ts index 5374eca..2eebc5a 100644 --- a/src/api/verification.ts +++ b/src/api/verification.ts @@ -1,83 +1,110 @@ -import makeRequest from "@api/makeRequest" +import makeRequest from "@api/makeRequest"; -import { jsonToFormdata } from "@root/utils/jsonToFormdata" -import { parseAxiosError } from "@root/utils/parse-error" +import { jsonToFormdata } from "@root/utils/jsonToFormdata"; +import { parseAxiosError } from "@root/utils/parse-error"; import type { - Verification, - SendDocumentsArgs, - UpdateDocumentsArgs, -} from "@root/model/auth" -import { AxiosError } from "axios" + Verification, + SendDocumentsArgs, + UpdateDocumentsArgs, +} from "@root/model/auth"; -const apiUrl = process.env.REACT_APP_DOMAIN + "/verification/v1.0.0" +const API_URL = `${process.env.REACT_APP_DOMAIN}/verification/v1.0.0/verification`; export async function verification( - userId: string + userId: string ): Promise<[Verification | null, string?]> { - try { - const verificationResponse = await makeRequest({ - url: apiUrl + "/verification/" + userId, - method: "GET", - useToken: true, - withCredentials: true, - }) + try { + const verificationResponse = await makeRequest({ + url: `${API_URL}/${userId}`, + method: "GET", + useToken: true, + withCredentials: true, + }); - verificationResponse.files = verificationResponse.files.map((obj) => { - obj.url = obj.url.replace("https://hub.pena.digital", process.env.REACT_APP_DOMAIN?.toString() || "").replace("https://shub.pena.digital", process.env.REACT_APP_DOMAIN?.toString() || "") - return obj - }) + verificationResponse.files = verificationResponse.files.map((obj) => { + obj.url = obj.url + .replace( + "https://hub.pena.digital", + process.env.REACT_APP_DOMAIN?.toString() || "" + ) + .replace( + "https://shub.pena.digital", + process.env.REACT_APP_DOMAIN?.toString() || "" + ); + return obj; + }); - return [verificationResponse] - } catch (nativeError) { - const err = nativeError as AxiosError - if (err.response?.status === 404) { - return [null, `нет данных`] - } - const [error] = parseAxiosError(nativeError) + return [verificationResponse]; + } catch (nativeError) { + const [error, status] = parseAxiosError(nativeError); - return [null, `Ошибка запроса верификации. ${error}`] - } + if (status === 404) { + return [null, "нет данных"]; + } + + return [null, `Ошибка запроса верификации. ${error}`]; + } } export async function sendDocuments( - documents: SendDocumentsArgs + documents: SendDocumentsArgs ): Promise<[Verification | "OK" | null, string?]> { - try { - const sendDocumentsResponse = await makeRequest({ - url: apiUrl + "/verification", - method: "POST", - useToken: true, - withCredentials: true, - body: jsonToFormdata({ ...documents, egrule: documents.inn }), - }) + try { + const sendDocumentsResponse = await makeRequest({ + url: API_URL, + method: "POST", + useToken: true, + withCredentials: true, + body: jsonToFormdata({ ...documents, egrule: documents.inn }), + }); - return [sendDocumentsResponse] - } catch (nativeError) { - const [error] = parseAxiosError(nativeError) + return [sendDocumentsResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка отправки документов. ${error}`] - } + return [null, `Ошибка отправки документов. ${error}`]; + } } export async function updateDocuments( - documents: UpdateDocumentsArgs -): Promise<[Verification | "OK" | null, string? ]> { - try { - const updateDocumentsResponse = await makeRequest({ - url: apiUrl + "/verification/file", - method: "PATCH", - useToken: true, - withCredentials: true, - body: jsonToFormdata( - documents.inn ? { ...documents, egrule: documents.inn } : documents - ), - }) + documents: UpdateDocumentsArgs +): Promise<[Verification | "OK" | null, string?]> { + try { + const updateDocumentsResponse = await makeRequest({ + url: `${API_URL}/file`, + method: "PATCH", + useToken: true, + withCredentials: true, + body: jsonToFormdata( + documents.inn ? { ...documents, egrule: documents.inn } : documents + ), + }); - return [updateDocumentsResponse] - } catch (nativeError) { - const [error] = parseAxiosError(nativeError) + return [updateDocumentsResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка обновления документов. ${error}`] - } + return [null, `Ошибка обновления документов. ${error}`]; + } } + +export const updateDocument = async ( + body: FormData +): Promise<[Verification | "OK" | null, string?]> => { + try { + const updateDocumentResponse = await makeRequest({ + url: API_URL, + method: "PATCH", + body, + useToken: true, + withCredentials: true, + }); + + return [updateDocumentResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + + return [null, `Ошибка обновления документа. ${error}`]; + } +}; diff --git a/src/components/FloatingSupportChat/Chat.tsx b/src/components/FloatingSupportChat/Chat.tsx index a558e64..8f83e99 100644 --- a/src/components/FloatingSupportChat/Chat.tsx +++ b/src/components/FloatingSupportChat/Chat.tsx @@ -10,7 +10,6 @@ import { useMediaQuery, useTheme, } from "@mui/material"; -import makeRequest from "@api/makeRequest" import { createTicket, getMessageFromFetchError, @@ -28,13 +27,17 @@ import { useMemo, useRef, useState, - WheelEvent + WheelEvent, } from "react"; import ChatMessage from "../ChatMessage"; import SendIcon from "../icons/SendIcon"; import ArrowLeft from "@root/assets/Icons/arrowLeft"; import UserCircleIcon from "./UserCircleIcon"; -import { sendTicketMessage, shownMessage } from "@root/api/ticket"; +import { + sendTicketMessage, + shownMessage, + sendFile as sendFileRequest, +} from "@root/api/ticket"; import { useSSETab } from "@root/utils/hooks/useSSETab"; import { ACCEPT_SEND_MEDIA_TYPES_MAP, @@ -115,7 +118,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { ? offHoursMessage : workingHoursMessage; - return ({ + return { created_at: new Date().toISOString(), files: [], id: "111", @@ -125,8 +128,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { shown: { me: 1 }, ticket_id: "111", user_id: "greetingMessage", - }); - + }; }, [open]); useTicketMessages({ @@ -182,7 +184,8 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { onSuccess: (result) => { if (result.data?.length) { const currentTicket = result.data.find( - ({ origin, state }) => !origin.includes("/support") && state !== "close" + ({ origin, state }) => + !origin.includes("/support") && state !== "close" ); if (!currentTicket) { @@ -199,7 +202,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { const message = getMessageFromFetchError(error); if (message) enqueueSnackbar(message); }, - onFetchStateChange: () => { }, + onFetchStateChange: () => {}, enabled: Boolean(user), }); @@ -228,7 +231,6 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { scrollToBottom(); }, [open]); - useEffect( function scrollOnNewMessage() { if (!chatBoxRef.current) return; @@ -353,19 +355,15 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { const ticketId = ticket.sessionData?.ticketId || data?.Ticket; if (ticketId !== undefined) { if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize"); - try { - const body = new FormData(); - body.append(file.name, file); - body.append("ticket", ticketId); - await makeRequest({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/sendFiles", - body: body, - method: "POST", - }); - } catch (error: any) { - const errorMessage = getMessageFromFetchError(error); - if (errorMessage) enqueueSnackbar(errorMessage); + const [, sendFileError] = await sendFileRequest( + ticketId, + file + ); + + if(sendFileError) { + enqueueSnackbar(sendFileError) + } } return true; } @@ -547,8 +545,13 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { ); })} {!ticket.sessionData?.ticketId && ( - ) - } + + )} ; - keyName: KeyNames + text: string; + documentUrl: string; + sx?: SxProps; + keyName: KeyNames; } +export default function DocumentItem({ + text, + documentUrl = "", + sx, + keyName, +}: Props) { + const theme = useTheme(); -export default function DocumentItem({ text, documentUrl = "", sx, keyName }: Props) { - const theme = useTheme() + const fileInputRef = useRef(null); - const fileInputRef = useRef(null) + function handleChooseFileClick() { + fileInputRef.current?.click(); + } - function handleChooseFileClick() { - fileInputRef.current?.click() - } + const downloadFile = async () => { + const { data } = await axios.get(documentUrl, { + responseType: "arraybuffer", + }); - const downloadFile = async () => { - const { data } = await axios.get(documentUrl, { - responseType: "arraybuffer", - }) + if (!data) { + return; + } - if (!data) { - return - } + downloadFileToDevice( + `${documentUrl.split("/").pop()?.split(".")?.[0] || "document"}.pdf`, + Buffer.from(data) + ); - downloadFileToDevice( - `${documentUrl.split("/").pop()?.split(".")?.[0] || "document"}.pdf`, - Buffer.from(data) - ) + return; + }; - return - } + async function sendDocument(e: ChangeEvent) { + const target = e.target as HTMLInputElement; + const file = target?.files?.[0] || null; + if (file !== null) { + const readedFile = await readFile(file, "binary"); + const [, updateDocumentError] = await updateDocument( + jsonToFormdata({ [keyName]: readedFile }) + ); - async function sendDocument( - e: ChangeEvent - ) { - const target = e.target as HTMLInputElement; - const file = target?.files?.[0] || null; - if (file !== null) { - const readedFile = await readFile(file, "binary") - try { - await makeRequest({ - url: `${process.env.REACT_APP_DOMAIN}/verification/v1.0.0/verification`, - method: "PATCH", - useToken: true, - withCredentials: true, - body: jsonToFormdata({ [keyName]: readedFile }), - }) + if (updateDocumentError) { + return enqueueSnackbar( + `Ошибка отправки документов. ${updateDocumentError}` + ); + } - enqueueSnackbar("Данные обновлены") - } catch (nativeError) { - const [error] = parseAxiosError(nativeError) + enqueueSnackbar("Данные обновлены"); + } + } - enqueueSnackbar(`Ошибка отправки документов. ${error}`) - } - } - } + return ( + + + {text} + + {documentUrl && ( + <> + + + {documentUrl.split("/").pop()?.split(".")?.[0]} + + + + + + - return ( - - - {text} - - {documentUrl && ( - <> - - - {documentUrl.split("/").pop()?.split(".")?.[0]} - - - - - - - - - - - - - )} - - ) + + + + + )} + + ); } diff --git a/src/pages/History/AccordionWrapper.tsx b/src/pages/History/AccordionWrapper.tsx index 70ee3f6..6107306 100644 --- a/src/pages/History/AccordionWrapper.tsx +++ b/src/pages/History/AccordionWrapper.tsx @@ -1,21 +1,19 @@ import { - Box, - IconButton, - Typography, - useMediaQuery, - useTheme -} from "@mui/material" -import CustomAccordion from "@components/CustomAccordion" -import File from "@components/icons/File" -import {getDeclension} from "@utils/declension" -import {enqueueSnackbar} from "notistack" -import {addTariffToCart, useUserStore} from "@root/stores/user" -import ForwardToInboxOutlinedIcon - from "@mui/icons-material/ForwardToInboxOutlined"; -import {makeRequest} from "@frontend/kitui"; -import {KeyValue, RawDetails} from "@api/history"; -import {useNavigate} from "react-router-dom" -import {VerificationStatus} from "@root/model/account" + Box, + IconButton, + Typography, + useMediaQuery, + useTheme, +} from "@mui/material"; +import CustomAccordion from "@components/CustomAccordion"; +import File from "@components/icons/File"; +import { getDeclension } from "@utils/declension"; +import { enqueueSnackbar } from "notistack"; +import { addTariffToCart, useUserStore } from "@root/stores/user"; +import ForwardToInboxOutlinedIcon from "@mui/icons-material/ForwardToInboxOutlined"; +import { KeyValue, RawDetails, sendReport } from "@api/history"; +import { useNavigate } from "react-router-dom"; +import { VerificationStatus } from "@root/model/account"; export type History = { title: string; @@ -31,296 +29,322 @@ interface AccordionWrapperProps { last?: boolean; first?: boolean; createdAt: string; - onClickMail?: any - mainId: string + onClickMail?: any; + mainId: string; } -export default function AccordionWrapper({ content, last, first, createdAt, onClickMail, mainId }: AccordionWrapperProps) { - const theme = useTheme() - const upMd = useMediaQuery(theme.breakpoints.up("md")) - const upSm = useMediaQuery(theme.breakpoints.up("sm")) - const isTablet = useMediaQuery(theme.breakpoints.down(900)) - const isMobile = useMediaQuery(theme.breakpoints.down(560)) - const navigate = useNavigate(); - const verificationStatus = useUserStore((state) => state.verificationStatus) - const OrgName = useUserStore((state) => state.userAccount?.name.orgname) +export default function AccordionWrapper({ + content, + last, + first, + createdAt, + onClickMail, + mainId, +}: AccordionWrapperProps) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const isTablet = useMediaQuery(theme.breakpoints.down(900)); + const isMobile = useMediaQuery(theme.breakpoints.down(560)); + const navigate = useNavigate(); + const verificationStatus = useUserStore((state) => state.verificationStatus); + const OrgName = useUserStore((state) => state.userAccount?.name.orgname); - const valuesByKey: any = {} - if (Array.isArray(content[0].Value) && Array.isArray(content[0].Value[0])) { - (content[0].Value[0] as KeyValue[]).forEach((item: KeyValue) => { - valuesByKey[item.Key] = item.Value; - }); - } - const extractDateFromString = (tariffName: string) => { - const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/) - return dateMatch ? dateMatch[0] : null - } + const valuesByKey: any = {}; + if (Array.isArray(content[0].Value) && Array.isArray(content[0].Value[0])) { + (content[0].Value[0] as KeyValue[]).forEach((item: KeyValue) => { + valuesByKey[item.Key] = item.Value; + }); + } + const extractDateFromString = (tariffName: string) => { + const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/); + return dateMatch ? dateMatch[0] : null; + }; + async function handleTariffItemClick(tariffId: string) { + const { patchCartError } = await addTariffToCart(tariffId); + if (patchCartError) { + enqueueSnackbar(patchCartError); + } else { + enqueueSnackbar("Тариф добавлен в корзину"); + } + } - async function handleTariffItemClick(tariffId: string) { - const { patchCartError } = await addTariffToCart(tariffId) - if (patchCartError) { - enqueueSnackbar(patchCartError) - } else { - enqueueSnackbar("Тариф добавлен в корзину") - } - } + async function sendBillByEmail(logId: string) { + if (verificationStatus === VerificationStatus.VERIFICATED && OrgName) { + const [, sendReportError] = await sendReport(logId); - async function sendBillByEmail(logId: string) { + if (!sendReportError) { + return enqueueSnackbar( + "Акт будет отправлен на почту, указанную при регистрации" + ); + } - if(verificationStatus === VerificationStatus.VERIFICATED && OrgName){ - try { - await makeRequest({ - url: process.env.REACT_APP_DOMAIN + `/customer/sendReport`, - body: {id: logId}, - method: "POST", - }); - return enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); - } catch (e) { - enqueueSnackbar("Извините, произошла ошибка"); - } - } - navigate("/settings") - if(verificationStatus !== VerificationStatus.VERIFICATED && !OrgName){ - enqueueSnackbar("Пройдите верификацию и заполните название организации"); - } else if(!OrgName){ - enqueueSnackbar("Заполните поле название организации"); - }else if(verificationStatus !== VerificationStatus.VERIFICATED) { - enqueueSnackbar("Пройдите верификацию"); - } - } + enqueueSnackbar("Извините, произошла ошибка"); + } + navigate("/settings"); + if (verificationStatus !== VerificationStatus.VERIFICATED && !OrgName) { + enqueueSnackbar("Пройдите верификацию и заполните название организации"); + } else if (!OrgName) { + enqueueSnackbar("Заполните поле название организации"); + } else if (verificationStatus !== VerificationStatus.VERIFICATED) { + enqueueSnackbar("Пройдите верификацию"); + } + } - return ( - - ( - - {e[1].Value} - {e[5].Value} {getDeclension(Number(e[5].Value), e[7].Value.toString())} - ) - )} - header={ - <> - - - - {createdAt} - + return ( + + ( + + {e[1].Value} - {e[5].Value}{" "} + {getDeclension(Number(e[5].Value), e[7].Value.toString())} + + ))} + header={ + <> + + + + {createdAt} + - - {valuesByKey.iscustom ? "Мой тариф" : valuesByKey.name} - - - - - Способ оплаты: ${valuesByKey.payMethod}}`} - > - {valuesByKey.payMethod && Способ оплаты: {valuesByKey.payMethod}} - - - - {Number(content[1].Value) / 100 ? Number(content[1].Value) / 100 : "nodata"} руб. - - - - {!isMobile && - <> - { - e.stopPropagation(); - sendBillByEmail(mainId); - }} - sx={{ - ml: "20px", - bgcolor: "#EEE4FC", - color: "#7E2AEA", - borderRadius: 2, - "&:hover": { - bgcolor: "#7E2AEA", - color: "white", - }, - "&:active": { - bgcolor: "black", - color: "white", - } - }} - > - - - { - e.stopPropagation() - handleTariffItemClick(valuesByKey.id) - }} - sx={{ - ml: "20px", - bgcolor:"#EEE4FC", - stroke: "#7E2AEA", - borderRadius: 2, - "&:hover": { - bgcolor:"#7E2AEA", - stroke: "white", - }, - "&:active": { - bgcolor:"black", - stroke: "white", - } - }} - > - - - - - } - - - {isMobile && - <> - { - e.stopPropagation(); - sendBillByEmail(mainId); - }} - sx={{ - m: "0 10px", - bgcolor: "#EEE4FC", - color: "#7E2AEA", - borderRadius: 2, - "&:hover": { - bgcolor: "#7E2AEA", - color: "white", - }, - "&:active": { - bgcolor: "black", - color: "white", - } - }} - > - - - { - e.stopPropagation() - handleTariffItemClick(valuesByKey.id) - }} - sx={{ - mr: "10px", - bgcolor:"#EEE4FC", - stroke: "#7E2AEA", - borderRadius: 2, - "&:hover": { - bgcolor:"#7E2AEA", - stroke: "white", - }, - "&:active": { - bgcolor:"black", - stroke: "white", - } - }} - > - - - - - } - - } - /> - - ) + + {valuesByKey.iscustom ? "Мой тариф" : valuesByKey.name} + + + + + Способ оплаты: ${valuesByKey.payMethod}}`} + > + {valuesByKey.payMethod && ( + + Способ оплаты: {valuesByKey.payMethod} + + )} + + + + {Number(content[1].Value) / 100 + ? Number(content[1].Value) / 100 + : "nodata"}{" "} + руб. + + + + {!isMobile && ( + <> + { + e.stopPropagation(); + sendBillByEmail(mainId); + }} + sx={{ + ml: "20px", + bgcolor: "#EEE4FC", + color: "#7E2AEA", + borderRadius: 2, + "&:hover": { + bgcolor: "#7E2AEA", + color: "white", + }, + "&:active": { + bgcolor: "black", + color: "white", + }, + }} + > + + + { + e.stopPropagation(); + handleTariffItemClick(valuesByKey.id); + }} + sx={{ + ml: "20px", + bgcolor: "#EEE4FC", + stroke: "#7E2AEA", + borderRadius: 2, + "&:hover": { + bgcolor: "#7E2AEA", + stroke: "white", + }, + "&:active": { + bgcolor: "black", + stroke: "white", + }, + }} + > + + + + )} + + + {isMobile && ( + <> + { + e.stopPropagation(); + sendBillByEmail(mainId); + }} + sx={{ + m: "0 10px", + bgcolor: "#EEE4FC", + color: "#7E2AEA", + borderRadius: 2, + "&:hover": { + bgcolor: "#7E2AEA", + color: "white", + }, + "&:active": { + bgcolor: "black", + color: "white", + }, + }} + > + + + { + e.stopPropagation(); + handleTariffItemClick(valuesByKey.id); + }} + sx={{ + mr: "10px", + bgcolor: "#EEE4FC", + stroke: "#7E2AEA", + borderRadius: 2, + "&:hover": { + bgcolor: "#7E2AEA", + stroke: "white", + }, + "&:active": { + bgcolor: "black", + stroke: "white", + }, + }} + > + + + + )} + + } + /> + + ); } diff --git a/src/pages/History/AccordionWrapper2.tsx b/src/pages/History/AccordionWrapper2.tsx index 9b7ef72..632e290 100644 --- a/src/pages/History/AccordionWrapper2.tsx +++ b/src/pages/History/AccordionWrapper2.tsx @@ -1,160 +1,175 @@ import { - Box, - IconButton, - Typography, - useMediaQuery, - useTheme + Box, + IconButton, + Typography, + useMediaQuery, + useTheme, } from "@mui/material"; import CustomAccordion from "@components/CustomAccordion"; import File from "@components/icons/File"; -import {getDeclension} from "@utils/declension"; -import {enqueueSnackbar} from "notistack"; -import {addTariffToCart, useUserStore} from "@root/stores/user" -import {makeRequest, Tariff} from "@frontend/kitui"; -import {currencyFormatter} from "@root/utils/currencyFormatter"; -import ForwardToInboxIcon from '@mui/icons-material/ForwardToInbox'; -import {VerificationStatus} from "@root/model/account" -import {useNavigate} from "react-router-dom" +import { getDeclension } from "@utils/declension"; +import { enqueueSnackbar } from "notistack"; +import { addTariffToCart, useUserStore } from "@root/stores/user"; +import { makeRequest, Tariff } from "@frontend/kitui"; +import { currencyFormatter } from "@root/utils/currencyFormatter"; +import ForwardToInboxIcon from "@mui/icons-material/ForwardToInbox"; +import { VerificationStatus } from "@root/model/account"; +import { useNavigate } from "react-router-dom"; +import { sendReport } from "@api/history"; export type History = { - title: string; - date: string; - info: string; - description: string; - payMethod?: string; - expired?: boolean; + title: string; + date: string; + info: string; + description: string; + payMethod?: string; + expired?: boolean; }; interface AccordionWrapperProps { - tariff: Tariff; - price: number; - last?: boolean; - first?: boolean; - createdAt: string; - mainId: string + tariff: Tariff; + price: number; + last?: boolean; + first?: boolean; + createdAt: string; + mainId: string; } -export default function AccordionWrapper2({ tariff, price, last, first, createdAt, mainId }: AccordionWrapperProps) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const upSm = useMediaQuery(theme.breakpoints.up("sm")); - const isTablet = useMediaQuery(theme.breakpoints.down(900)); - const isMobile = useMediaQuery(theme.breakpoints.down(560)); - const navigate = useNavigate(); - const verificationStatus = useUserStore((state) => state.verificationStatus) - const OrgName = useUserStore((state) => state.userAccount?.name.orgname) - async function handleTariffItemClick(tariffId: string) { - const { patchCartError } = await addTariffToCart(tariffId); - if (patchCartError) { - enqueueSnackbar(patchCartError); - } else { - enqueueSnackbar("Тариф добавлен в корзину"); - } +export default function AccordionWrapper2({ + tariff, + price, + last, + first, + createdAt, + mainId, +}: AccordionWrapperProps) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const isTablet = useMediaQuery(theme.breakpoints.down(900)); + const isMobile = useMediaQuery(theme.breakpoints.down(560)); + const navigate = useNavigate(); + const verificationStatus = useUserStore((state) => state.verificationStatus); + const OrgName = useUserStore((state) => state.userAccount?.name.orgname); + async function handleTariffItemClick(tariffId: string) { + const { patchCartError } = await addTariffToCart(tariffId); + if (patchCartError) { + enqueueSnackbar(patchCartError); + } else { + enqueueSnackbar("Тариф добавлен в корзину"); } + } - async function sendBillByEmail(logId: string) { - if(verificationStatus === VerificationStatus.VERIFICATED && OrgName){ - try { - await makeRequest({ - url: process.env.REACT_APP_DOMAIN + `/customer/sendReport`, - body: {id: logId}, - method: "POST", - }); - return enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации"); - } catch (e) { - enqueueSnackbar("Извините, произошла ошибка"); - } - } - navigate("/settings") - if(verificationStatus !== VerificationStatus.VERIFICATED && !OrgName){ - enqueueSnackbar("Пройдите верификацию и заполните название организации"); - } else if(!OrgName){ - enqueueSnackbar("Заполните поле название организации"); - }else if(verificationStatus !== VerificationStatus.VERIFICATED) { - enqueueSnackbar("Пройдите верификацию"); - } + async function sendBillByEmail(logId: string) { + if (verificationStatus === VerificationStatus.VERIFICATED && OrgName) { + const [, sendReportError] = await sendReport(logId); + + if (!sendReportError) { + return enqueueSnackbar( + "Акт будет отправлен на почту, указанную при регистрации" + ); + } + + enqueueSnackbar("Извините, произошла ошибка"); } + navigate("/settings"); + if (verificationStatus !== VerificationStatus.VERIFICATED && !OrgName) { + enqueueSnackbar("Пройдите верификацию и заполните название организации"); + } else if (!OrgName) { + enqueueSnackbar("Заполните поле название организации"); + } else if (verificationStatus !== VerificationStatus.VERIFICATED) { + enqueueSnackbar("Пройдите верификацию"); + } + } - return ( - - ( - `${privilege.description} - ${privilege.amount} ${getDeclension(Number(privilege.serviceKey), privilege.value)} ` - ))} - header={ - <> - - - - {createdAt} - + return ( + + + `${privilege.description} - ${privilege.amount} ${getDeclension( + Number(privilege.serviceKey), + privilege.value + )} ` + )} + header={ + <> + + + + {createdAt} + - - {tariff.isCustom ? "Мой тариф" : tariff.name} - - - - - {/* + {tariff.isCustom ? "Мой тариф" : tariff.name} + + + + + {/* Способ оплаты: {valuesByKey.payMethod}} */} - - - {currencyFormatter.format(price)} - - - - {!isMobile && - <> - { - e.stopPropagation(); - sendBillByEmail(mainId); - }} - sx={{ - ml: "20px", - bgcolor: "#EEE4FC", - color: "#7E2AEA", - borderRadius: 2, - "&:hover": { - bgcolor: "#7E2AEA", - color: "white", - }, - "&:active": { - bgcolor: "black", - color: "white", - } - }} - > - - - { - e.stopPropagation(); - handleTariffItemClick(tariff._id); - }} - sx={{ - ml: "20px", - bgcolor: "#EEE4FC", - stroke: "#7E2AEA", - borderRadius: 2, - "&:hover": { - bgcolor: "#7E2AEA", - stroke: "white", - }, - "&:active": { - bgcolor: "black", - stroke: "white", - } - }} - > - - - - - } - - - {isMobile && - <> - { - e.stopPropagation(); - sendBillByEmail(mainId); - }} - sx={{ - m: "0 10px", - bgcolor: "#EEE4FC", - color: "#7E2AEA", - borderRadius: 2, - "&:hover": { - bgcolor: "#7E2AEA", - color: "white", - }, - "&:active": { - bgcolor: "black", - color: "white", - } - }} - > - - - { - e.stopPropagation(); - handleTariffItemClick(tariff._id); - }} - sx={{ - mr: "10px", - bgcolor: "#EEE4FC", - stroke: "#7E2AEA", - borderRadius: 2, - "&:hover": { - bgcolor: "#7E2AEA", - stroke: "white", - }, - "&:active": { - bgcolor: "black", - stroke: "white", - } - }} - > - - - - - } - - } - /> - - ); + + + {currencyFormatter.format(price)} + + + + {!isMobile && ( + <> + { + e.stopPropagation(); + sendBillByEmail(mainId); + }} + sx={{ + ml: "20px", + bgcolor: "#EEE4FC", + color: "#7E2AEA", + borderRadius: 2, + "&:hover": { + bgcolor: "#7E2AEA", + color: "white", + }, + "&:active": { + bgcolor: "black", + color: "white", + }, + }} + > + + + { + e.stopPropagation(); + handleTariffItemClick(tariff._id); + }} + sx={{ + ml: "20px", + bgcolor: "#EEE4FC", + stroke: "#7E2AEA", + borderRadius: 2, + "&:hover": { + bgcolor: "#7E2AEA", + stroke: "white", + }, + "&:active": { + bgcolor: "black", + stroke: "white", + }, + }} + > + + + + )} + + + {isMobile && ( + <> + { + e.stopPropagation(); + sendBillByEmail(mainId); + }} + sx={{ + m: "0 10px", + bgcolor: "#EEE4FC", + color: "#7E2AEA", + borderRadius: 2, + "&:hover": { + bgcolor: "#7E2AEA", + color: "white", + }, + "&:active": { + bgcolor: "black", + color: "white", + }, + }} + > + + + { + e.stopPropagation(); + handleTariffItemClick(tariff._id); + }} + sx={{ + mr: "10px", + bgcolor: "#EEE4FC", + stroke: "#7E2AEA", + borderRadius: 2, + "&:hover": { + bgcolor: "#7E2AEA", + stroke: "white", + }, + "&:active": { + bgcolor: "black", + stroke: "white", + }, + }} + > + + + + )} + + } + /> + + ); } diff --git a/src/pages/History/index.tsx b/src/pages/History/index.tsx index 3060f45..f699846 100644 --- a/src/pages/History/index.tsx +++ b/src/pages/History/index.tsx @@ -1,154 +1,168 @@ -import {useState} from "react"; +import { useState } from "react"; import { - Box, - IconButton, - Typography, - useMediaQuery, - useTheme + Box, + IconButton, + Typography, + useMediaQuery, + useTheme, } from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import SectionWrapper from "@root/components/SectionWrapper"; -import {Select} from "@root/components/Select"; -import {Tabs} from "@root/components/Tabs"; +import { Select } from "@root/components/Select"; +import { Tabs } from "@root/components/Tabs"; import AccordionWrapper from "./AccordionWrapper"; -import {useHistoryTracker} from "@root/utils/hooks/useHistoryTracker"; -import {ErrorBoundary} from "react-error-boundary"; -import {handleComponentError} from "@root/utils/handleComponentError"; -import {useHistoryStore} from "@root/stores/history"; -import {enqueueSnackbar} from "notistack"; -import {makeRequest} from "@frontend/kitui"; -import {HistoryRecord, HistoryRecord2} from "@root/api/history"; +import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker"; +import { ErrorBoundary } from "react-error-boundary"; +import { handleComponentError } from "@root/utils/handleComponentError"; +import { useHistoryStore } from "@root/stores/history"; +import { enqueueSnackbar } from "notistack"; +import { + HistoryRecord, + HistoryRecord2, + sendReportById, +} from "@root/api/history"; import AccordionWrapper2 from "./AccordionWrapper2"; const subPages = ["Платежи"]; // const subPages = ["Платежи", "Покупки тарифов", "Окончания тарифов"] export default function History() { - const [selectedItem, setSelectedItem] = useState(0); + const [selectedItem, setSelectedItem] = useState(0); - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const isMobile = useMediaQuery(theme.breakpoints.down(600)); - const isTablet = useMediaQuery(theme.breakpoints.down(1000)); - const historyData = useHistoryStore(state => state.history); - const handleCustomBackNavigation = useHistoryTracker(); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const isMobile = useMediaQuery(theme.breakpoints.down(600)); + const isTablet = useMediaQuery(theme.breakpoints.down(1000)); + const historyData = useHistoryStore((state) => state.history); + const handleCustomBackNavigation = useHistoryTracker(); - const extractDateFromString = (tariffName: string) => { - const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/); - return dateMatch ? dateMatch[0] : ""; - }; + const extractDateFromString = (tariffName: string) => { + const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/); + return dateMatch ? dateMatch[0] : ""; + }; - async function handleHistoryResponse(tariffId: string) { - try { - await makeRequest( - { - url: process.env.REACT_APP_DOMAIN + `/customer/sendReport/${tariffId}`, - method: "POST", - } - ); - enqueueSnackbar("Запрос отправлен"); - } catch (e) { - enqueueSnackbar("извините, произошла ошибка"); - } + async function handleHistoryResponse(tariffId: string) { + const [, sendReportError] = await sendReportById(tariffId); + + if (sendReportError) { + return enqueueSnackbar(sendReportError); } - return ( - + + {isMobile && ( + + + + )} + - - {isMobile && ( - - - - )} - - История - - - {isMobile ? ( - + ) : ( + + )} + Ошибка загрузки истории} + onError={handleComponentError} + > + {historyData?.length === 0 && ( + Нет данных + )} + {/* Для ненормального rawDetails */} + {historyData + ?.filter((e): e is HistoryRecord => { + e.createdAt = extractDateFromString(e.createdAt); + return ( + !e.isDeleted && + e.key === "payCart" && + Array.isArray(e.rawDetails) && + Array.isArray(e.rawDetails[0].Value) + ); + }) + .map((e, index) => { + return ( + + { + event.stopPropagation(); + handleHistoryResponse(e.id); + }} + /> + + ); + })} + {/* Для нормального rawDetails */} + {historyData + ?.filter((e): e is HistoryRecord2 => { + e.createdAt = extractDateFromString(e.createdAt); + return ( + !e.isDeleted && + e.key === "payCart" && + !Array.isArray(e.rawDetails) && + !!e.rawDetails.tariffs[0] + ); + }) + .map((e, index) => { + return ( + + + + ); + })} + + + ); } diff --git a/src/pages/QuizPayment/QuizPayment.tsx b/src/pages/QuizPayment/QuizPayment.tsx index 1fd55ce..db853eb 100644 --- a/src/pages/QuizPayment/QuizPayment.tsx +++ b/src/pages/QuizPayment/QuizPayment.tsx @@ -1,5 +1,4 @@ import { useEffect, useState } from "react"; -import axios, { AxiosResponse } from "axios"; import { ApologyPage } from "../ApologyPage"; import { useNavigate } from "react-router-dom"; import { @@ -22,19 +21,6 @@ import { clearCustomTariffs } from "@root/stores/customTariffs"; import { clearTickets } from "@root/stores/tickets"; import {setNotEnoughMoneyAmount} from "@stores/cart" -function refresh(token: string) { - return axios>( - process.env.REACT_APP_DOMAIN + "/auth/refresh", - { - headers: { - Authorization: "Bearer " + token, - "Content-Type": "application/json", - }, - method: "POST", - } - ); -} - const params = new URLSearchParams(window.location.search); const action = params.get("action"); const dif = params.get("dif"); diff --git a/src/pages/Support/SupportChat.tsx b/src/pages/Support/SupportChat.tsx index 6e60d83..cee479e 100644 --- a/src/pages/Support/SupportChat.tsx +++ b/src/pages/Support/SupportChat.tsx @@ -14,7 +14,6 @@ import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useParams } from "react-router-dom"; import SendIcon from "@components/icons/SendIcon"; -import makeRequest from "@api/makeRequest" import { throttle, useToken } from "@frontend/kitui"; import { enqueueSnackbar } from "notistack"; import { useTicketStore } from "@root/stores/tickets"; @@ -35,7 +34,11 @@ import { useSSESubscription, useTicketMessages, } from "@frontend/kitui"; -import { shownMessage, sendTicketMessage } from "@root/api/ticket"; +import { + shownMessage, + sendTicketMessage, + sendFile as sendFileRequest, +} from "@root/api/ticket"; import { withErrorBoundary } from "react-error-boundary"; import { handleComponentError } from "@root/utils/handleComponentError"; import { useSSETab } from "@root/utils/hooks/useSSETab"; @@ -196,20 +199,12 @@ function SupportChat() { return; } - try { - const body = new FormData(); + const [, sendFileError] = await sendFileRequest(ticketId, file); - body.append(file.name, file); - body.append("ticket", ticketId); - await makeRequest({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/sendFiles", - body: body, - method: "POST", - }); - } catch (error: any) { - const errorMessage = getMessageFromFetchError(error); - if (errorMessage) enqueueSnackbar(errorMessage); + if (sendFileError) { + enqueueSnackbar(sendFileError); } + return true; } }; diff --git a/src/pages/auth/Recover.tsx b/src/pages/auth/Recover.tsx index cb1c8df..b2b0c58 100644 --- a/src/pages/auth/Recover.tsx +++ b/src/pages/auth/Recover.tsx @@ -21,7 +21,6 @@ import { setUserId, useUserStore } from "@root/stores/user"; import { cardShadow } from "@root/utils/theme"; import AmoButton from "./AmoButton"; import { recover } from "@root/api/auth"; -import {AxiosError} from "axios" interface Values { email: string; diff --git a/src/pages/auth/RecoverPassword.tsx b/src/pages/auth/RecoverPassword.tsx index 6d39afd..77a261b 100644 --- a/src/pages/auth/RecoverPassword.tsx +++ b/src/pages/auth/RecoverPassword.tsx @@ -1,12 +1,12 @@ import { - Box, - Dialog, - IconButton, - Link, - Typography, - useMediaQuery, - useTheme, - Button, + Box, + Dialog, + IconButton, + Link, + Typography, + useMediaQuery, + useTheme, + Button, } from "@mui/material"; import CloseIcon from "@mui/icons-material/Close"; import { useLocation, useNavigate } from "react-router-dom"; @@ -19,175 +19,180 @@ import { useEffect, useState } from "react"; import { useUserStore } from "@root/stores/user"; import { cardShadow } from "@root/utils/theme"; -import makeRequest from "@api/makeRequest" -import { setAuthToken } from "@frontend/kitui" +import { patchUser } from "@api/user"; +import { setAuthToken } from "@frontend/kitui"; interface Values { - password: string; + password: string; } const initialValues: Values = { - password: "", + password: "", }; const validationSchema = object({ - password: string() - .min(8, "Минимум 8 символов") - .matches(/^[.,:;\-_+!&()*<>\[\]\{\}`@"#$\%\^\=?\d\w]+$/, "Некорректные символы") - .required("Поле обязательно"), + password: string() + .min(8, "Минимум 8 символов") + .matches( + /^[.,:;\-_+!&()*<>\[\]\{\}`@"#$\%\^\=?\d\w]+$/, + "Некорректные символы" + ) + .required("Поле обязательно"), }); export default function RecoverPassword() { - const [isDialogOpen, setIsDialogOpen] = useState(true); - const [tokenUser, setTokenUser] = useState(""); - const user = useUserStore((state) => state.user); - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const navigate = useNavigate(); - const location = useLocation(); - const formik = useFormik({ - initialValues, - validationSchema, - onSubmit: async (values, formikHelpers) => { - if (tokenUser) { - setAuthToken(tokenUser || "") - try { - const response = await makeRequest({ - url: process.env.REACT_APP_DOMAIN + "/user/", - method: "PATCH", - body: {password: values.password}, - }); - setIsDialogOpen(false) - navigate("/") - enqueueSnackbar("Пароль успешно сменён") - } catch (error) { - setAuthToken("") - enqueueSnackbar("Извините, произошла ошибка, попробуйте повторить позже")} + const [isDialogOpen, setIsDialogOpen] = useState(true); + const [tokenUser, setTokenUser] = useState(""); + const user = useUserStore((state) => state.user); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const navigate = useNavigate(); + const location = useLocation(); + const formik = useFormik({ + initialValues, + validationSchema, + onSubmit: async (values, formikHelpers) => { + if (tokenUser) { + setAuthToken(tokenUser || ""); + const [, patchUserError] = await patchUser({ + password: values.password, + }); - } else { - enqueueSnackbar("Неверный url-адрес") - } - }, - }); - useEffect(() => { - const params = new URLSearchParams(window.location.search) - const authToken = params.get("auth") - setTokenUser(authToken) + if (!patchUserError) { + setIsDialogOpen(false); + navigate("/"); + enqueueSnackbar("Пароль успешно сменён"); + } else { + setAuthToken(""); + enqueueSnackbar( + "Извините, произошла ошибка, попробуйте повторить позже" + ); + } + } else { + enqueueSnackbar("Неверный url-адрес"); + } + }, + }); + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const authToken = params.get("auth"); + setTokenUser(authToken); - history.pushState(null, document.title, "/changepwd"); - return () => {setAuthToken("")} - }, []); + history.pushState(null, document.title, "/changepwd"); + return () => { + setAuthToken(""); + }; + }, []); - function handleClose() { - setIsDialogOpen(false); - setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen); - } + function handleClose() { + setIsDialogOpen(false); + setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen); + } - return ( - - - - - - - - - - Введите новый пароль - - - - - - - - ); + return ( + + + + + + + + + + Введите новый пароль + + + + + + + ); } diff --git a/src/utils/handleComponentError.ts b/src/utils/handleComponentError.ts index 301cc42..fda2c59 100644 --- a/src/utils/handleComponentError.ts +++ b/src/utils/handleComponentError.ts @@ -1,42 +1,35 @@ -import { ErrorInfo } from "react" - +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 +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, - // }); - errorsQueue = [] + errorsQueue = []; } From f58f2c0c4757133b13841be8007bcf2bd8a9489c Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Tue, 28 May 2024 16:38:01 +0300 Subject: [PATCH 09/27] refactor: api methods --- src/api/auth.ts | 30 +- src/api/cart.ts | 34 +- src/api/history.ts | 21 +- src/api/price.ts | 12 +- src/api/promocode.ts | 14 +- src/api/recentlyPurchasedTariffs.ts | 20 +- src/api/tariff.ts | 54 +-- src/api/ticket.ts | 18 +- src/api/user.ts | 12 +- src/api/verification.ts | 32 +- src/api/wallet.ts | 52 +-- src/components/FloatingSupportChat/Chat.tsx | 14 +- src/pages/History/index.tsx | 1 + src/pages/Payment/Payment.tsx | 19 +- src/pages/Tariffs/TariffsPage.tsx | 404 +++++++++++--------- src/pages/auth/Recover.tsx | 12 +- 16 files changed, 397 insertions(+), 352 deletions(-) diff --git a/src/api/auth.ts b/src/api/auth.ts index 7cc5010..aaa65d8 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -9,13 +9,17 @@ import type { RegisterResponse, } from "@frontend/kitui"; +type RecoverResponse = { + message: string; +}; + const API_URL = `${process.env.REACT_APP_DOMAIN}/auth`; -export async function register( +export const register = async ( login: string, password: string, phoneNumber: string -): Promise<[RegisterResponse | null, string?]> { +): Promise<[RegisterResponse | null, string?]> => { try { const registerResponse = await makeRequest< RegisterRequest, @@ -33,12 +37,12 @@ export async function register( return [null, `Не удалось зарегестрировать аккаунт. ${error}`]; } -} +}; -export async function login( +export const login = async ( login: string, password: string -): Promise<[LoginResponse | null, string?]> { +): Promise<[LoginResponse | null, string?]> => { try { const loginResponse = await makeRequest({ url: `${API_URL}/login`, @@ -53,11 +57,11 @@ export async function login( return [null, `Не удалось войти. ${error}`]; } -} +}; -export async function recover( +export const recover = async ( email: string -): Promise<[unknown | null, string?]> { +): Promise<[RecoverResponse | null, string?]> => { try { const formData = new FormData(); @@ -67,7 +71,7 @@ export async function recover( `${process.env.REACT_APP_DOMAIN}/changepwd` ); - const recoverResponse = await makeRequest({ + const recoverResponse = await makeRequest({ url: `${process.env.REACT_APP_DOMAIN}/codeword/recover`, body: formData, useToken: false, @@ -80,13 +84,13 @@ export async function recover( return [null, `Не удалось восстановить пароль. ${error}`]; } -} +}; -export async function logout(): Promise<[unknown, string?]> { +export const logout = async (): Promise<[void | null, string?]> => { try { const logoutResponse = await makeRequest({ - url: `${API_URL}/logout`, method: "POST", + url: `${API_URL}/logout`, useToken: true, withCredentials: true, }); @@ -97,4 +101,4 @@ export async function logout(): Promise<[unknown, string?]> { return [null, `Не удалось выйти. ${error}`]; } -} +}; diff --git a/src/api/cart.ts b/src/api/cart.ts index c4c3dcb..30e2490 100644 --- a/src/api/cart.ts +++ b/src/api/cart.ts @@ -5,13 +5,13 @@ import { parseAxiosError } from "@root/utils/parse-error"; const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; -export async function patchCart( +export const patchCart = async ( tariffId: string -): Promise<[string[], string?]> { +): Promise<[string[], string?]> => { try { const patchCartResponse = await makeRequest({ - url: `${API_URL}/cart?id=${tariffId}`, method: "PATCH", + url: `${API_URL}/cart?id=${tariffId}`, useToken: true, }); @@ -23,15 +23,15 @@ export async function patchCart( return [[], `Не удалось добавить товар в корзину. ${error}`]; } -} +}; -export async function deleteCart( +export const deleteCart = async ( tariffId: string -): Promise<[string[], string?]> { +): Promise<[string[], string?]> => { try { const deleteCartResponse = await makeRequest({ - url: `${API_URL}/cart?id=${tariffId}`, method: "DELETE", + url: `${API_URL}/cart?id=${tariffId}`, useToken: true, }); @@ -41,13 +41,13 @@ export async function deleteCart( return [[], `Не удалось удалить товар из корзины. ${error}`]; } -} +}; -export async function payCart(): Promise<[UserAccount | null, string?]> { +export const payCart = async (): Promise<[UserAccount | null, string?]> => { try { const payCartResponse = await makeRequest({ - url: `${API_URL}/cart/pay`, method: "POST", + url: `${API_URL}/cart/pay`, useToken: true, }); @@ -57,22 +57,20 @@ export async function payCart(): Promise<[UserAccount | null, string?]> { return [null, `Не удалось оплатить товар из корзины. ${error}`]; } -} +}; -export async function patchCurrency( +export const patchCurrency = async ( currency: string -): Promise<[UserAccount | null, string?]> { +): Promise<[UserAccount | null, string?]> => { try { const patchCurrencyResponse = await makeRequest< { currency: string }, UserAccount >({ - url: `${API_URL}/wallet`, method: "PATCH", + url: `${API_URL}/wallet`, useToken: true, - body: { - currency, - }, + body: { currency }, }); return [patchCurrencyResponse]; @@ -81,4 +79,4 @@ export async function patchCurrency( return [null, `Не удалось изменить валюту. ${error}`]; } -} +}; diff --git a/src/api/history.ts b/src/api/history.ts index 42f5fff..ac5c06c 100644 --- a/src/api/history.ts +++ b/src/api/history.ts @@ -46,16 +46,16 @@ export type RawDetails = { const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; -export async function getHistory(): Promise< +export const getHistory = async (): Promise< [GetHistoryResponse | GetHistoryResponse2 | null, string?] -> { +> => { try { const historyResponse = await makeRequest< never, GetHistoryResponse | GetHistoryResponse2 >({ - url: `${API_URL}/history?page=1&limit=100&type=payCart`, method: "GET", + url: `${API_URL}/history?page=1&limit=100&type=payCart`, useToken: true, }); @@ -64,14 +64,6 @@ export async function getHistory(): Promise< } const checked = historyResponse.records.map((data) => { - //const buffer:RawDetails[] = []; - /*(data.rawDetails as HistoryRecord["rawDetails"]).forEach((slot) => { - const index = regList[slot.Key] - buffer[index] = { ...slot } - })*/ - //Чистим дыры с помощью .filter(() => true) - //@ts-ignore - //data.rawDetails = buffer const checkedRowDetails = [ (data.rawDetails as HistoryRecord["rawDetails"]).find( (details) => details.Key === "tariffs" @@ -80,6 +72,7 @@ export async function getHistory(): Promise< (details) => details.Key === "price" ) as KeyValue, ]; + return { ...data, rawDetails: checkedRowDetails } as HistoryRecord; }); @@ -90,15 +83,15 @@ export async function getHistory(): Promise< return [null, `Не удалось получить историю. ${error}`]; } -} +}; export const sendReport = async ( id: string ): Promise<[unknown | null, string?]> => { try { const sendReportResponse = await makeRequest<{ id: string }, unknown>({ - url: `${API_URL}/sendReport`, method: "POST", + url: `${API_URL}/sendReport`, body: { id }, }); @@ -115,8 +108,8 @@ export const sendReportById = async ( ): Promise<[unknown | null, string?]> => { try { const sendReportResponse = await makeRequest({ - url: `${API_URL}/sendReport/${tariffId}`, method: "POST", + url: `${API_URL}/sendReport/${tariffId}`, }); return [sendReportResponse]; diff --git a/src/api/price.ts b/src/api/price.ts index fe8c8dc..a411f78 100644 --- a/src/api/price.ts +++ b/src/api/price.ts @@ -1,4 +1,4 @@ -import makeRequest from "@api/makeRequest" +import makeRequest from "@api/makeRequest"; import type { GetDiscountsResponse } from "@root/model/discount"; import { useUserStore } from "@root/stores/user"; import { parseAxiosError } from "@root/utils/parse-error"; @@ -7,15 +7,15 @@ import useSWR from "swr"; const API_URL = `${process.env.REACT_APP_DOMAIN}/price`; -export async function getDiscounts(userId: string | null) { +export const getDiscounts = async (userId: string | null) => { if (userId === null) { return; } try { const discountsResponse = await makeRequest({ + method: "GET", url: `${API_URL}/discount/user/${userId}`, - method: "get", useToken: true, }); @@ -25,9 +25,9 @@ export async function getDiscounts(userId: string | null) { throw new Error(`Ошибка получения списка скидок. ${error}`); } -} +}; -export function useDiscounts(userId: string | null) { +export const useDiscounts = (userId: string | null) => { const { data } = useSWR("discounts", () => getDiscounts(userId), { keepPreviousData: true, onError: (error) => { @@ -38,4 +38,4 @@ export function useDiscounts(userId: string | null) { }); return data; -} +}; diff --git a/src/api/promocode.ts b/src/api/promocode.ts index 3234f47..eab67f2 100644 --- a/src/api/promocode.ts +++ b/src/api/promocode.ts @@ -4,22 +4,24 @@ import { parseAxiosError } from "@utils/parse-error"; const API_URL = `${process.env.REACT_APP_DOMAIN}/codeword/promocode`; -export async function activatePromocode(promocode: string) { +export const activatePromocode = async ( + promocode: string +): Promise<[string | null, string?]> => { try { const response = await makeRequest< { codeword: string } | { fastLink: string }, { greetings: string } >({ - url: `${API_URL}/activate`, method: "POST", - contentType: true, + url: `${API_URL}/activate`, body: { codeword: promocode }, + contentType: true, }); - return response.greetings; + return [response.greetings]; } catch (nativeError) { const [error] = parseAxiosError(nativeError); - throw new Error(error); + return [null, error]; } -} +}; diff --git a/src/api/recentlyPurchasedTariffs.ts b/src/api/recentlyPurchasedTariffs.ts index e9b4758..8e86cf9 100644 --- a/src/api/recentlyPurchasedTariffs.ts +++ b/src/api/recentlyPurchasedTariffs.ts @@ -3,13 +3,21 @@ import { parseAxiosError } from "@root/utils/parse-error"; const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; -export async function getRecentlyPurchasedTariffs(): Promise< - [any | null, string?] -> { +type GetRecentlyPurchasedTariffsResponse = { + id: string; +}; + +export const getRecentlyPurchasedTariffs = async (): Promise< + [GetRecentlyPurchasedTariffsResponse | null, string?] +> => { try { - const recentlyPurchased = await makeRequest({ - url: `${API_URL}/recent`, + debugger; + const recentlyPurchased = await makeRequest< + never, + GetRecentlyPurchasedTariffsResponse + >({ method: "GET", + url: `${API_URL}/recent`, useToken: true, }); @@ -19,4 +27,4 @@ export async function getRecentlyPurchasedTariffs(): Promise< return [null, `Не удалось получить историю. ${error}`]; } -} +}; diff --git a/src/api/tariff.ts b/src/api/tariff.ts index 218b236..a4f9e3e 100644 --- a/src/api/tariff.ts +++ b/src/api/tariff.ts @@ -9,17 +9,24 @@ import type { import type { GetTariffsResponse } from "@root/model/tariff"; import { removeTariffFromCart } from "@root/stores/user"; +interface CreateTariffBody { + name: string; + price?: number; + isCustom: boolean; + privileges: PrivilegeWithoutPrice[]; +} + const API_URL = `${process.env.REACT_APP_DOMAIN}/strator`; -export async function getTariffs( +export const getTariffs = async ( apiPage: number, tariffsPerPage: number, signal: AbortSignal | undefined -): Promise<[GetTariffsResponse | null, string?]> { +): Promise<[GetTariffsResponse | null, string?]> => { try { const tariffsResponse = await makeRequest({ + method: "GET", url: `${API_URL}/tariff?page=${apiPage}&limit=${tariffsPerPage}`, - method: "get", useToken: true, signal, }); @@ -30,24 +37,17 @@ export async function getTariffs( return [null, `Не удалось получить список тарифов. ${error}`]; } -} +}; -interface CreateTariffBody { - name: string; - price?: number; - isCustom: boolean; - privileges: PrivilegeWithoutPrice[]; -} - -export async function createTariff( +export const createTariff = async ( tariff: CreateTariffBody -): Promise<[Tariff | null, string?]> { +): Promise<[Tariff | null, string?]> => { try { const createTariffResponse = await makeRequest({ + method: "POST", url: `${API_URL}/tariff`, - method: "post", - useToken: true, body: tariff, + useToken: true, }); return [createTariffResponse]; @@ -56,15 +56,15 @@ export async function createTariff( return [null, `Не удалось создать тариф. ${error}`]; } -} +}; -export async function getTariffById( +export const getTariffById = async ( tariffId: string -): Promise<[Tariff | null, string?, number?]> { +): Promise<[Tariff | null, string?, number?]> => { try { const getTariffByIdResponse = await makeRequest({ + method: "GET", url: `${API_URL}/tariff/${tariffId}`, - method: "get", useToken: true, }); @@ -74,19 +74,19 @@ export async function getTariffById( return [null, `Не удалось получить тарифы. ${error}`, status]; } -} +}; -export async function getCustomTariffs( +export const getCustomTariffs = async ( signal: AbortSignal | undefined -): Promise<[ServiceKeyToPrivilegesMap | null, string?]> { +): Promise<[ServiceKeyToPrivilegesMap | null, string?]> => { try { const customTariffsResponse = await makeRequest< null, ServiceKeyToPrivilegesMap >({ + method: "GET", url: `${API_URL}/privilege/service`, signal, - method: "get", useToken: true, }); @@ -101,16 +101,16 @@ export async function getCustomTariffs( return [null, `Не удалось получить мои тарифы. ${error}`]; } -} +}; -export async function getTariffArray(tariffIds: string[] | undefined) { +export const getTariffArray = async (tariffIds: string[] | undefined) => { if (!tariffIds) return null; const responses = await Promise.allSettled( tariffIds.map((tariffId) => makeRequest({ + method: "GET", url: `${API_URL}/tariff/${tariffId}`, - method: "get", useToken: true, }) ) @@ -133,4 +133,4 @@ export async function getTariffArray(tariffIds: string[] | undefined) { }); return tariffs; -} +}; diff --git a/src/api/ticket.ts b/src/api/ticket.ts index 85059b1..11c39cb 100644 --- a/src/api/ticket.ts +++ b/src/api/ticket.ts @@ -9,19 +9,19 @@ type SendFileResponse = { const API_URL = `${process.env.REACT_APP_DOMAIN}/heruvym`; -export async function sendTicketMessage( +export const sendTicketMessage = async ( ticketId: string, message: string -): Promise<[null, string?]> { +): Promise<[null, string?]> => { try { const sendTicketMessageResponse = await makeRequest< SendTicketMessageRequest, null >({ - url: `${API_URL}/send`, method: "POST", - useToken: true, + url: `${API_URL}/send`, body: { ticket: ticketId, message: message, lang: "ru", files: [] }, + useToken: true, }); return [sendTicketMessageResponse]; @@ -30,15 +30,15 @@ export async function sendTicketMessage( return [null, `Не удалось отправить сообщение. ${error}`]; } -} +}; -export async function shownMessage(id: string): Promise<[null, string?]> { +export const shownMessage = async (id: string): Promise<[null, string?]> => { try { const shownMessageResponse = await makeRequest<{ id: string }, null>({ - url: `${API_URL}/shown`, method: "POST", - useToken: true, + url: `${API_URL}/shown`, body: { id }, + useToken: true, }); return [shownMessageResponse]; @@ -47,7 +47,7 @@ export async function shownMessage(id: string): Promise<[null, string?]> { return [null, `Не удалось прочесть сообщение. ${error}`]; } -} +}; export const sendFile = async ( ticketId: string, diff --git a/src/api/user.ts b/src/api/user.ts index 4ee472f..a3a62db 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -5,17 +5,17 @@ import { parseAxiosError } from "@root/utils/parse-error"; const API_URL = `${process.env.REACT_APP_DOMAIN}/user`; -export async function patchUser( +export const patchUser = async ( user: PatchUserRequest -): Promise<[User | null, string?]> { +): Promise<[User | null, string?]> => { try { const patchUserResponse = await makeRequest({ - url: `${API_URL}/`, - contentType: true, method: "PATCH", + url: `${API_URL}/`, + body: user, + contentType: true, useToken: true, withCredentials: false, - body: user, }); return [patchUserResponse]; @@ -24,4 +24,4 @@ export async function patchUser( return [null, `Не удалось изменить пользователя. ${error}`]; } -} +}; diff --git a/src/api/verification.ts b/src/api/verification.ts index 2eebc5a..b473884 100644 --- a/src/api/verification.ts +++ b/src/api/verification.ts @@ -11,13 +11,13 @@ import type { const API_URL = `${process.env.REACT_APP_DOMAIN}/verification/v1.0.0/verification`; -export async function verification( +export const verification = async ( userId: string -): Promise<[Verification | null, string?]> { +): Promise<[Verification | null, string?]> => { try { const verificationResponse = await makeRequest({ - url: `${API_URL}/${userId}`, method: "GET", + url: `${API_URL}/${userId}`, useToken: true, withCredentials: true, }); @@ -45,18 +45,18 @@ export async function verification( return [null, `Ошибка запроса верификации. ${error}`]; } -} +}; -export async function sendDocuments( +export const sendDocuments = async ( documents: SendDocumentsArgs -): Promise<[Verification | "OK" | null, string?]> { +): Promise<[Verification | "OK" | null, string?]> => { try { const sendDocumentsResponse = await makeRequest({ - url: API_URL, method: "POST", + url: API_URL, + body: jsonToFormdata({ ...documents, egrule: documents.inn }), useToken: true, withCredentials: true, - body: jsonToFormdata({ ...documents, egrule: documents.inn }), }); return [sendDocumentsResponse]; @@ -65,20 +65,20 @@ export async function sendDocuments( return [null, `Ошибка отправки документов. ${error}`]; } -} +}; -export async function updateDocuments( +export const updateDocuments = async ( documents: UpdateDocumentsArgs -): Promise<[Verification | "OK" | null, string?]> { +): Promise<[Verification | "OK" | null, string?]> => { try { const updateDocumentsResponse = await makeRequest({ - url: `${API_URL}/file`, method: "PATCH", - useToken: true, - withCredentials: true, + url: `${API_URL}/file`, body: jsonToFormdata( documents.inn ? { ...documents, egrule: documents.inn } : documents ), + useToken: true, + withCredentials: true, }); return [updateDocumentsResponse]; @@ -87,15 +87,15 @@ export async function updateDocuments( return [null, `Ошибка обновления документов. ${error}`]; } -} +}; export const updateDocument = async ( body: FormData ): Promise<[Verification | "OK" | null, string?]> => { try { const updateDocumentResponse = await makeRequest({ - url: API_URL, method: "PATCH", + url: API_URL, body, useToken: true, withCredentials: true, diff --git a/src/api/wallet.ts b/src/api/wallet.ts index e744367..e5a934d 100644 --- a/src/api/wallet.ts +++ b/src/api/wallet.ts @@ -1,4 +1,4 @@ -import makeRequest from "@api/makeRequest" +import makeRequest from "@api/makeRequest"; import { SendPaymentRequest, SendPaymentResponse } from "@root/model/wallet"; import { parseAxiosError } from "@root/utils/parse-error"; @@ -7,14 +7,14 @@ const isStaging = (() => { return host.includes("s") ? "s" : ""; })(); -const apiUrl = process.env.REACT_APP_DOMAIN + "/customer"; +const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; interface PaymentBody { type: string; amount: number; } -export async function sendPayment({ +export const sendPayment = async ({ userId, body, fromSquiz, @@ -24,33 +24,35 @@ export async function sendPayment({ body: PaymentBody; fromSquiz: boolean; paymentPurpose: "paycart" | "replenishwallet"; -}): Promise<[SendPaymentResponse | null, string?]> { +}): Promise<[SendPaymentResponse | null, string?]> => { + const reqeustBody = { + currency: "RUB", + bankCard: { + number: "RUB", + expiryYear: "2021", + expiryMonth: "05", + csc: "05", + cardholder: "IVAN IVANOV", + }, + phoneNumber: "79000000000", + login: "login_test", + returnUrl: `https://${isStaging}hub.pena.digital/afterpay?from=${ + fromSquiz ? "quiz" : "hub" + }&purpose=${paymentPurpose}&userid=${userId}`, + ...body, + }; + try { const sendPaymentResponse = await makeRequest< SendPaymentRequest, SendPaymentResponse >({ - url: apiUrl + "/wallet", - contentType: true, method: "POST", + url: `${API_URL}/wallet`, + body: reqeustBody, + contentType: true, useToken: true, withCredentials: false, - body: { - currency: "RUB", - bankCard: { - number: "RUB", - expiryYear: "2021", - expiryMonth: "05", - csc: "05", - cardholder: "IVAN IVANOV", - }, - phoneNumber: "79000000000", - login: "login_test", - returnUrl: `https://${isStaging}hub.pena.digital/afterpay?from=${ - fromSquiz ? "quiz" : "hub" - }&purpose=${paymentPurpose}&userid=${userId}`, - ...body, - }, }); return [sendPaymentResponse]; @@ -59,15 +61,15 @@ export async function sendPayment({ return [null, `Ошибка оплаты. ${error}`]; } -} +}; export const sendRSPayment = async (money: number): Promise => { try { await makeRequest({ - url: apiUrl + "/wallet/rspay", method: "POST", - useToken: true, + url: `${API_URL}/wallet/rspay`, body: { money: money }, + useToken: true, withCredentials: false, }); diff --git a/src/components/FloatingSupportChat/Chat.tsx b/src/components/FloatingSupportChat/Chat.tsx index 8f83e99..abd2bbb 100644 --- a/src/components/FloatingSupportChat/Chat.tsx +++ b/src/components/FloatingSupportChat/Chat.tsx @@ -79,7 +79,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { const upMd = useMediaQuery(theme.breakpoints.up("md")); const isMobile = useMediaQuery(theme.breakpoints.down(800)); const [messageField, setMessageField] = useState(""); - const [disableFileButton, setDisableFileButton] = useState(false); + const [disableFileButton, setDisableFileButton] = useState(false); const [sseEnabled, setSseEnabled] = useState(true); const [modalWarningType, setModalWarningType] = useState(null); @@ -356,15 +356,12 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { if (ticketId !== undefined) { if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize"); - const [, sendFileError] = await sendFileRequest( - ticketId, - file - ); + const [, sendFileError] = await sendFileRequest(ticketId, file); - if(sendFileError) { - enqueueSnackbar(sendFileError) - } + if (sendFileError) { + enqueueSnackbar(sendFileError); } + return true; } }; @@ -375,6 +372,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { enqueueSnackbar(check); return; } + setDisableFileButton(true); await sendFile(file); setDisableFileButton(false); diff --git a/src/pages/History/index.tsx b/src/pages/History/index.tsx index f699846..b763dc0 100644 --- a/src/pages/History/index.tsx +++ b/src/pages/History/index.tsx @@ -52,6 +52,7 @@ export default function History() { enqueueSnackbar("Запрос отправлен"); } + return ( { - enqueueSnackbar(response); - mutate("discounts"); - }) - .catch((error) => { - if (error.message !== "" && typeof error.message === "string") - enqueueSnackbar(error.message); - }); + const [greeting, activateError] = await activatePromocode(promocodeField); + + if (activateError) { + return enqueueSnackbar(activateError); + } + + enqueueSnackbar(greeting); + mutate("discounts"); } return ( diff --git a/src/pages/Tariffs/TariffsPage.tsx b/src/pages/Tariffs/TariffsPage.tsx index a653e46..6ce564b 100644 --- a/src/pages/Tariffs/TariffsPage.tsx +++ b/src/pages/Tariffs/TariffsPage.tsx @@ -1,7 +1,17 @@ import SectionWrapper from "@components/SectionWrapper"; -import { Tariff, calcTariffPrice, getMessageFromFetchError } from "@frontend/kitui"; +import { + Tariff, + calcTariffPrice, + getMessageFromFetchError, +} from "@frontend/kitui"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; -import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { + Box, + IconButton, + Typography, + useMediaQuery, + useTheme, +} from "@mui/material"; import NumberIcon from "@root/components/NumberIcon"; import { useTariffStore } from "@root/stores/tariffs"; import { addTariffToCart, useUserStore } from "@root/stores/user"; @@ -22,198 +32,230 @@ import { Tabs } from "@components/Tabs"; const subPages = ["Базовый тариф PenaQuiz", 'Убрать логотип "PenaQuiz"']; const StepperText: Record = { - volume: "Тарифы на объём", - time: "Тарифы на время", + volume: "Тарифы на объём", + time: "Тарифы на время", }; - - function TariffPage() { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const isMobile = useMediaQuery(theme.breakpoints.down(600)); - const isTablet = useMediaQuery(theme.breakpoints.down(1000)); - const location = useLocation(); - const tariffs = useTariffStore((state) => state.tariffs); - const [selectedItem, setSelectedItem] = useState(0); - const purchasesAmount = useUserStore((state) => state.userAccount?.wallet.spent) ?? 0; - const userId = useUserStore(state => state.user?._id) ?? ""; - const discounts = useDiscounts(userId); - const isUserNko = useUserStore((state) => state.userAccount?.status) === "nko"; - const currentTariffs = useCartTariffs(); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const isMobile = useMediaQuery(theme.breakpoints.down(600)); + const isTablet = useMediaQuery(theme.breakpoints.down(1000)); + const location = useLocation(); + const tariffs = useTariffStore((state) => state.tariffs); + const [selectedItem, setSelectedItem] = useState(0); + const purchasesAmount = + useUserStore((state) => state.userAccount?.wallet.spent) ?? 0; + const userId = useUserStore((state) => state.user?._id) ?? ""; + const discounts = useDiscounts(userId); + const isUserNko = + useUserStore((state) => state.userAccount?.status) === "nko"; + const currentTariffs = useCartTariffs(); - const handleCustomBackNavigation = usePrevLocation(location); + const handleCustomBackNavigation = usePrevLocation(location); - const unit: string = String(location.pathname).slice(9); + const unit: string = String(location.pathname).slice(9); - function handleTariffItemClick(tariffId: string) { - addTariffToCart(tariffId) - .then(() => { - enqueueSnackbar("Тариф добавлен в корзину"); - }) - .catch((error) => { - const message = getMessageFromFetchError(error); - if (message) enqueueSnackbar(message); - }); + function handleTariffItemClick(tariffId: string) { + addTariffToCart(tariffId) + .then(() => { + enqueueSnackbar("Тариф добавлен в корзину"); + }) + .catch((error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); + }); + } + + const filteredTariffs = tariffs.filter((tariff) => { + if (tariff.privileges[0] === undefined) return false; + if ( + (tariff.privileges[0].type === "day") === (unit === "time") && + !tariff.isDeleted && + !tariff.isCustom + ) { + if ( + ((selectedItem === 0 && unit === "time") || unit !== "time") && + tariff.privileges[0].serviceKey === "squiz" && + tariff.privileges[0].privilegeId !== "squizHideBadge" + ) + return true; + } + if ( + selectedItem === 1 && + unit === "time" && + tariff.privileges[0].privilegeId === "squizHideBadge" + ) { + return true; + } + return false; + }); + + const createTariffElements = ( + filteredTariffs: Tariff[], + addFreeTariff = false + ) => { + const tariffElements = filteredTariffs + .filter((tariff) => tariff.privileges.length > 0) + .map((tariff, index) => { + const { priceBeforeDiscounts, priceAfterDiscounts } = calcTariffPrice( + tariff, + discounts ?? [], + purchasesAmount, + currentTariffs ?? [], + isUserNko, + userId + ); + + return ( + + } + buttonProps={{ + text: "Выбрать", + onClick: () => handleTariffItemClick(tariff._id), + }} + headerText={tariff.name} + text={tariff.description || ""} + price={ + <> + {priceBeforeDiscounts !== priceAfterDiscounts && ( + + {currencyFormatter.format( + Math.trunc(priceBeforeDiscounts) / 100 + )} + + )} + + {currencyFormatter.format( + Math.trunc(priceAfterDiscounts) / 100 + )} + + + } + /> + ); + }); + + if (addFreeTariff) { + if (tariffElements.length < 6) + tariffElements.push(); + else + tariffElements.splice(5, 0, ); } - const filteredTariffs = tariffs.filter((tariff) => { - if (tariff.privileges[0] === undefined) return false; - if ( - (tariff.privileges[0].type === "day") === (unit === "time") && - !tariff.isDeleted && - !tariff.isCustom - ) { - if ( - ((selectedItem === 0 && unit === "time") || unit !== "time") && - tariff.privileges[0].serviceKey === "squiz" && - tariff.privileges[0].privilegeId !== "squizHideBadge" - ) - return true; - } - if ( - selectedItem === 1 && - unit === "time" && - tariff.privileges[0].privilegeId === "squizHideBadge" - ) { - return true; - } - return false; - }); + return tariffElements; + }; - const createTariffElements = (filteredTariffs: Tariff[], addFreeTariff = false) => { - const tariffElements = filteredTariffs - .filter((tariff) => tariff.privileges.length > 0) - .map((tariff, index) => { - const { priceBeforeDiscounts, priceAfterDiscounts } = calcTariffPrice( - tariff, - discounts ?? [], - purchasesAmount, - currentTariffs ?? [], - isUserNko, - userId, - ); - - return ( - - } - buttonProps={{ - text: "Выбрать", - onClick: () => handleTariffItemClick(tariff._id), - }} - headerText={tariff.name} - text={tariff.description || ""} - price={ - <> - {priceBeforeDiscounts !== priceAfterDiscounts && ( - {currencyFormatter.format(Math.trunc(priceBeforeDiscounts) / 100)} - )} - {currencyFormatter.format(Math.trunc(priceAfterDiscounts) / 100)} - - } - /> - ); - }); - - if (addFreeTariff) { - if (tariffElements.length < 6) tariffElements.push(); - else tariffElements.splice(5, 0, ); - } - - return tariffElements; - }; - - return ( - + + {isMobile && ( + + + + )} + - - {isMobile && ( - - - - )} - - {StepperText[unit]} - - - {unit === "time" && ( - <> - {isMobile ? ( - + ) : ( + + )} + + )} + + {createTariffElements(filteredTariffs, true)} + + {/*{recentlyPurchased.length > 0 && (*/} + {/* <>*/} + {/* */} + {/* Ранее вы*/} + {/* */} + {/* */} + {/* */} + {/*)}*/} + + ); } export default withErrorBoundary(TariffPage, { - fallback: ( - - Ошибка загрузки тарифов - - ), - onError: handleComponentError, + fallback: ( + + Ошибка загрузки тарифов + + ), + onError: handleComponentError, }); diff --git a/src/pages/auth/Recover.tsx b/src/pages/auth/Recover.tsx index b2b0c58..e9c15c4 100644 --- a/src/pages/auth/Recover.tsx +++ b/src/pages/auth/Recover.tsx @@ -47,17 +47,15 @@ export default function RecoverDialog() { initialValues, validationSchema, onSubmit: async (values, formikHelpers) => { - const [recoverResponse, recoverError] = await recover( - values.email.trim() - ); + const [, recoverError] = await recover(values.email.trim()); formikHelpers.setSubmitting(false); if (recoverError) { - enqueueSnackbar(recoverError); - return + enqueueSnackbar(recoverError); + return; } - navigate("/") - enqueueSnackbar("Письмо придёт Вам на почту") + navigate("/"); + enqueueSnackbar("Письмо придёт Вам на почту"); }, }); From a76fe4b0a08508129eac176e6d6f115898f39908 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Wed, 29 May 2024 14:58:54 +0300 Subject: [PATCH 10/27] refactor: requests --- src/api/history.ts | 14 +- src/api/recentlyPurchasedTariffs.ts | 4 +- src/api/ticket.ts | 24 +- src/api/wallet.ts | 12 +- src/components/FloatingSupportChat/Chat.tsx | 65 ++--- .../FloatingSupportChat/ChatDocument.tsx | 2 +- .../FloatingSupportChat/ChatImage.tsx | 2 +- .../FloatingSupportChat/ChatVideo.tsx | 2 +- src/pages/Payment/Payment.tsx | 8 +- src/pages/Support/ChatImageNewWindow.tsx | 2 +- src/pages/Support/CreateTicket.tsx | 258 +++++++++--------- src/utils/hooks/useCustomTariffs.ts | 59 ++-- src/utils/hooks/useTariffFetcher.ts | 70 +++-- 13 files changed, 283 insertions(+), 239 deletions(-) diff --git a/src/api/history.ts b/src/api/history.ts index ac5c06c..6e21f69 100644 --- a/src/api/history.ts +++ b/src/api/history.ts @@ -87,9 +87,10 @@ export const getHistory = async (): Promise< export const sendReport = async ( id: string -): Promise<[unknown | null, string?]> => { +): Promise<[void | null, string?]> => { + debugger; try { - const sendReportResponse = await makeRequest<{ id: string }, unknown>({ + const sendReportResponse = await makeRequest<{ id: string }, void>({ method: "POST", url: `${API_URL}/sendReport`, body: { id }, @@ -99,15 +100,16 @@ export const sendReport = async ( } catch (nativeError) { const [error] = parseAxiosError(nativeError); - return [[], `Не удалось отправить отчёт. ${error}`]; + return [null, `Не удалось отправить отчёт. ${error}`]; } }; export const sendReportById = async ( tariffId: string -): Promise<[unknown | null, string?]> => { +): Promise<[void | null, string?]> => { + debugger; try { - const sendReportResponse = await makeRequest({ + const sendReportResponse = await makeRequest({ method: "POST", url: `${API_URL}/sendReport/${tariffId}`, }); @@ -116,6 +118,6 @@ export const sendReportById = async ( } catch (nativeError) { const [error] = parseAxiosError(nativeError); - return [[], `Не удалось отправить отчёт. ${error}`]; + return [null, `Не удалось отправить отчёт. ${error}`]; } }; diff --git a/src/api/recentlyPurchasedTariffs.ts b/src/api/recentlyPurchasedTariffs.ts index 8e86cf9..ac3b052 100644 --- a/src/api/recentlyPurchasedTariffs.ts +++ b/src/api/recentlyPurchasedTariffs.ts @@ -8,13 +8,13 @@ type GetRecentlyPurchasedTariffsResponse = { }; export const getRecentlyPurchasedTariffs = async (): Promise< - [GetRecentlyPurchasedTariffsResponse | null, string?] + [GetRecentlyPurchasedTariffsResponse[] | null, string?] > => { try { debugger; const recentlyPurchased = await makeRequest< never, - GetRecentlyPurchasedTariffsResponse + GetRecentlyPurchasedTariffsResponse[] >({ method: "GET", url: `${API_URL}/recent`, diff --git a/src/api/ticket.ts b/src/api/ticket.ts index 11c39cb..96fea3e 100644 --- a/src/api/ticket.ts +++ b/src/api/ticket.ts @@ -1,5 +1,7 @@ import makeRequest from "@api/makeRequest"; import { parseAxiosError } from "@root/utils/parse-error"; +import { createTicket as createTicketRequest } from "@frontend/kitui"; +import type { CreateTicketResponse } from "@frontend/kitui"; import { SendTicketMessageRequest } from "@frontend/kitui"; @@ -7,7 +9,7 @@ type SendFileResponse = { message: string; }; -const API_URL = `${process.env.REACT_APP_DOMAIN}/heruvym`; +const API_URL = `${process.env.REACT_APP_DOMAIN}/heruvym/v1.0.0`; export const sendTicketMessage = async ( ticketId: string, @@ -61,7 +63,7 @@ export const sendFile = async ( const sendResponse = await makeRequest({ method: "POST", - url: `${process.env.REACT_APP_DOMAIN}/sendFiles`, + url: `${API_URL}/sendFiles`, body, }); @@ -72,3 +74,21 @@ export const sendFile = async ( return [null, `Не удалось отправить файл. ${error}`]; } }; + +export const createTicket = async ( + ticketNameField: string, + ticketBodyField: string +): Promise<[CreateTicketResponse | null, string?]> => { + try { + const createTicketResponse = await createTicketRequest({ + url: `${API_URL}/create`, + body: { Title: ticketNameField, Message: ticketBodyField }, + }); + + return [createTicketResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + + return [null, `Не удалось отправить файл. ${error}`]; + } +}; diff --git a/src/api/wallet.ts b/src/api/wallet.ts index e5a934d..1cb3745 100644 --- a/src/api/wallet.ts +++ b/src/api/wallet.ts @@ -63,20 +63,22 @@ export const sendPayment = async ({ } }; -export const sendRSPayment = async (money: number): Promise => { +export const sendRSPayment = async ( + money: number +): Promise<[string | null, string?]> => { try { - await makeRequest({ + const sendRSPaymentResponse = await makeRequest<{ money: number }, string>({ method: "POST", url: `${API_URL}/wallet/rspay`, - body: { money: money }, + body: { money }, useToken: true, withCredentials: false, }); - return null; + return [sendRSPaymentResponse]; } catch (nativeError) { const [error] = parseAxiosError(nativeError); - return `Ошибка оплаты. ${error}`; + return [null, `Ошибка оплаты. ${error}`]; } }; diff --git a/src/components/FloatingSupportChat/Chat.tsx b/src/components/FloatingSupportChat/Chat.tsx index abd2bbb..76263cd 100644 --- a/src/components/FloatingSupportChat/Chat.tsx +++ b/src/components/FloatingSupportChat/Chat.tsx @@ -11,7 +11,6 @@ import { useTheme, } from "@mui/material"; import { - createTicket, getMessageFromFetchError, throttle, TicketMessage, @@ -59,6 +58,7 @@ import AttachFileIcon from "@mui/icons-material/AttachFile"; import ChatDocument from "./ChatDocument"; import ChatImage from "./ChatImage"; import ChatVideo from "./ChatVideo"; +import { createTicket } from "@api/ticket"; type ModalWarningType = | "errorType" @@ -267,29 +267,24 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { if (!sessionData?.ticketId) { setIsMessageSending(true); - createTicket({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/create", - body: { - Title: "Unauth title", - Message: messageField, - }, - useToken: Boolean(user), - }) - .then((response) => { - setTicketData({ - ticketId: response.Ticket, - sessionId: response.sess, - }); - setSseEnabled(true); - }) - .catch((error) => { - const errorMessage = getMessageFromFetchError(error); - if (errorMessage) enqueueSnackbar(errorMessage); - }) - .finally(() => { - setMessageField(""); - setIsMessageSending(false); + + const [createTicketresult, createTicketerror] = await createTicket( + "Unauth title", + messageField + ); + + if (createTicketerror) { + enqueueSnackbar(createTicketerror); + } else if (createTicketresult) { + setTicketData({ + ticketId: createTicketresult.Ticket, + sessionId: createTicketresult.sess, }); + setSseEnabled(true); + } + + setMessageField(""); + setIsMessageSending(false); } else { setIsMessageSending(true); @@ -333,18 +328,18 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { let data; if (!ticket.sessionData?.ticketId) { try { - data = await createTicket({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/create", - body: { - Title: "Unauth title", - Message: "", - }, - useToken: Boolean(user), - }); - setTicketData({ - ticketId: data.Ticket, - sessionId: data.sess, - }); + const [createTicketresult] = await createTicket("Unauth title", ""); + + if (createTicketresult) { + data = createTicketresult; + } + + if (data) { + setTicketData({ + ticketId: data.Ticket, + sessionId: data.sess, + }); + } } catch (error: any) { const errorMessage = getMessageFromFetchError(error); if (errorMessage) enqueueSnackbar(errorMessage); diff --git a/src/components/FloatingSupportChat/ChatDocument.tsx b/src/components/FloatingSupportChat/ChatDocument.tsx index daaa037..c358755 100644 --- a/src/components/FloatingSupportChat/ChatDocument.tsx +++ b/src/components/FloatingSupportChat/ChatDocument.tsx @@ -98,7 +98,7 @@ export default function ChatDocument({ diff --git a/src/components/FloatingSupportChat/ChatVideo.tsx b/src/components/FloatingSupportChat/ChatVideo.tsx index 5779858..153e6b9 100644 --- a/src/components/FloatingSupportChat/ChatVideo.tsx +++ b/src/components/FloatingSupportChat/ChatVideo.tsx @@ -113,7 +113,7 @@ export default function ChatImage({ }} controls > - + diff --git a/src/pages/Payment/Payment.tsx b/src/pages/Payment/Payment.tsx index 6ceae08..e2e17f9 100644 --- a/src/pages/Payment/Payment.tsx +++ b/src/pages/Payment/Payment.tsx @@ -139,10 +139,12 @@ export default function Payment() { return; } - const sendRSPaymentError = await sendRSPayment(Number(paymentValueField)); + const [sendRSPaymentResponse] = await sendRSPayment( + Number(paymentValueField) + ); - if (sendRSPaymentError) { - return enqueueSnackbar(sendRSPaymentError); + if (sendRSPaymentResponse) { + return enqueueSnackbar(sendRSPaymentResponse); } enqueueSnackbar( diff --git a/src/pages/Support/ChatImageNewWindow.tsx b/src/pages/Support/ChatImageNewWindow.tsx index 4138ea5..86c2a2e 100644 --- a/src/pages/Support/ChatImageNewWindow.tsx +++ b/src/pages/Support/ChatImageNewWindow.tsx @@ -12,7 +12,7 @@ export default function ChatImageNewWindow() { maxHeight: "100vh", maxWidth: "100vw", }} - src={`https://storage.yandexcloud.net/pair/${srcImage}`} + src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/pair/${srcImage}`} /> ); diff --git a/src/pages/Support/CreateTicket.tsx b/src/pages/Support/CreateTicket.tsx index 77e03f4..a3adcf7 100644 --- a/src/pages/Support/CreateTicket.tsx +++ b/src/pages/Support/CreateTicket.tsx @@ -1,132 +1,138 @@ -import { Box, Typography, FormControl, InputBase, useMediaQuery, useTheme, Button } from "@mui/material" -import { useState } from "react" -import { useNavigate } from "react-router-dom" -import { enqueueSnackbar } from "notistack" -import { cardShadow } from "@root/utils/theme" -import { createTicket } from "@frontend/kitui" - +import { + Box, + Typography, + FormControl, + InputBase, + useMediaQuery, + useTheme, + Button, +} from "@mui/material"; +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { enqueueSnackbar } from "notistack"; +import { cardShadow } from "@root/utils/theme"; +import { createTicket } from "@api/ticket"; export default function CreateTicket() { - const theme = useTheme() - const upMd = useMediaQuery(theme.breakpoints.up("md")) - const navigate = useNavigate() - const [ticketNameField, setTicketNameField] = useState("") - const [ticketBodyField, setTicketBodyField] = useState("") + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const navigate = useNavigate(); + const [ticketNameField, setTicketNameField] = useState(""); + const [ticketBodyField, setTicketBodyField] = useState(""); - async function handleCreateTicket() { - if (!ticketBodyField || !ticketNameField) return + async function handleCreateTicket() { + if (!ticketBodyField || !ticketNameField) return; - createTicket({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/create", - body: { - Title: ticketNameField, - Message: ticketBodyField, - } - }).then(result => { - navigate(`/support/${result.Ticket}`) - }).catch(error => { - enqueueSnackbar(error.message) - }) - } + const [createTicketresult, createTicketerror] = await createTicket( + ticketNameField, + ticketBodyField + ); - return ( - - - - Написать обращение - - - setTicketNameField(e.target.value)} - /> - - - - setTicketBodyField(e.target.value)} - /> - - - - - - - - ) + if (createTicketerror) { + return enqueueSnackbar(createTicketerror); + } + + navigate(`/support/${createTicketresult?.Ticket}`); + } + + return ( + + + + Написать обращение + + + setTicketNameField(e.target.value)} + /> + + + + setTicketBodyField(e.target.value)} + /> + + + + + + + + ); } diff --git a/src/utils/hooks/useCustomTariffs.ts b/src/utils/hooks/useCustomTariffs.ts index 0b57118..cbfaa4d 100644 --- a/src/utils/hooks/useCustomTariffs.ts +++ b/src/utils/hooks/useCustomTariffs.ts @@ -1,38 +1,45 @@ -import { useEffect, useLayoutEffect, useRef } from "react" -import { devlog } from "@frontend/kitui" +import { useEffect, useLayoutEffect, useRef } from "react"; +import { devlog } from "@frontend/kitui"; -import { ServiceKeyToPrivilegesMap } from "@root/model/privilege" -import { getCustomTariffs } from "@root/api/tariff" +import { ServiceKeyToPrivilegesMap } from "@root/model/privilege"; +import { getCustomTariffs } from "@root/api/tariff"; export function useCustomTariffs({ - onError, - onNewUser, + onError, + onNewUser, }: { onNewUser: (response: ServiceKeyToPrivilegesMap) => void; onError: (error: any) => void; }) { - const onNewUserRef = useRef(onNewUser) - const onErrorRef = useRef(onError) + const onNewUserRef = useRef(onNewUser); + const onErrorRef = useRef(onError); - useLayoutEffect(() => { - onNewUserRef.current = onNewUser - onErrorRef.current = onError - }) + useLayoutEffect(() => { + onNewUserRef.current = onNewUser; + onErrorRef.current = onError; + }); - useEffect(() => { - const controller = new AbortController() + useEffect(() => { + const controller = new AbortController(); - getCustomTariffs(controller.signal) - .then(([customTariffs]) => { - if (customTariffs) { - onNewUserRef.current(customTariffs) - } - }) - .catch(([_, error]) => { - devlog("Error fetching custom tariffs", error) - onErrorRef.current(error) - }) + const getCustomTariffsRequest = async () => { + const [customTariffs, customTariffsError] = await getCustomTariffs( + controller.signal + ); - return () => controller.abort() - }, []) + if (customTariffsError) { + devlog("Error fetching custom tariffs", customTariffsError); + onErrorRef.current(customTariffsError); + + return; + } + if (customTariffs) { + onNewUserRef.current(customTariffs); + } + }; + + getCustomTariffsRequest(); + + return () => controller.abort(); + }, []); } diff --git a/src/utils/hooks/useTariffFetcher.ts b/src/utils/hooks/useTariffFetcher.ts index a099785..8013825 100644 --- a/src/utils/hooks/useTariffFetcher.ts +++ b/src/utils/hooks/useTariffFetcher.ts @@ -1,14 +1,14 @@ -import { useEffect, useLayoutEffect, useRef, useState } from "react" +import { useEffect, useLayoutEffect, useRef, useState } from "react"; -import { getTariffs } from "@root/api/tariff" +import { getTariffs } from "@root/api/tariff"; -import type { Tariff } from "@frontend/kitui" +import type { Tariff } from "@frontend/kitui"; export function useTariffFetcher({ - tariffsPerPage, - apiPage, - onSuccess, - onError, + tariffsPerPage, + apiPage, + onSuccess, + onError, }: { baseUrl?: string; tariffsPerPage: number; @@ -16,32 +16,42 @@ export function useTariffFetcher({ onSuccess: (response: Tariff[]) => void; onError?: (error: Error) => void; }) { - const [fetchState, setFetchState] = useState<"fetching" | "idle" | "all fetched">("idle") - const onSuccessRef = useRef(onSuccess) - const onErrorRef = useRef(onError) + const [fetchState, setFetchState] = useState< + "fetching" | "idle" | "all fetched" + >("idle"); + const onSuccessRef = useRef(onSuccess); + const onErrorRef = useRef(onError); - useLayoutEffect(() => { - onSuccessRef.current = onSuccess - onErrorRef.current = onError - }, [onError, onSuccess]) + useLayoutEffect(() => { + onSuccessRef.current = onSuccess; + onErrorRef.current = onError; + }, [onError, onSuccess]); - useEffect(() => { - const controller = new AbortController() + useEffect(() => { + const controller = new AbortController(); - setFetchState("fetching") - getTariffs(apiPage, tariffsPerPage, controller.signal) - .then(([result]) => { - if (result && result.tariffs.length > 0) { - onSuccessRef.current(result.tariffs) - setFetchState("idle") - } else setFetchState("all fetched") - }) - .catch(([_, error]) => { - onErrorRef.current?.(error) - }) + const getTariffsRequest = async () => { + setFetchState("fetching"); + const [tariffs, tariffsError] = await getTariffs( + apiPage, + tariffsPerPage, + controller.signal + ); - return () => controller.abort() - }, [apiPage, tariffsPerPage]) + if (tariffsError) { + return onErrorRef.current?.(new Error(tariffsError)); + } - return fetchState + if (tariffs && tariffs.tariffs.length > 0) { + onSuccessRef.current(tariffs.tariffs); + setFetchState("idle"); + } else setFetchState("all fetched"); + }; + + getTariffsRequest(); + + return () => controller.abort(); + }, [apiPage, tariffsPerPage]); + + return fetchState; } From 5d0e9d8b0e58ad02b5a6ff31a7f473f4cf0774c5 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Wed, 29 May 2024 15:12:35 +0300 Subject: [PATCH 11/27] fix: error --- src/utils/parse-error.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/utils/parse-error.ts b/src/utils/parse-error.ts index 3869f35..c8b5f03 100644 --- a/src/utils/parse-error.ts +++ b/src/utils/parse-error.ts @@ -9,10 +9,10 @@ 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": "Пользователь с таким логином уже существует", @@ -29,12 +29,16 @@ export const parseAxiosError = (nativeError: unknown): [string, number?] => { if (error.response?.data) { const serverError = error.response.data as ServerError; let SEMessage; + + if (typeof error.response?.data === "string") { + return [error.response?.data]; + } if ("statusCode" in (error.response?.data as ServerError)) { SEMessage = serverError?.message.toLowerCase() || ""; } if ( - "error" in (error.response?.data as ServerError) && - !("statusCode" in (error.response?.data as ServerError)) + "error" in (error.response?.data as ServerError) && + !("statusCode" in (error.response?.data as ServerError)) ) { SEMessage = serverError?.error.toLowerCase() || ""; } From 56b00a57a088b32b3bde66bae824ff5666563598 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Wed, 29 May 2024 15:25:33 +0300 Subject: [PATCH 12/27] fix: urls --- src/components/FloatingSupportChat/ChatDocument.tsx | 6 +++--- src/components/FloatingSupportChat/ChatImage.tsx | 6 +++--- src/components/FloatingSupportChat/ChatVideo.tsx | 4 +++- src/pages/Support/ChatImageNewWindow.tsx | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/FloatingSupportChat/ChatDocument.tsx b/src/components/FloatingSupportChat/ChatDocument.tsx index c358755..a506854 100644 --- a/src/components/FloatingSupportChat/ChatDocument.tsx +++ b/src/components/FloatingSupportChat/ChatDocument.tsx @@ -21,8 +21,8 @@ export default function ChatDocument({ const messageBackgroundColor = isSelf ? "white" : unAuthenticated - ? "#EFF0F5" - : "#434657"; + ? "#EFF0F5" + : "#434657"; const date = new Date(createdAt); const today = isDateToday(date); @@ -98,7 +98,7 @@ export default function ChatDocument({ diff --git a/src/components/FloatingSupportChat/ChatVideo.tsx b/src/components/FloatingSupportChat/ChatVideo.tsx index 153e6b9..424be19 100644 --- a/src/components/FloatingSupportChat/ChatVideo.tsx +++ b/src/components/FloatingSupportChat/ChatVideo.tsx @@ -113,7 +113,9 @@ export default function ChatImage({ }} controls > - + diff --git a/src/pages/Support/ChatImageNewWindow.tsx b/src/pages/Support/ChatImageNewWindow.tsx index 86c2a2e..89e2260 100644 --- a/src/pages/Support/ChatImageNewWindow.tsx +++ b/src/pages/Support/ChatImageNewWindow.tsx @@ -12,7 +12,7 @@ export default function ChatImageNewWindow() { maxHeight: "100vh", maxWidth: "100vw", }} - src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/pair/${srcImage}`} + src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/angesight/pair/${srcImage}`} /> ); From 79efe911a3c56a068500df8c99cf2ef9d9a70376 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Fri, 31 May 2024 12:17:06 +0000 Subject: [PATCH 13/27] Update Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3010b3d..ee3f09e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine as build +FROM penahub.gitlab.yandexcloud.net:5050/devops/dockerhub-backup/node as build RUN apk update && rm -rf /var/cache/apk/* WORKDIR /usr/app @@ -8,7 +8,7 @@ RUN yarn install --ignore-scripts --non-interactive --frozen-lockfile && yarn ca RUN yarn build -FROM nginx:latest as result +FROM penahub.gitlab.yandexcloud.net:5050/devops/dockerhub-backup/nginx as result WORKDIR /usr/share/nginx/html COPY --from=build /usr/app/build/ /usr/share/nginx/html COPY hub.conf /etc/nginx/conf.d/default.conf From e44e1ce102454519a93419b0793352bbb9eda336 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Fri, 31 May 2024 18:59:57 +0000 Subject: [PATCH 14/27] Update docker-compose.yaml --- deployments/staging/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/deployments/staging/docker-compose.yaml b/deployments/staging/docker-compose.yaml index f3e44ff..f6fa148 100644 --- a/deployments/staging/docker-compose.yaml +++ b/deployments/staging/docker-compose.yaml @@ -1,3 +1,4 @@ +version: "3" services: hub: container_name: hub From 57977b2e4103e266bf6feac1a33082d607194a23 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Fri, 31 May 2024 19:03:47 +0000 Subject: [PATCH 15/27] Update docker-compose.yaml --- deployments/staging/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/deployments/staging/docker-compose.yaml b/deployments/staging/docker-compose.yaml index 8a445d5..0ec0663 100644 --- a/deployments/staging/docker-compose.yaml +++ b/deployments/staging/docker-compose.yaml @@ -1,3 +1,4 @@ +version: "3" services: hub: container_name: hub From 2398475e295bfccaf9b9cc7003225e9400fd8e75 Mon Sep 17 00:00:00 2001 From: Nastya Date: Sat, 1 Jun 2024 03:18:10 +0300 Subject: [PATCH 16/27] new version tp --- src/components/FloatingSupportChat/ChatDocument.tsx | 4 ++-- src/components/FloatingSupportChat/ChatImage.tsx | 2 +- src/components/FloatingSupportChat/ChatVideo.tsx | 2 +- src/pages/Support/ChatImageNewWindow.tsx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/FloatingSupportChat/ChatDocument.tsx b/src/components/FloatingSupportChat/ChatDocument.tsx index a506854..53f3f9d 100644 --- a/src/components/FloatingSupportChat/ChatDocument.tsx +++ b/src/components/FloatingSupportChat/ChatDocument.tsx @@ -98,7 +98,7 @@ export default function ChatDocument({ ); -} +} \ No newline at end of file diff --git a/src/components/FloatingSupportChat/ChatImage.tsx b/src/components/FloatingSupportChat/ChatImage.tsx index e932a34..2115341 100644 --- a/src/components/FloatingSupportChat/ChatImage.tsx +++ b/src/components/FloatingSupportChat/ChatImage.tsx @@ -110,7 +110,7 @@ export default function ChatImage({ height: "217px", width: "217px", }} - src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/angesight/pair/${file}`} + src={`https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/angesight/${file}`} /> diff --git a/src/components/FloatingSupportChat/ChatVideo.tsx b/src/components/FloatingSupportChat/ChatVideo.tsx index 424be19..375f384 100644 --- a/src/components/FloatingSupportChat/ChatVideo.tsx +++ b/src/components/FloatingSupportChat/ChatVideo.tsx @@ -114,7 +114,7 @@ export default function ChatImage({ controls > diff --git a/src/pages/Support/ChatImageNewWindow.tsx b/src/pages/Support/ChatImageNewWindow.tsx index 89e2260..4d33853 100644 --- a/src/pages/Support/ChatImageNewWindow.tsx +++ b/src/pages/Support/ChatImageNewWindow.tsx @@ -12,7 +12,7 @@ export default function ChatImageNewWindow() { maxHeight: "100vh", maxWidth: "100vw", }} - src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/angesight/pair/${srcImage}`} + src={`https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/angesight/${srcImage}`} /> ); From 396e7b688a4c60abe96e2783e3b5123d066703e0 Mon Sep 17 00:00:00 2001 From: Nastya Date: Sat, 1 Jun 2024 19:28:09 +0300 Subject: [PATCH 17/27] angesight->angelsight --- src/components/FloatingSupportChat/ChatDocument.tsx | 2 +- src/components/FloatingSupportChat/ChatImage.tsx | 2 +- src/components/FloatingSupportChat/ChatVideo.tsx | 2 +- src/pages/Support/ChatImageNewWindow.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/FloatingSupportChat/ChatDocument.tsx b/src/components/FloatingSupportChat/ChatDocument.tsx index 53f3f9d..6f51b04 100644 --- a/src/components/FloatingSupportChat/ChatDocument.tsx +++ b/src/components/FloatingSupportChat/ChatDocument.tsx @@ -98,7 +98,7 @@ export default function ChatDocument({ diff --git a/src/components/FloatingSupportChat/ChatVideo.tsx b/src/components/FloatingSupportChat/ChatVideo.tsx index 375f384..bba23c4 100644 --- a/src/components/FloatingSupportChat/ChatVideo.tsx +++ b/src/components/FloatingSupportChat/ChatVideo.tsx @@ -114,7 +114,7 @@ export default function ChatImage({ controls > diff --git a/src/pages/Support/ChatImageNewWindow.tsx b/src/pages/Support/ChatImageNewWindow.tsx index 4d33853..69dd7de 100644 --- a/src/pages/Support/ChatImageNewWindow.tsx +++ b/src/pages/Support/ChatImageNewWindow.tsx @@ -12,7 +12,7 @@ export default function ChatImageNewWindow() { maxHeight: "100vh", maxWidth: "100vw", }} - src={`https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/angesight/${srcImage}`} + src={`https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/angelsight/${srcImage}`} /> ); From bd370901a9d3a8798a1a661469f5ed1e25601377 Mon Sep 17 00:00:00 2001 From: Tamara Date: Mon, 3 Jun 2024 02:00:19 +0300 Subject: [PATCH 18/27] heruvym version --- src/components/FloatingSupportChat/Chat.tsx | 6 +++--- src/components/ProtectedLayout.tsx | 4 ++-- src/pages/Support/SupportChat.tsx | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/FloatingSupportChat/Chat.tsx b/src/components/FloatingSupportChat/Chat.tsx index 76263cd..56d620b 100644 --- a/src/components/FloatingSupportChat/Chat.tsx +++ b/src/components/FloatingSupportChat/Chat.tsx @@ -132,7 +132,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { }, [open]); useTicketMessages({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/getMessages", + url: process.env.REACT_APP_DOMAIN + "/heruvym/v1.0.0/getMessages", isUnauth: true, ticketId: sessionData?.ticketId, messagesPerPage, @@ -154,7 +154,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { enabled: sseEnabled && isActiveSSETab && Boolean(sessionData), url: process.env.REACT_APP_DOMAIN + - `/heruvym/ticket?ticket=${sessionData?.ticketId}&s=${sessionData?.sessionId}`, + `/heruvym/v1.0.0/ticket?ticket=${sessionData?.ticketId}&s=${sessionData?.sessionId}`, onNewData: (ticketMessages) => { const isTicketClosed = ticketMessages.some( (message) => message.session_id === "close" @@ -178,7 +178,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) { }); useTicketsFetcher({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/getTickets", + url: process.env.REACT_APP_DOMAIN + "/heruvym/v1.0.0/getTickets", ticketsPerPage: 10, ticketApiPage: 0, onSuccess: (result) => { diff --git a/src/components/ProtectedLayout.tsx b/src/components/ProtectedLayout.tsx index 2ccbafe..7ba6a7e 100644 --- a/src/components/ProtectedLayout.tsx +++ b/src/components/ProtectedLayout.tsx @@ -38,7 +38,7 @@ export default function ProtectedLayout() { enabled: isActiveSSETab, url: process.env.REACT_APP_DOMAIN + - `/heruvym/subscribe?Authorization=${token}`, + `/heruvym/v1.0.0/subscribe?Authorization=${token}`, onNewData: (data) => { updateSSEValue(data); updateTickets(data.filter((d) => Boolean(d.id))); @@ -48,7 +48,7 @@ export default function ProtectedLayout() { }); useTicketsFetcher({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/getTickets", + url: process.env.REACT_APP_DOMAIN + "/heruvym/v1.0.0/getTickets", ticketsPerPage, ticketApiPage, onSuccess: (result) => { diff --git a/src/pages/Support/SupportChat.tsx b/src/pages/Support/SupportChat.tsx index cee479e..6589f8f 100644 --- a/src/pages/Support/SupportChat.tsx +++ b/src/pages/Support/SupportChat.tsx @@ -78,7 +78,7 @@ function SupportChat() { ); useTicketMessages({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/getMessages", + url: process.env.REACT_APP_DOMAIN + "/heruvym/v1.0.0/getMessages", ticketId, messagesPerPage, messageApiPage, @@ -98,7 +98,7 @@ function SupportChat() { enabled: isActiveSSETab && Boolean(token) && Boolean(ticketId), url: process.env.REACT_APP_DOMAIN + - `/heruvym/ticket?ticket=${ticketId}&Authorization=${token}`, + `/heruvym/v1.0.0/ticket?ticket=${ticketId}&Authorization=${token}`, onNewData: (ticketMessages) => { updateSSEValue(ticketMessages); addOrUpdateMessages(ticketMessages); From 6df5268476f2356d5ea0242127b7a0ffb80d1190 Mon Sep 17 00:00:00 2001 From: Nastya Date: Fri, 7 Jun 2024 19:59:44 +0300 Subject: [PATCH 19/27] customer v 1.0.0 --- src/api/cart.ts | 2 +- src/api/history.ts | 2 +- src/api/recentlyPurchasedTariffs.ts | 2 +- src/api/wallet.ts | 2 +- src/index.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/api/cart.ts b/src/api/cart.ts index 30e2490..ed136e4 100644 --- a/src/api/cart.ts +++ b/src/api/cart.ts @@ -3,7 +3,7 @@ import makeRequest from "@api/makeRequest"; import { parseAxiosError } from "@root/utils/parse-error"; -const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; +const API_URL = `${process.env.REACT_APP_DOMAIN}/customer/v1.0.0`; export const patchCart = async ( tariffId: string diff --git a/src/api/history.ts b/src/api/history.ts index 6e21f69..d44846f 100644 --- a/src/api/history.ts +++ b/src/api/history.ts @@ -44,7 +44,7 @@ export type RawDetails = { Value: string | number | KeyValue[][]; }; -const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; +const API_URL = `${process.env.REACT_APP_DOMAIN}/customer/v1.0.0`; export const getHistory = async (): Promise< [GetHistoryResponse | GetHistoryResponse2 | null, string?] diff --git a/src/api/recentlyPurchasedTariffs.ts b/src/api/recentlyPurchasedTariffs.ts index ac3b052..d154faa 100644 --- a/src/api/recentlyPurchasedTariffs.ts +++ b/src/api/recentlyPurchasedTariffs.ts @@ -1,7 +1,7 @@ import makeRequest from "@api/makeRequest"; import { parseAxiosError } from "@root/utils/parse-error"; -const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; +const API_URL = `${process.env.REACT_APP_DOMAIN}/customer/v1.0.0`; type GetRecentlyPurchasedTariffsResponse = { id: string; diff --git a/src/api/wallet.ts b/src/api/wallet.ts index 1cb3745..dd25e4b 100644 --- a/src/api/wallet.ts +++ b/src/api/wallet.ts @@ -7,7 +7,7 @@ const isStaging = (() => { return host.includes("s") ? "s" : ""; })(); -const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`; +const API_URL = `${process.env.REACT_APP_DOMAIN}/customer/v1.0.0`; interface PaymentBody { type: string; diff --git a/src/index.tsx b/src/index.tsx index 0c8efc9..992b2d2 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -82,7 +82,7 @@ const App = () => { }); useUserAccountFetcher({ - url: process.env.REACT_APP_DOMAIN + "/customer/account", + url: process.env.REACT_APP_DOMAIN + "/customer/v1.0.0/account", userId, onNewUserAccount: setUserAccount, onError: (error) => { From 3b235db66a1fc45c8bde6dbe8b563d626ad2b988 Mon Sep 17 00:00:00 2001 From: Nastya Date: Thu, 27 Jun 2024 17:25:33 +0300 Subject: [PATCH 20/27] =?UTF-8?q?=D1=80=D0=B5=D1=88=D0=B5=D0=BD=20undefine?= =?UTF-8?q?d=20=D0=BF=D1=80=D0=B8=D0=B2=D0=B8=D0=BB=D0=B5=D0=B3=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BD=D0=B0=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BA=D0=B2=D0=B8=D0=B7=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/CustomSlider.tsx | 1 + src/pages/TariffConstructor/TariffItem.tsx | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/components/CustomSlider.tsx b/src/components/CustomSlider.tsx index e0aae16..4028807 100644 --- a/src/components/CustomSlider.tsx +++ b/src/components/CustomSlider.tsx @@ -20,6 +20,7 @@ export const CustomSlider = ({ const [step, setStep] = useState(1) useEffect(() => { + if (firstStep === 1) return setStep(1) if (value <= firstStep) { return setStep(firstStep) } diff --git a/src/pages/TariffConstructor/TariffItem.tsx b/src/pages/TariffConstructor/TariffItem.tsx index 5aa3256..6f2a239 100644 --- a/src/pages/TariffConstructor/TariffItem.tsx +++ b/src/pages/TariffConstructor/TariffItem.tsx @@ -16,10 +16,11 @@ const sliderSettingsByType = { день: { max: 365, min: 0 }, шаблон: { max: 5000, min: 0 }, МБ: { max: 5000, min: 0 }, - заявка: { max: 5000, min: 0 } + заявка: { max: 5000, min: 0 }, + шт: {max: 100, min: 0} }; -type PrivilegeName = "день" | "шаблон" | "МБ" | "заявка"; +type PrivilegeName = "день" | "шаблон" | "МБ" | "заявка" | "шт"; interface Props { privilege: CustomPrivilege; @@ -52,6 +53,8 @@ export default function TariffPrivilegeSlider({ privilege }: Props) { ); function handleSliderChange(measurement: PrivilegeName) { + console.log(measurement) + console.log(sliderSettingsByType) return (value: number | number[]) => { if (Number(value) < Number(sliderSettingsByType[measurement]?.min)) { @@ -165,7 +168,16 @@ export default function TariffPrivilegeSlider({ privilege }: Props) { min={sliderSettingsByType[privilege.value]?.min} max={sliderSettingsByType[privilege.value]?.max || 100} onChange={handleSliderChange(privilege.value)} - firstStep={privilege.value === "день" ? 30 : 100} + firstStep={ + ( + privilege.value !== "день" && + privilege.value !== "МБ" && + privilege.value !== "заявка" && + privilege.value !== "шаблон" + ) ? 1 + : + privilege.value === "день" ? 30 : 100 + } /> {!upMd && quantityElement} From 0740568a80d1671b32c927c4e5d772ca0f155158 Mon Sep 17 00:00:00 2001 From: Nastya Date: Sat, 6 Jul 2024 14:31:26 +0300 Subject: [PATCH 21/27] =?UTF-8?q?=D1=81=D1=82=D1=80=20=D0=BF=D0=BE=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B1=D0=B5=D1=80?= =?UTF-8?q?=D1=91=D1=82=20=D0=BD=D0=B5=D0=B4=D0=BE=D1=81=D1=82=D0=B0=D1=8E?= =?UTF-8?q?=D1=89=D1=83=D1=8E=20=D1=81=D1=83=D0=BC=D0=BC=D1=83=20=D0=B8?= =?UTF-8?q?=D0=B7=20=D0=B1=D0=BE=D0=BA=D0=BE=D0=B2=D0=BE=D0=B9=20=D0=BA?= =?UTF-8?q?=D0=BE=D1=80=D0=B7=D0=B8=D0=BD=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Drawers.tsx | 5 +++++ src/pages/Payment/Payment.tsx | 17 +++++++++++++++-- src/stores/diffMoney.ts | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/stores/diffMoney.ts diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index ab88653..0a94792 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -20,6 +20,7 @@ import { withErrorBoundary } from "react-error-boundary"; import { handleComponentError } from "@root/utils/handleComponentError"; import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"; import { setNotEnoughMoneyAmount, useCartStore } from "@root/stores/cart"; +import { useDiffMoney } from "@root/stores/diffMoney"; function Drawers() { const [openNotificationsModal, setOpenNotificationsModal] = useState(false); @@ -35,6 +36,7 @@ function Drawers() { const userAccount = useUserStore((state) => state.userAccount); const tickets = useTicketStore((state) => state.tickets); const notEnoughMoneyAmount = useCartStore(state => state.notEnoughMoneyAmount); + const { setNewDiff } = useDiffMoney() const notificationsCount = tickets.filter( ({ user, top_message }) => user !== top_message.user_id && top_message.shown.me !== 1 @@ -67,6 +69,9 @@ function Drawers() { function handleReplenishWallet() { setIsDrawerOpen(false); + if (location.pathname.includes("/payment")) { + setNewDiff(notEnoughMoneyAmount) + } navigate("/payment", { state: { notEnoughMoneyAmount } }); } diff --git a/src/pages/Payment/Payment.tsx b/src/pages/Payment/Payment.tsx index e2e17f9..88bb214 100644 --- a/src/pages/Payment/Payment.tsx +++ b/src/pages/Payment/Payment.tsx @@ -25,13 +25,15 @@ import { currencyFormatter } from "@root/utils/currencyFormatter"; import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker"; import { cardShadow } from "@root/utils/theme"; import { enqueueSnackbar } from "notistack"; -import { useLayoutEffect, useState } from "react"; +import { useEffect, useLayoutEffect, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; import CollapsiblePromocodeField from "./CollapsiblePromocodeField"; import PaymentMethodCard from "./PaymentMethodCard"; import { SorryModal } from "./SorryModal"; import { WarnModal } from "./WarnModal"; import { mutate } from "swr"; +import { useCartStore } from "@root/stores/cart"; +import { useDiffMoney } from "@root/stores/diffMoney"; type PaymentMethod = { label: string; @@ -66,11 +68,14 @@ export default function Payment() { const [fromSquiz, setIsFromSquiz] = useState(false); const location = useLocation(); console.log("location", location); + console.log("location", location.state); const verificationStatus = useUserStore((state) => state.verificationStatus); const userId = useUserStore((state) => state.userId); const navigate = useNavigate(); const handleCustomBackNavigation = useHistoryTracker(); + const {diffMoney, setNewDiff} = useDiffMoney() + const notEnoughMoneyAmount = (location.state?.notEnoughMoneyAmount as number) ?? 0; @@ -78,6 +83,14 @@ export default function Payment() { bigDecimal.multiply(parseFloat(paymentValueField), 100) ); + useEffect(() => { + console.log(diffMoney) + if (diffMoney > 0) { + setNewDiff(0) + setPaymentValueField((diffMoney / 100).toString()) + } + }, [diffMoney]) + useLayoutEffect(() => { setPaymentValueField((notEnoughMoneyAmount / 100).toString()); const params = new URLSearchParams(window.location.search); @@ -89,7 +102,7 @@ export default function Payment() { navigate(`/payment`, { replace: true, }); - }, []); + }, [ ]); async function handleChoosePaymentClick() { if (!selectedPaymentMethod) { diff --git a/src/stores/diffMoney.ts b/src/stores/diffMoney.ts new file mode 100644 index 0000000..2e3a49a --- /dev/null +++ b/src/stores/diffMoney.ts @@ -0,0 +1,14 @@ +import { HistoryRecord, HistoryRecord2 } from "@root/api/history"; +import { create } from "zustand"; +import { devtools, persist } from "zustand/middleware"; + + +type DiffMoneyType = { + diffMoney: number; + setNewDiff: (diff: number) => void +}; + +export const useDiffMoney = create((set) => ({ + diffMoney: 0, + setNewDiff: (diff: number) => set({diffMoney: diff}) + })); From 51752ba5e6e88689a6f01f79cc7d60c81b5ce814 Mon Sep 17 00:00:00 2001 From: Nastya Date: Sat, 6 Jul 2024 23:20:32 +0300 Subject: [PATCH 22/27] =?UTF-8?q?=D0=BA=D0=B0=D1=81=D1=82=D0=BE=D0=BC?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D1=82=D0=B0=D1=80=D0=B8=D1=84=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B7=D0=B8=D1=82?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=D0=B2=D0=B8=D0=BB=D0=B5=D0=B3=D0=B8=D1=8E?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=BC=D0=B0=D0=BD=D1=83=D0=B0=D0=BB=D0=BA?= =?UTF-8?q?=D0=B2=D0=B8=D0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/TariffConstructor/CustomTariffCard.tsx | 5 +++-- src/pages/TariffConstructor/TariffConstructor.tsx | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/TariffConstructor/CustomTariffCard.tsx b/src/pages/TariffConstructor/CustomTariffCard.tsx index 17a7928..892767c 100644 --- a/src/pages/TariffConstructor/CustomTariffCard.tsx +++ b/src/pages/TariffConstructor/CustomTariffCard.tsx @@ -78,9 +78,10 @@ export default function CustomTariffCard({ serviceKey, privileges }: Props) { gap: "25px", }} > - {privileges.map((privilege) => ( + {privileges.map((privilege) => { + if (privilege.privilegeId !== "quizManual") return ( - ))} + )})} {!upMd && ( {Object.entries(customTariffs).filter(([serviceKey]) => serviceKey === "squiz").map(([serviceKey, privileges], index) => { + console.log("privileges") + console.log(privileges) return ( Date: Wed, 10 Jul 2024 18:51:18 +0300 Subject: [PATCH 23/27] =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=B8=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4?= =?UTF-8?q?=20pdf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/verification.ts | 3 ++- src/index.tsx | 5 +---- .../DocumentsDialog/DocumentUploadItem.tsx | 10 ++++------ 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/api/verification.ts b/src/api/verification.ts index b473884..8f0007d 100644 --- a/src/api/verification.ts +++ b/src/api/verification.ts @@ -17,7 +17,8 @@ export const verification = async ( try { const verificationResponse = await makeRequest({ method: "GET", - url: `${API_URL}/${userId}`, + url: API_URL, + // url: `${API_URL}/${userId}`, useToken: true, withCredentials: true, }); diff --git a/src/index.tsx b/src/index.tsx index 992b2d2..b0c6e67 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -57,10 +57,7 @@ import AfterPay from "./pages/AfterPay"; import { PageNotFound } from "./pages/PageNotFound"; import {setNotEnoughMoneyAmount} from "@stores/cart" -pdfjs.GlobalWorkerOptions.workerSrc = new URL( - "pdfjs-dist/build/pdf.worker.min.js", - import.meta.url -).toString(); +pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`; const App = () => { const location = useLocation(); diff --git a/src/pages/AccountSettings/DocumentsDialog/DocumentUploadItem.tsx b/src/pages/AccountSettings/DocumentsDialog/DocumentUploadItem.tsx index bf39d91..6961ce9 100644 --- a/src/pages/AccountSettings/DocumentsDialog/DocumentUploadItem.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/DocumentUploadItem.tsx @@ -21,10 +21,7 @@ interface Props { accept?: string; } -pdfjs.GlobalWorkerOptions.workerSrc = new URL( - "pdfjs-dist/build/pdf.worker.min.js", - import.meta.url -).toString() +pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`; export default function DocumentUploadItem({ text, @@ -96,7 +93,8 @@ export default function DocumentUploadItem({ multiple accept={accept} /> - + {(document.file || documentUrl) && + - + } ) } From bf80655118086b6ddedbc3a14d8b56c4541adc28 Mon Sep 17 00:00:00 2001 From: Nastya Date: Wed, 10 Jul 2024 22:56:48 +0300 Subject: [PATCH 24/27] =?UTF-8?q?PUT=20method=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20=D0=B2=D0=B5=D1=80=D0=B8?= =?UTF-8?q?=D1=84=D0=B8=D0=BA=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/verification.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/verification.ts b/src/api/verification.ts index 8f0007d..c3d7cbe 100644 --- a/src/api/verification.ts +++ b/src/api/verification.ts @@ -73,8 +73,8 @@ export const updateDocuments = async ( ): Promise<[Verification | "OK" | null, string?]> => { try { const updateDocumentsResponse = await makeRequest({ - method: "PATCH", - url: `${API_URL}/file`, + method: "PUT", + url: `${API_URL}`, body: jsonToFormdata( documents.inn ? { ...documents, egrule: documents.inn } : documents ), From 1c150c540d7ada38442466eea774a8f2d3d55789 Mon Sep 17 00:00:00 2001 From: Nastya Date: Thu, 11 Jul 2024 17:12:45 +0300 Subject: [PATCH 25/27] -- --- src/index.css | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/index.css b/src/index.css index 9b02132..984b1d4 100644 --- a/src/index.css +++ b/src/index.css @@ -7,20 +7,28 @@ body { -moz-osx-font-smoothing: grayscale; } /*скрывает все скролл-бары*/ -html * { - -ms-overflow-style: none; - overflow: -moz-scrollbars-none; - scrollbar-width: none; +html * ::-webkit-scrollbar { + background-color: inherit; } -html ::-webkit-scrollbar { +html * ::-webkit-scrollbar-button { display: none; - width : 0 !important; +} +html * ::-webkit-scrollbar-track { + background-color: inherit; +} +html * ::-webkit-scrollbar-thumb { + width: 5px; + background-color: gray; } -html { - -ms-overflow-style : none; /* IE 10+ */ - scrollbar-width : none; /* Firefox */ -} + +/* +::-webkit-scrollbar — это фон самого скроллбара. +::-webkit-scrollbar-button — кнопки направления на полосе прокрутки. +::-webkit-scrollbar-track — пустое пространство под индикатором прокрутки. +::-webkit-scrollbar-thumb — индикатор прокрутки, перетаскиваемый элемент. +*/ + video { pointer-events: none; From 4287b0e9a677b8d01eda6c7d9b21602acfaf62f8 Mon Sep 17 00:00:00 2001 From: Nastya Date: Thu, 11 Jul 2024 18:03:48 +0300 Subject: [PATCH 26/27] -- --- src/components/NotificationsModal.tsx | 11 ----------- src/index.css | 9 ++++++--- .../DocumentsDialog/JuridicalDocumentsDialog.tsx | 3 --- .../DocumentsDialog/NkoDocumentsDialog.tsx | 3 --- 4 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/components/NotificationsModal.tsx b/src/components/NotificationsModal.tsx index f150d75..7b62a08 100644 --- a/src/components/NotificationsModal.tsx +++ b/src/components/NotificationsModal.tsx @@ -46,17 +46,6 @@ export const NotificationsModal = ({ borderRadius: "8px", boxShadow: "0px 3px 18px rgba(49, 28, 77, 0.1), 0px 3px 34px rgba(49, 28, 77, 0.15)", - "&::-webkit-scrollbar": { width: "6px" }, - "&::-webkit-scrollbar-track": { - background: "#F0F0F6", - margin: "5px", - borderRadius: "5px", - }, - "&::-webkit-scrollbar-thumb": { - width: "4px", - background: "#9A9AAF", - borderRadius: "5px", - }, }, }} > diff --git a/src/index.css b/src/index.css index 984b1d4..2063aec 100644 --- a/src/index.css +++ b/src/index.css @@ -6,7 +6,8 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -/*скрывает все скролл-бары*/ + +/*все скролл-бары*/ html * ::-webkit-scrollbar { background-color: inherit; } @@ -17,8 +18,10 @@ html * ::-webkit-scrollbar-track { background-color: inherit; } html * ::-webkit-scrollbar-thumb { - width: 5px; - background-color: gray; + width: 4px; + background-color: #9A9AAF; + color: #9A9AAF; + border-radius: "5px"; } diff --git a/src/pages/AccountSettings/DocumentsDialog/JuridicalDocumentsDialog.tsx b/src/pages/AccountSettings/DocumentsDialog/JuridicalDocumentsDialog.tsx index 3b77e44..5029e21 100644 --- a/src/pages/AccountSettings/DocumentsDialog/JuridicalDocumentsDialog.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/JuridicalDocumentsDialog.tsx @@ -23,9 +23,6 @@ import {parseAxiosError} from "@utils/parse-error" const dialogContainerStyle = { height: "100%", overflowY: "scroll", - "::-webkit-scrollbar": { - display: "none", - }, } export default function JuridicalDocumentsDialog() { diff --git a/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx b/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx index 325ebf2..7621d33 100644 --- a/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx @@ -19,9 +19,6 @@ import { theme } from "@root/utils/theme" const dialogContainerStyle = { height: "100%", overflowY: "scroll", - "::-webkit-scrollbar": { - display: "none", - }, } export default function NkoDocumentsDialog() { From 0aee37fe16615296b3a7ccda9e8bc3cd22842fed Mon Sep 17 00:00:00 2001 From: Nastya Date: Sat, 13 Jul 2024 16:25:34 +0300 Subject: [PATCH 27/27] =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=B8=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=8F=D0=B5=D1=82=D1=81=D1=8F=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B4=20=D1=80=D0=B8=D1=81=D0=BE=D0=B2=D0=BA=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=BF=D0=B4=D1=84,=20=D1=83=D0=BC=D0=B5=D0=BD=D1=8C=D1=88?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=BF=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.css | 7 ++- .../DocumentsDialog/DocumentItem.tsx | 25 +++++++-- .../DocumentsDialog/DocumentUploadItem.tsx | 53 +++++++++++++------ .../JuridicalDocumentsDialog.tsx | 7 +-- .../DocumentsDialog/NkoDocumentsDialog.tsx | 3 +- 5 files changed, 64 insertions(+), 31 deletions(-) diff --git a/src/index.css b/src/index.css index b13fa9a..6305e66 100644 --- a/src/index.css +++ b/src/index.css @@ -20,11 +20,14 @@ html * ::-webkit-scrollbar-track { } html * ::-webkit-scrollbar-thumb { width: 4px; - background-color: #9A9AAF; - color: #9A9AAF; + background-color: #7e2aea; + color: #7e2aea; border-radius: 5px; } +.cnvs { + width: 80px; +} /* ::-webkit-scrollbar — это фон самого скроллбара. diff --git a/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx b/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx index ec3530b..6ee36ef 100644 --- a/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/DocumentItem.tsx @@ -11,7 +11,7 @@ import { Document, Page } from "react-pdf"; import { Buffer } from "buffer"; import { downloadFileToDevice } from "@root/utils/downloadFileToDevice"; import EditIcon from "@mui/icons-material/Edit"; -import { ChangeEvent, useRef } from "react"; +import { ChangeEvent, useEffect, useRef, useState } from "react"; import { SendDocumentsArgs, Verification } from "@root/model/auth"; import { updateDocument } from "@api/verification"; import { jsonToFormdata } from "@utils/jsonToFormdata"; @@ -58,6 +58,22 @@ export default function DocumentItem({ return; }; + const [readyShowDocument, setReadyShowDocument] = useState(false); + + useEffect(() => { + if (typeof documentUrl === 'string') { + if (!documentUrl.includes("pena.digital")) { + console.log(documentUrl) + fetch(documentUrl) + .then(e => { + console.log(e) + setReadyShowDocument(true) + }) + .catch(e => console.log(e)) + } + } + }, []) + async function sendDocument(e: ChangeEvent) { const target = e.target as HTMLInputElement; const file = target?.files?.[0] || null; @@ -76,7 +92,6 @@ export default function DocumentItem({ enqueueSnackbar("Данные обновлены"); } } - return ( {text} - {documentUrl && ( + {documentUrl && readyShowDocument && ( <> - + diff --git a/src/pages/AccountSettings/DocumentsDialog/DocumentUploadItem.tsx b/src/pages/AccountSettings/DocumentsDialog/DocumentUploadItem.tsx index 6961ce9..5572813 100644 --- a/src/pages/AccountSettings/DocumentsDialog/DocumentUploadItem.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/DocumentUploadItem.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useRef } from "react" +import { ChangeEvent, useEffect, useRef, useState } from "react" import axios from "axios" import { Document, Page, pdfjs } from "react-pdf" import { Box, SxProps, Theme, Typography } from "@mui/material" @@ -13,12 +13,12 @@ import { readFile } from "@root/utils/readFile" import { downloadFileToDevice } from "@root/utils/downloadFileToDevice" interface Props { - text: string; - document: UserDocument; - documentUrl?: string; - onFileChange: (event: ChangeEvent) => void; - sx?: SxProps; - accept?: string; + text: string; + document: UserDocument; + documentUrl?: string; + onFileChange: (event: ChangeEvent) => void; + sx?: SxProps; + accept?: string; } pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`; @@ -31,12 +31,31 @@ export default function DocumentUploadItem({ sx, accept = "image/*", }: Props) { + const urlOrFile = document.file || documentUrl const fileInputRef = useRef(null) function handleChooseFileClick() { fileInputRef.current?.click() } + const [readyShowDocument, setReadyShowDocument] = useState(false); + + useEffect(() => { + if (typeof urlOrFile === 'string') { + if (!urlOrFile.includes("pena.digital")) { + console.log(documentUrl) + fetch(documentUrl) + .then(e => { + console.log(e) + setReadyShowDocument(true) + }) + .catch(e => console.log(e)) + } + } else { + setReadyShowDocument(true) + } + }, []) + const downloadFile = async () => { if (!document.file && documentUrl) { const { data } = await axios.get(documentUrl, { @@ -93,16 +112,16 @@ export default function DocumentUploadItem({ multiple accept={accept} /> - {(document.file || documentUrl) && - - - } + {urlOrFile && readyShowDocument && + + + } ) } diff --git a/src/pages/AccountSettings/DocumentsDialog/JuridicalDocumentsDialog.tsx b/src/pages/AccountSettings/DocumentsDialog/JuridicalDocumentsDialog.tsx index 5029e21..d34b782 100644 --- a/src/pages/AccountSettings/DocumentsDialog/JuridicalDocumentsDialog.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/JuridicalDocumentsDialog.tsx @@ -13,12 +13,7 @@ import { sendDocuments, updateDocuments } from "@root/api/verification" import { readFile } from "@root/utils/readFile" import { deleteEmptyKeys } from "@root/utils/deleteEmptyKeys" import { verify } from "../helper" -import {ChangeEvent, useState} from "react" import { theme } from "@root/utils/theme" -import makeRequest from "@api/makeRequest" -import {Verification} from "@root/model/auth" -import {jsonToFormdata} from "@utils/jsonToFormdata" -import {parseAxiosError} from "@utils/parse-error" const dialogContainerStyle = { height: "100%", @@ -174,7 +169,7 @@ export default function JuridicalDocumentsDialog() { > - + {verificationStatus === VerificationStatus.VERIFICATED ? "Ваши документы" diff --git a/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx b/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx index 7621d33..7271c57 100644 --- a/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx +++ b/src/pages/AccountSettings/DocumentsDialog/NkoDocumentsDialog.tsx @@ -129,6 +129,7 @@ export default function NkoDocumentsDialog() { text="1. Свидетельство о регистрации НКО" keyName="certificate" documentUrl={documentsUrl["Свидетельство о регистрации НКО"]} + /> - + {verificationStatus === VerificationStatus.VERIFICATED ? "Ваши документы"