From f375c787af137007cb5e4c2bf2655e1c3daac783 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Tue, 1 Aug 2023 14:16:27 +0300 Subject: [PATCH 01/41] fix: Support, TariffsPage styles --- src/pages/Payment/Payment.tsx | 345 ++++++++++++++++-------------- src/pages/Support/Support.tsx | 164 +++++++------- src/pages/Support/SupportChat.tsx | 1 + src/pages/Tariffs/TariffCard.tsx | 12 +- src/pages/Tariffs/TariffsPage.tsx | 1 + 5 files changed, 281 insertions(+), 242 deletions(-) diff --git a/src/pages/Payment/Payment.tsx b/src/pages/Payment/Payment.tsx index 5161693..8246ebc 100644 --- a/src/pages/Payment/Payment.tsx +++ b/src/pages/Payment/Payment.tsx @@ -1,4 +1,10 @@ -import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { + Box, + IconButton, + Typography, + useMediaQuery, + useTheme, +} from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import CustomButton from "@components/CustomButton"; import SectionWrapper from "@components/SectionWrapper"; @@ -18,185 +24,194 @@ import { enqueueSnackbar } from "notistack"; import { currencyFormatter } from "@root/utils/currencyFormatter"; import { useLocation } from "react-router-dom"; - const paymentMethods = [ - { name: "Mastercard", image: mastercardLogo }, - { name: "Visa", image: visaLogo }, - { name: "QIWI Кошелек", image: qiwiLogo }, - { name: "Мир", image: mirLogo }, - { name: "Тинькофф", image: tinkoffLogo }, + { name: "Mastercard", image: mastercardLogo }, + { name: "Visa", image: visaLogo }, + { name: "QIWI Кошелек", image: qiwiLogo }, + { name: "Мир", image: mirLogo }, + { name: "Тинькофф", image: tinkoffLogo }, ] as const; -type PaymentMethod = typeof paymentMethods[number]["name"]; +type PaymentMethod = (typeof paymentMethods)[number]["name"]; export default function Payment() { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const upSm = useMediaQuery(theme.breakpoints.up("sm")); - const [selectedPaymentMethod, setSelectedPaymentMethod] = useState(null); - const [paymentValueField, setPaymentValueField] = useState("0"); - const [paymentLink, setPaymentLink] = useState(""); - const location = useLocation(); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const [selectedPaymentMethod, setSelectedPaymentMethod] = + useState(null); + const [paymentValueField, setPaymentValueField] = useState("0"); + const [paymentLink, setPaymentLink] = useState(""); + const location = useLocation(); - const notEnoughMoneyAmount = location.state?.notEnoughMoneyAmount as number ?? 0; + const notEnoughMoneyAmount = + (location.state?.notEnoughMoneyAmount as number) ?? 0; - useEffect(() => { - setPaymentValueField((notEnoughMoneyAmount / 100).toString()); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + useEffect(() => { + setPaymentValueField((notEnoughMoneyAmount / 100).toString()); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - const paymentValue = parseFloat(paymentValueField) * 100; + useEffect(() => { + setPaymentLink(""); + }, [selectedPaymentMethod]); - function handleChoosePaymentClick() { - sendPayment().then(result => { - setPaymentLink(result.link); - }).catch(error => { - const message = getMessageFromFetchError(error); - if (message) enqueueSnackbar(message); + const paymentValue = parseFloat(paymentValueField) * 100; + + function handleChoosePaymentClick() { + if (Number(paymentValueField) !== 0) { + sendPayment() + .then((result) => { + setPaymentLink(result.link); + }) + .catch((error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); }); } + } - return ( - + {upMd && } + + {!upMd && ( + + + + )} + Способ оплаты + + {!upMd && ( + + Выберите способ оплаты + + )} + + - {upMd && } - ( + setSelectedPaymentMethod(method.name)} + /> + ))} + + + + {upMd && Выберите способ оплаты} + К оплате + {paymentLink ? ( + - {!upMd && ( - - - - )} - Способ оплаты - - {!upMd && ( - - Выберите способ оплаты - + > + {currencyFormatter.format(paymentValue / 100)} + + ) : ( + setPaymentValueField(e.target.value)} + id="payment-amount" + gap={upMd ? "16px" : "10px"} + color={"#F2F3F7"} + FormInputSx={{ mb: "28px" }} + /> )} - + {paymentLink ? ( + - - {paymentMethods.map(method => - setSelectedPaymentMethod(method.name)} - /> - )} - - - - {upMd && Выберите способ оплаты} - К оплате - {paymentLink ? - - {currencyFormatter.format(paymentValue / 100)} - - : - setPaymentValueField(e.target.value)} - id="payment-amount" - gap={upMd ? "16px" : "10px"} - color={"#F2F3F7"} - FormInputSx={{ - mb: "28px", - }} - /> - } - - {paymentLink ? - - Оплатить - - : - - Выбрать - - } - - - - ); + Оплатить + + ) : ( + + Выбрать + + )} + + + + ); } diff --git a/src/pages/Support/Support.tsx b/src/pages/Support/Support.tsx index 6b0b97d..525c234 100644 --- a/src/pages/Support/Support.tsx +++ b/src/pages/Support/Support.tsx @@ -1,6 +1,12 @@ -import { Typography, Box, useTheme, useMediaQuery, IconButton } from "@mui/material"; +import { + Typography, + Box, + useTheme, + useMediaQuery, + IconButton, +} from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; -import { useParams } from "react-router-dom"; +import { Link, useParams } from "react-router-dom"; import SectionWrapper from "@components/SectionWrapper"; import ComplexNavText from "@components/ComplexNavText"; import SupportChat from "./SupportChat"; @@ -8,82 +14,96 @@ import CreateTicket from "./CreateTicket"; import TicketList from "./TicketList/TicketList"; import { useCallback } from "react"; import { Ticket, getMessageFromFetchError, useToken } from "@frontend/kitui"; -import { updateTickets, setTicketCount, clearTickets, useTicketStore } from "@root/stores/tickets"; +import { + updateTickets, + setTicketCount, + clearTickets, + useTicketStore, +} from "@root/stores/tickets"; import { enqueueSnackbar } from "notistack"; import { useSSESubscription, useTickets } from "@frontend/kitui"; - export default function Support() { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const ticketId = useParams().ticketId; - const ticketApiPage = useTicketStore(state => state.apiPage); - const ticketsPerPage = useTicketStore(state => state.ticketsPerPage); - const token = useToken(); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const ticketId = useParams().ticketId; + const ticketApiPage = useTicketStore((state) => state.apiPage); + const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage); + const token = useToken(); - const fetchState = useTickets({ - url: "https://hub.pena.digital/heruvym/getTickets", - ticketsPerPage, - ticketApiPage, - onNewTickets: useCallback(result => { - if (result.data) updateTickets(result.data); - setTicketCount(result.count); - }, []), - onError: useCallback((error: Error) => { - const message = getMessageFromFetchError(error); - if (message) enqueueSnackbar(message); - }, []) - }); + const fetchState = useTickets({ + url: "https://hub.pena.digital/heruvym/getTickets", + ticketsPerPage, + ticketApiPage, + onNewTickets: useCallback((result) => { + if (result.data) updateTickets(result.data); + setTicketCount(result.count); + }, []), + onError: useCallback((error: Error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); + }, []), + }); - useSSESubscription({ - enabled: Boolean(token), - url: `https://admin.pena.digital/heruvym/subscribe?Authorization=${token}`, - onNewData: updateTickets, - onDisconnect: useCallback(() => { - clearTickets(); - }, []), - marker: "ticket" - }); + useSSESubscription({ + enabled: Boolean(token), + url: `https://admin.pena.digital/heruvym/subscribe?Authorization=${token}`, + onNewData: updateTickets, + onDisconnect: useCallback(() => { + clearTickets(); + }, []), + marker: "ticket", + }); - return ( - - {upMd && } - + {upMd && ( + + )} + + {!upMd && ( + + - {!upMd && ( - - - - )} - Запрос в службу техподдержки - - {ticketId ? ( - - ) : ( - - - - - )} - - ); + + + + )} + Запрос в службу техподдержки + + {ticketId ? ( + + ) : ( + + + + + )} + + ); } diff --git a/src/pages/Support/SupportChat.tsx b/src/pages/Support/SupportChat.tsx index 9e72d19..30b4ccb 100644 --- a/src/pages/Support/SupportChat.tsx +++ b/src/pages/Support/SupportChat.tsx @@ -134,6 +134,7 @@ export default function SupportChat() { borderRadius: "12px", p: upMd ? "20px" : undefined, gap: "40px", + height: !upMd ? "calc(100% - 90px)" : null, boxShadow: upMd ? cardShadow : undefined, diff --git a/src/pages/Tariffs/TariffCard.tsx b/src/pages/Tariffs/TariffCard.tsx index eac2a89..dbd7b80 100644 --- a/src/pages/Tariffs/TariffCard.tsx +++ b/src/pages/Tariffs/TariffCard.tsx @@ -96,12 +96,13 @@ export default function TariffCard({ {line} @@ -115,12 +116,13 @@ export default function TariffCard({ sx={{ minHeight: "calc(1.185*2em)", marginBottom: "auto", - height: "42px", + height: "65px", overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitBoxOrient: "vertical", - WebkitLineClamp: 2, + MozBoxOrient: "vertical", + WebkitLineClamp: 3, }} > {text} @@ -134,7 +136,7 @@ export default function TariffCard({ sx={{ color: theme.palette.brightPurple.main, borderColor: theme.palette.brightPurple.main, - mt: "35px", + mt: "10px", ...buttonProps.sx, }} > diff --git a/src/pages/Tariffs/TariffsPage.tsx b/src/pages/Tariffs/TariffsPage.tsx index 282dbf1..3b44d44 100644 --- a/src/pages/Tariffs/TariffsPage.tsx +++ b/src/pages/Tariffs/TariffsPage.tsx @@ -149,6 +149,7 @@ export default function TariffPage() { )} Date: Tue, 1 Aug 2023 15:13:29 +0300 Subject: [PATCH 02/41] fix: Tariffs --- src/pages/Tariffs/Tariffs.tsx | 224 ++++++++++++++++++---------------- 1 file changed, 120 insertions(+), 104 deletions(-) diff --git a/src/pages/Tariffs/Tariffs.tsx b/src/pages/Tariffs/Tariffs.tsx index 298fa24..edd1bca 100644 --- a/src/pages/Tariffs/Tariffs.tsx +++ b/src/pages/Tariffs/Tariffs.tsx @@ -12,112 +12,128 @@ import ImageTextButtonCard from "./ImageTextButtonCard"; import WideTemplCard from "@components/wideTemplCard"; import TemplCardPhoneLight from "@components/templCardPhoneLight"; - export default function Tariffs() { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const navigate = useNavigate(); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const navigate = useNavigate(); - return ( - + Выберите удобный тариф + + + } + headerText="Тарифы на время" + text="безлимит на 1 месяц , 3 , 6 , 12" + buttonProps={{ + text: "Подробнее", + onClick: () => navigate("time"), + }} + sx={{ maxWidth: "360px" }} + /> + + } + headerText="Тариф на объем" + text="200 шаблонов, 1000 шаблонов, 5000 шаблонов, 10 000 шаблонов" + buttonProps={{ + text: "Подробнее", + onClick: () => navigate("volume"), + }} + sx={{ maxWidth: "360px" }} + /> + + } + headerText="Кастом" + text="Текст-заполнитель — это текст, который имеет " + buttonProps={{ + text: "Подробнее", + onClick: () => navigate("/tariffconstructor"), + }} + sx={{ maxWidth: "360px" }} + /> + + + {`Или попробуйте наш `} + - Выберите удобный тариф - - } - headerText="Тарифы на время" - text="безлимит на 1 месяц , 3 , 6 , 12" - buttonProps={{ - text: "Подробнее", - onClick: () => navigate("time") - }} - sx={{ maxWidth: "360px" }} - /> - } - headerText="Тариф на объем" - text="200 шаблонов, 1000 шаблонов, 5000 шаблонов, 10 000 шаблонов" - buttonProps={{ - text: "Подробнее", - onClick: () => navigate("volume") - }} - sx={{ maxWidth: "360px" }} - /> - } - headerText="Кастом" - text="Текст-заполнитель — это текст, который имеет " - buttonProps={{ - text: "Подробнее", - onClick: () => navigate("/tariffconstructor") - }} - sx={{ maxWidth: "360px" }} - /> - - - {`Или попробуйте наш `} - - бесплатный план - - - - Наши продукты - + бесплатный план + + + + Наши продукты + - {upMd ? - - : - } - {/**/} - {/* */} - {/* */} - {/* */} - {/**/} - - ); + {upMd ? ( + + ) : ( + + )} + {/**/} + {/* */} + {/* */} + {/* */} + {/**/} + + ); } From 36572740c8bd12da55b942969d43fa437e067985 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Wed, 2 Aug 2023 11:31:58 +0300 Subject: [PATCH 03/41] fix: NavbarCollapsed --- src/components/Layout.tsx | 15 +- src/components/Navbar/DialogMenu.tsx | 195 ++++++--------------- src/components/Navbar/Navbar.tsx | 16 +- src/components/Navbar/NavbarCollapsed.tsx | 164 +++++++++++------ src/components/Navbar/NavbarFull.tsx | 203 ++++++++++++---------- src/pages/Support/Support.tsx | 2 +- src/pages/Support/SupportChat.tsx | 3 +- 7 files changed, 293 insertions(+), 305 deletions(-) diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 4ad3d38..370ae6b 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -1,13 +1,10 @@ import { Outlet } from "react-router-dom"; import Navbar from "./Navbar/Navbar"; - export default function Layout() { - - return ( - <> - - - - ); -} \ No newline at end of file + return ( + + + + ); +} diff --git a/src/components/Navbar/DialogMenu.tsx b/src/components/Navbar/DialogMenu.tsx index 5e5475a..a91a12e 100644 --- a/src/components/Navbar/DialogMenu.tsx +++ b/src/components/Navbar/DialogMenu.tsx @@ -1,29 +1,20 @@ import { useState } from "react"; -import { TransitionProps } from "@mui/material/transitions"; - -import logotip from "../../assets/Icons/logoPenaHab.svg"; -import logotipBlack from "../../assets/Icons/black_logo_PenaHab.svg"; -import CustomAvatar from "./Avatar"; -import CloseIcon from "../icons/CloseIcons"; -import React from "react"; +import { Link, useLocation } from "react-router-dom"; import { - AppBar, Box, Button, - Dialog, - IconButton, List, ListItem, - Slide, - Toolbar, Typography, useMediaQuery, useTheme, } from "@mui/material"; -import { Link, useLocation } from "react-router-dom"; + import { useUserStore } from "@root/stores/user"; import { currencyFormatter } from "@root/utils/currencyFormatter"; +import CustomAvatar from "./Avatar"; + type MenuItem = { name: string; url: string; @@ -46,22 +37,11 @@ const arrayMenu: MenuItem[] = [ { name: "История", url: "/history" }, ]; -const Transition = React.forwardRef(function Transition( - props: TransitionProps & { - children: React.ReactElement; - }, - - ref: React.Ref -) { - return ; -}); - interface DialogMenuProps { - open: boolean; handleClose: () => void; } -export default function DialogMenu({ open, handleClose }: DialogMenuProps) { +export default function DialogMenu({ handleClose }: DialogMenuProps) { const [activeSubMenuIndex, setActiveSubMenuIndex] = useState(-1); const theme = useTheme(); const location = useLocation(); @@ -81,64 +61,12 @@ export default function DialogMenu({ open, handleClose }: DialogMenuProps) { ); return ( - - - - - - - {isMobile && ( - - icon - - )} - - + ))} - {isMobile ? ( - location.pathname === "/" ? ( - - ) : ( - - - - - Мой баланс - - - {currencyFormatter.format(cash / 100)} - - - - ) + {location.pathname === "/" ? ( + ) : ( - icon + + + + Мой баланс + + + {currencyFormatter.format(cash / 100)} + + )} - + ); } diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index a191c67..de1c449 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -2,14 +2,24 @@ import { useMediaQuery, useTheme } from "@mui/material"; import NavbarCollapsed from "./NavbarCollapsed"; import NavbarFull from "./NavbarFull"; +import type { ReactNode } from "react"; + interface Props { - isCollapsed?: boolean; isLoggedIn: boolean; + children: ReactNode; } -export default function Navbar({ isLoggedIn, isCollapsed = false }: Props) { +export default function Navbar({ isLoggedIn, children }: Props) { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); - return upMd ? : ; + return ( + <> + {upMd ? ( + {children} + ) : ( + {children} + )} + + ); } diff --git a/src/components/Navbar/NavbarCollapsed.tsx b/src/components/Navbar/NavbarCollapsed.tsx index 2af3098..9a994b2 100644 --- a/src/components/Navbar/NavbarCollapsed.tsx +++ b/src/components/Navbar/NavbarCollapsed.tsx @@ -1,34 +1,44 @@ -import { useState } from "react"; -import { Badge, IconButton, useTheme } from "@mui/material"; -import MenuIcon from "@mui/icons-material/Menu"; +import { useState, useEffect } from "react"; +import { Box, Badge, Drawer, IconButton, useTheme } from "@mui/material"; import { Link } from "react-router-dom"; import SectionWrapper from "../SectionWrapper"; + import { useUserStore } from "@root/stores/user"; -import PenaLogo from "../PenaLogo"; -import DialogMenu from "./DialogMenu"; - +import MenuIcon from "@mui/icons-material/Menu"; import cartIcon from "@root/assets/Icons/cart.svg"; +import DialogMenu from "./DialogMenu"; +import PenaLogo from "../PenaLogo"; +import CloseIcon from "../icons/CloseIcons"; + +import type { ReactNode } from "react"; interface Props { isLoggedIn: boolean; + children: ReactNode; } -export default function NavbarCollapsed({ isLoggedIn }: Props) { +export default function NavbarCollapsed({ isLoggedIn, children }: Props) { const [open, setOpen] = useState(false); const userAccount = useUserStore((state) => state.userAccount); const theme = useTheme(); - const handleClickOpen = () => { - setOpen(true); - }; - const handleClose = () => { setOpen(false); }; + useEffect(() => { + if (open) { + document.body.style.overflow = "hidden"; + + return; + } + + document.body.style.overflow = "unset"; + }, [open]); + return ( - - - - setOpen((isOpened) => !isOpened)} + sx={{ p: 0, width: "30px", color: theme.palette.primary.main }} > - + ) : ( + + )} + + + - cart - - - - - - - + + cart + + + + + + + + + + + + + {children} + + ); } diff --git a/src/components/Navbar/NavbarFull.tsx b/src/components/Navbar/NavbarFull.tsx index d7dda09..de77edd 100644 --- a/src/components/Navbar/NavbarFull.tsx +++ b/src/components/Navbar/NavbarFull.tsx @@ -21,11 +21,14 @@ import { clearAuthToken, getMessageFromFetchError } from "@frontend/kitui"; import { clearCustomTariffs } from "@root/stores/customTariffs"; import { currencyFormatter } from "@root/utils/currencyFormatter"; +import type { ReactNode } from "react"; + interface Props { isLoggedIn: boolean; + children: ReactNode; } -export default function NavbarFull({ isLoggedIn }: Props) { +export default function NavbarFull({ isLoggedIn, children }: Props) { const theme = useTheme(); const navigate = useNavigate(); const location = useLocation(); @@ -45,104 +48,112 @@ export default function NavbarFull({ isLoggedIn }: Props) { } } - return isLoggedIn ? ( - - - - - - - - navigate("/wallet")} + return ( + + {isLoggedIn ? ( + - - - - + + + + - Мой баланс - - - {currencyFormatter.format(cash / 100)} - - - - - - - - - ) : ( - <> - - - - - - + + navigate("/wallet")} + > + + + + + Мой баланс + + + {currencyFormatter.format(cash / 100)} + + + + + + + + + ) : ( + <> + + + + + + + )} + {children} + ); } diff --git a/src/pages/Support/Support.tsx b/src/pages/Support/Support.tsx index 525c234..58f691e 100644 --- a/src/pages/Support/Support.tsx +++ b/src/pages/Support/Support.tsx @@ -60,7 +60,7 @@ export default function Support() { maxWidth="lg" sx={{ pt: upMd ? "25px" : "20px", - pb: upMd ? "82px" : "43px", + pb: upMd ? "82px" : "20px", height: "calc(100vh - 51px)", maxHeight: "calc(100vh - 51px)", }} diff --git a/src/pages/Support/SupportChat.tsx b/src/pages/Support/SupportChat.tsx index 30b4ccb..6509f36 100644 --- a/src/pages/Support/SupportChat.tsx +++ b/src/pages/Support/SupportChat.tsx @@ -17,6 +17,7 @@ import { getMessageFromFetchError, useEventListener, useSSESubscription, useTick export default function SupportChat() { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); + const isMobile = useMediaQuery(theme.breakpoints.up(460)); const [messageField, setMessageField] = useState(""); const tickets = useTicketStore(state => state.tickets); const messages = useMessageStore(state => state.messages); @@ -134,7 +135,7 @@ export default function SupportChat() { borderRadius: "12px", p: upMd ? "20px" : undefined, gap: "40px", - height: !upMd ? "calc(100% - 90px)" : null, + height: !upMd ? `calc(100% - ${isMobile ? 90 : 115}px)` : null, boxShadow: upMd ? cardShadow : undefined, From 4d0e3653d9c539bcfe6b0019c1c14ceafdd6610e Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Wed, 2 Aug 2023 14:34:48 +0300 Subject: [PATCH 04/41] fix: Select --- src/components/Navbar/NavbarCollapsed.tsx | 2 +- src/components/Select/index.tsx | 97 ++++++++++++++++------- src/components/Select/select.css | 20 ++--- 3 files changed, 82 insertions(+), 37 deletions(-) diff --git a/src/components/Navbar/NavbarCollapsed.tsx b/src/components/Navbar/NavbarCollapsed.tsx index 9a994b2..584a075 100644 --- a/src/components/Navbar/NavbarCollapsed.tsx +++ b/src/components/Navbar/NavbarCollapsed.tsx @@ -47,7 +47,6 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { backgroundColor: theme.palette.navbarbg.main, position: "sticky", top: 0, - zIndex: 1501, }} sx={{ height: "51px", padding: "0" }} > @@ -111,6 +110,7 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { sx={{ width: 210, position: "relative", + zIndex: open ? "none" : "-1", "& .MuiDrawer-paper": { position: "absolute", top: "0", diff --git a/src/components/Select/index.tsx b/src/components/Select/index.tsx index 796d040..417647b 100644 --- a/src/components/Select/index.tsx +++ b/src/components/Select/index.tsx @@ -1,12 +1,18 @@ -import { useState } from "react"; -import { Select as MuiSelect, MenuItem, useTheme } from "@mui/material"; +import { useState, useRef } from "react"; +import { + Select as MuiSelect, + MenuItem, + Box, + Typography, + useTheme, +} from "@mui/material"; import classnames from "classnames"; -import { cardShadow } from "@root/utils/themes/shadow"; +import checkIcon from "@root/assets/Icons/check.svg"; import "./select.css"; -import checkIcon from "@root/assets/Icons/check.svg"; +import type { SelectChangeEvent } from "@mui/material"; type SelectProps = { items: string[]; @@ -20,36 +26,73 @@ export const Select = ({ setSelectedItem, }: SelectProps) => { const [opened, setOpened] = useState(false); + const [currentValue, setCurrentValue] = useState(items[selectedItem]); + const ref = useRef(null); const theme = useTheme(); + const selectItem = (event: SelectChangeEvent) => { + setCurrentValue(items[Number(event.target.value)]); + setSelectedItem(Number(event.target.value)); + }; + return ( - setSelectedItem(Number(event.target.value))} - sx={{ - width: "100%", - color: theme.palette.brightPurple.main, - border: "2px solid #ffffff", - borderRadius: "30px", - fontWeight: "bold", - boxShadow: cardShadow, - }} - open={opened} - onClick={() => setOpened((isOpened) => !isOpened)} - IconComponent={() => ( + + ref.current?.click()} + > + + {currentValue} + check - )} - > - {items.map((item, index) => ( - - {item} - - ))} - + + setOpened((isOpened) => !isOpened)} + > + {items.map((item, index) => ( + + {item} + + ))} + + ); }; diff --git a/src/components/Select/select.css b/src/components/Select/select.css index 2ddd8e9..53cab65 100644 --- a/src/components/Select/select.css +++ b/src/components/Select/select.css @@ -1,10 +1,3 @@ -.select.MuiInputBase-root.MuiOutlinedInput-root { - z-index: 1500; - background: #ebebf2; - box-shadow: 0px 5px 40px rgba(210, 208, 225, 0.58), - 0px 2.76726px 8.55082px rgba(210, 208, 225, 0.4); -} - .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline { border: 0; } @@ -20,7 +13,16 @@ } .MuiPaper-root.MuiMenu-paper { - padding-top: 60px; - margin-top: -60px; + padding-top: 50px; + margin-top: -50px; border-radius: 28px; } + +.MuiInputBase-root.MuiOutlinedInput-root { + display: block; +} + +.MuiInputBase-root.MuiOutlinedInput-root > div:first-child, +.MuiInputBase-root.MuiOutlinedInput-root .MuiSelect-icon { + display: none; +} From 215b1e60eced7a1bfdcd52f2db71c2137a5f6cd3 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Wed, 2 Aug 2023 15:23:16 +0300 Subject: [PATCH 05/41] upgrade kitui package --- package.json | 2 +- src/utils/calcCart/calcCart.test.ts | 4 ++++ yarn.lock | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7a59c9a..a2f5b46 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dependencies": { "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", - "@frontend/kitui": "^1.0.16", + "@frontend/kitui": "^1.0.17", "@mui/icons-material": "^5.10.14", "@mui/material": "^5.10.14", "@popperjs/core": "^2.11.8", diff --git a/src/utils/calcCart/calcCart.test.ts b/src/utils/calcCart/calcCart.test.ts index a22f645..92fb178 100644 --- a/src/utils/calcCart/calcCart.test.ts +++ b/src/utils/calcCart/calcCart.test.ts @@ -656,6 +656,7 @@ const templategenTariff1: Tariff = { isCustom: false, privilegies: [ { + _id: "p1", name: "n1", privilegeId: "p1", serviceKey: "templategen", @@ -678,6 +679,7 @@ const templategenTariff2: Tariff = { isCustom: false, privilegies: [ { + _id: "p5", name: "n5", privilegeId: "p5", serviceKey: "templategen", @@ -700,6 +702,7 @@ const squizTariff: Tariff = { isCustom: false, privilegies: [ { + _id: "p2", name: "n2", privilegeId: "p2", serviceKey: "squiz", @@ -722,6 +725,7 @@ const reducerTariff: Tariff = { isCustom: false, privilegies: [ { + _id: "p3", name: "n3", privilegeId: "p3", serviceKey: "reducer", diff --git a/yarn.lock b/yarn.lock index 3e73b8b..fa6faaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1495,10 +1495,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@frontend/kitui@^1.0.16": - version "1.0.16" - resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/@frontend/kitui/-/@frontend/kitui-1.0.16.tgz#bd3f9912d02a983a30a985c7d8732df7bfb3390d" - integrity sha1-vT+ZEtAqmDowqYXH2HMt97+zOQ0= +"@frontend/kitui@^1.0.17": + version "1.0.17" + resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/@frontend/kitui/-/@frontend/kitui-1.0.17.tgz#a5bddaaa18b168be0e1814d5cfbd86e4030d15af" + integrity sha1-pb3aqhixaL4OGBTVz72G5AMNFa8= dependencies: immer "^10.0.2" reconnecting-eventsource "^1.6.2" From 6eef2bf1ec3b38db867688677a6bf3697bf7f145 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Thu, 3 Aug 2023 15:14:31 +0300 Subject: [PATCH 06/41] feat: bell --- src/assets/Icons/bell.svg | 4 + src/assets/Icons/exclamation_point.svg | 5 + src/assets/Icons/wallet_icon.svg | 3 + src/components/Drawers.tsx | 87 +++++++----- src/components/Navbar/DialogMenu.tsx | 2 +- src/components/Navbar/NavbarCollapsed.tsx | 57 +++++++- src/components/Navbar/NavbarFull.tsx | 23 +-- src/components/icons/WalletIcon.tsx | 10 +- src/pages/Support/Support.tsx | 27 ++-- src/pages/Support/TicketList/TicketCard.tsx | 47 +++++-- src/pages/Support/TicketList/TicketList.tsx | 147 +++++++++++--------- 11 files changed, 271 insertions(+), 141 deletions(-) create mode 100644 src/assets/Icons/bell.svg create mode 100644 src/assets/Icons/exclamation_point.svg create mode 100644 src/assets/Icons/wallet_icon.svg diff --git a/src/assets/Icons/bell.svg b/src/assets/Icons/bell.svg new file mode 100644 index 0000000..ca75e06 --- /dev/null +++ b/src/assets/Icons/bell.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/Icons/exclamation_point.svg b/src/assets/Icons/exclamation_point.svg new file mode 100644 index 0000000..15da3f5 --- /dev/null +++ b/src/assets/Icons/exclamation_point.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/Icons/wallet_icon.svg b/src/assets/Icons/wallet_icon.svg new file mode 100644 index 0000000..75ed2b7 --- /dev/null +++ b/src/assets/Icons/wallet_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index 33f55ad..cc1c4a3 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -1,3 +1,4 @@ +import { useCallback } from "react"; import { Typography, Drawer, @@ -12,6 +13,7 @@ import { import { IconsCreate } from "@root/lib/IconsCreate"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import ClearIcon from "@mui/icons-material/Clear"; +import { useTickets } from "@frontend/kitui"; import BasketIcon from "../assets/Icons/BasketIcon.svg"; import SectionWrapper from "./SectionWrapper"; import CustomWrapperDrawer from "./CustomWrapperDrawer"; @@ -26,6 +28,13 @@ import { } from "@root/stores/cart"; import { useCustomTariffsStore } from "@root/stores/customTariffs"; import { useUserStore } from "@root/stores/user"; +import { + updateTickets, + setTicketCount, + useTicketStore, +} from "@root/stores/tickets"; + +import bellIcon from "@root/assets/Icons/bell.svg"; export default function Drawers() { const navigate = useNavigate(); @@ -40,6 +49,20 @@ export default function Drawers() { (state) => state.summaryPriceAfterDiscountsMap ); const userAccount = useUserStore((state) => state.userAccount); + const ticketCount = useTicketStore((state) => state.ticketCount); + const ticketApiPage = useTicketStore((state) => state.apiPage); + const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage); + + useTickets({ + url: "https://hub.pena.digital/heruvym/getTickets", + ticketsPerPage, + ticketApiPage, + onNewTickets: useCallback((result) => { + if (result.data) updateTickets(result.data); + setTicketCount(result.count); + }, []), + onError: () => {}, + }); const basePrice = Object.values(summaryPriceBeforeDiscountsMap).reduce( (a, e) => a + e, @@ -54,12 +77,36 @@ export default function Drawers() { const totalPriceAfterDiscounts = cart.priceAfterDiscounts + discountedPrice; return ( - - + + + cart + + + - - {cart.itemCount && ( - - - {cart.itemCount} - - - )} + - + ); } diff --git a/src/components/Navbar/DialogMenu.tsx b/src/components/Navbar/DialogMenu.tsx index a91a12e..6d2a2d5 100644 --- a/src/components/Navbar/DialogMenu.tsx +++ b/src/components/Navbar/DialogMenu.tsx @@ -61,7 +61,7 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) { ); return ( - + state.userAccount); + const ticketCount = useTicketStore((state) => state.ticketCount); + const ticketApiPage = useTicketStore((state) => state.apiPage); + const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage); + + useTickets({ + url: "https://hub.pena.digital/heruvym/getTickets", + ticketsPerPage, + ticketApiPage, + onNewTickets: useCallback((result) => { + if (result.data) updateTickets(result.data); + setTicketCount(result.count); + }, []), + onError: () => {}, + }); const theme = useTheme(); @@ -61,7 +82,11 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { > setOpen((isOpened) => !isOpened)} - sx={{ p: 0, width: "30px", color: theme.palette.primary.main }} + sx={{ + p: 0, + width: "30px", + color: theme.palette.primary.main, + }} > {open ? ( @@ -73,6 +98,8 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { + + + cart + + diff --git a/src/components/Navbar/NavbarFull.tsx b/src/components/Navbar/NavbarFull.tsx index de77edd..0009b95 100644 --- a/src/components/Navbar/NavbarFull.tsx +++ b/src/components/Navbar/NavbarFull.tsx @@ -9,7 +9,6 @@ import { } from "@mui/material"; import SectionWrapper from "../SectionWrapper"; import LogoutIcon from "../icons/LogoutIcon"; -import WalletIcon from "../icons/WalletIcon"; import CustomAvatar from "./Avatar"; import Drawers from "../Drawers"; import PenaLogo from "../PenaLogo"; @@ -19,8 +18,11 @@ import { enqueueSnackbar } from "notistack"; import { clearUserData, useUserStore } from "@root/stores/user"; import { clearAuthToken, getMessageFromFetchError } from "@frontend/kitui"; import { clearCustomTariffs } from "@root/stores/customTariffs"; + import { currencyFormatter } from "@root/utils/currencyFormatter"; +import walletIcon from "@root/assets/Icons/wallet_icon.svg"; + import type { ReactNode } from "react"; interface Props { @@ -69,18 +71,21 @@ export default function NavbarFull({ isLoggedIn, children }: Props) { - + navigate("/wallet")} > - + wallet - + - {!upMd && ( - - - - - - )} - Запрос в службу техподдержки + + + + + Запрос в службу техподдержки + {ticketId ? ( diff --git a/src/pages/Support/TicketList/TicketCard.tsx b/src/pages/Support/TicketList/TicketCard.tsx index 45599a3..7933fe0 100644 --- a/src/pages/Support/TicketList/TicketCard.tsx +++ b/src/pages/Support/TicketList/TicketCard.tsx @@ -1,20 +1,23 @@ -import { Link as RouterLink } from "react-router-dom"; import { useMemo } from "react"; +import { Link as RouterLink } from "react-router-dom"; import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; import CustomButton from "@components/CustomButton"; + import { cardShadow } from "@root/utils/themes/shadow"; -interface Props { - name: string; - body: string; - time: string; - ticketId: string; -} +import ExclamationPointIcon from "@root/assets/Icons/exclamation_point.svg"; -export default function TicketCard({ name, body, time, ticketId }: Props) { +import type { Ticket } from "@frontend/kitui"; + +type TicketCardProps = { + ticket: Ticket; +}; + +export default function TicketCard({ ticket }: TicketCardProps) { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); + const adminReplied = ticket.user !== ticket.top_message.user_id; const timeText = useMemo( () => ( @@ -28,10 +31,10 @@ export default function TicketCard({ name, body, time, ticketId }: Props) { mb: "5px", }} > - {time} + {new Date(ticket.updated_at).toLocaleDateString()} ), - [theme.palette.grey2.main, time] + [theme.palette.grey2.main, ticket] ); return ( @@ -48,8 +51,22 @@ export default function TicketCard({ name, body, time, ticketId }: Props) { boxShadow: cardShadow, }} > - {!upMd && timeText} + {!upMd && {timeText}} + {adminReplied && ( + + ExclamationPoint + Вам ответили + + )} - {name} + {ticket.title} + + + {ticket.top_message.message} - {body} state.tickets); - const ticketCount = useTicketStore(state => state.ticketCount); - const ticketApiPage = useTicketStore(state => state.apiPage); - const ticketsPerPage = useTicketStore(state => state.ticketsPerPage); + const theme = useTheme(); + const tickets = useTicketStore((state) => state.tickets); + const ticketCount = useTicketStore((state) => state.ticketCount); + const ticketApiPage = useTicketStore((state) => state.apiPage); + const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage); - const sortedTickets = tickets.sort(sortTicketsByUpdateTime).slice(ticketApiPage * ticketsPerPage, (ticketApiPage + 1) * ticketsPerPage); - - return ( - - - {sortedTickets.map((ticket) => ( - - - - ))} - {fetchState === "fetching" && ( - - - - )} - - {ticketCount > ticketsPerPage && - setTicketApiPage(value - 1)} - sx={{ alignSelf: "center" }} - /> - } - + const sortedTickets = tickets + .sort(sortTicketsByUpdateTime) + .slice( + ticketApiPage * ticketsPerPage, + (ticketApiPage + 1) * ticketsPerPage ); + + return ( + + + {sortedTickets.map((ticket) => ( + + + + ))} + {fetchState === "fetching" && ( + + + + )} + + {ticketCount > ticketsPerPage && ( + setTicketApiPage(value - 1)} + sx={{ alignSelf: "center" }} + /> + )} + + ); } function sortTicketsByUpdateTime(ticket1: Ticket, ticket2: Ticket) { - const date1 = new Date(ticket1.updated_at).getTime(); - const date2 = new Date(ticket2.updated_at).getTime(); - return date2 - date1; -} \ No newline at end of file + const date1 = new Date(ticket1.updated_at).getTime(); + const date2 = new Date(ticket2.updated_at).getTime(); + return date2 - date1; +} From df1da22cffe7968ef9f144cb9e731ce91e3cf6d4 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Thu, 3 Aug 2023 15:19:02 +0300 Subject: [PATCH 07/41] fix: TicketCard --- src/pages/Support/TicketList/TicketCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Support/TicketList/TicketCard.tsx b/src/pages/Support/TicketList/TicketCard.tsx index 7933fe0..5e4659a 100644 --- a/src/pages/Support/TicketList/TicketCard.tsx +++ b/src/pages/Support/TicketList/TicketCard.tsx @@ -59,7 +59,7 @@ export default function TicketCard({ ticket }: TicketCardProps) { display: "flex", alignItems: "center", columnGap: "10px", - marginTop: "-20px", + marginTop: !upMd ? "-20px" : null, marginBottom: "20px", }} > From f18184a849ab507a2c5821e64b3c039c8e0dd37c Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Thu, 3 Aug 2023 17:27:48 +0300 Subject: [PATCH 08/41] refactor: useless logic removed --- src/components/AccordionWrapper.tsx | 28 ---- src/components/CustomAccordionBasket.tsx | 154 ------------------ src/components/CustomRadioButtons.tsx | 46 ------ src/components/Drawers.tsx | 1 - src/components/StepperSquiz.tsx | 24 --- src/components/VideoGIF.tsx | 8 - src/components/icons/UploadIcon.tsx | 2 +- src/components/icons/WalletIcon.tsx | 4 +- src/components/templCardPhoneLight.tsx | 7 - src/components/templCardPhonePink.tsx | 39 +++-- src/components/wideTemplCard.tsx | 18 +- src/model/auth.ts | 1 - src/pages/Landing/Section2.tsx | 6 - src/pages/SavedTariffs/index.tsx | 10 +- src/pages/TariffConstructor/Summary.tsx | 82 ---------- src/pages/Tariffs/ImageTextButtonCard.tsx | 60 ------- src/pages/Tariffs/TariffCardTimeAndVolume.tsx | 93 ----------- src/pages/Tariffs/Tariffs.tsx | 32 +--- 18 files changed, 30 insertions(+), 585 deletions(-) delete mode 100644 src/components/AccordionWrapper.tsx delete mode 100644 src/components/CustomAccordionBasket.tsx delete mode 100644 src/components/CustomRadioButtons.tsx delete mode 100644 src/components/StepperSquiz.tsx delete mode 100644 src/components/VideoGIF.tsx delete mode 100644 src/pages/TariffConstructor/Summary.tsx delete mode 100644 src/pages/Tariffs/ImageTextButtonCard.tsx delete mode 100644 src/pages/Tariffs/TariffCardTimeAndVolume.tsx diff --git a/src/components/AccordionWrapper.tsx b/src/components/AccordionWrapper.tsx deleted file mode 100644 index 1cf9cfb..0000000 --- a/src/components/AccordionWrapper.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Box } from "@mui/material"; -import CustomAccordionBasket from "./CustomAccordionBasket"; -import { cardShadow } from "@root/utils/themes/shadow"; - -interface Props { - content: { title: string; data: [string, number][] }[]; -} - -export default function AccordionWrapperBasket({ content }: Props) { - return ( - - {content.map((accordionItem, index) => ( - - ))} - - ); -} diff --git a/src/components/CustomAccordionBasket.tsx b/src/components/CustomAccordionBasket.tsx deleted file mode 100644 index 260295c..0000000 --- a/src/components/CustomAccordionBasket.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { Box, SvgIcon, Typography, useMediaQuery, useTheme } from "@mui/material"; -import { useState } from "react"; -import ClearIcon from "@mui/icons-material/Clear"; -import ExpandIcon from "./icons/ExpandIcon"; - -interface Props { - header: string; - totalPrice: number; - dataSection: [string, number][]; -} - -function TotalSum(mass: [string, number][]): number { - let sum: number = 0; - mass.forEach((element) => { - sum += element[1]; - }); - return sum; -} - -export default function CustomAccordionBasket({ header, totalPrice, dataSection }: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const upSm = useMediaQuery(theme.breakpoints.up("sm")); - const [isExpanded, setIsExpanded] = useState(false); - let sum: number = TotalSum(dataSection); - - return ( - - setIsExpanded((prev) => !prev)} - sx={{ - height: "72px", - px: "20px", - - display: "flex", - alignItems: "center", - justifyContent: "space-between", - cursor: "pointer", - userSelect: "none", - }} - > - - {header} - - - - - {sum} руб. - - - - - - - {isExpanded && - dataSection.map((item, index) => { - return ( - - - {item[0]} - - - - {item[1]} руб. - - {upSm ? ( - - Удалить - - ) : ( - - )} - - - ); - })} - - ); -} diff --git a/src/components/CustomRadioButtons.tsx b/src/components/CustomRadioButtons.tsx deleted file mode 100644 index 31aff55..0000000 --- a/src/components/CustomRadioButtons.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Dispatch, SetStateAction, useState } from "react"; -import { List, ListItem, Typography, useMediaQuery, useTheme } from "@mui/material"; - -type Props = { - setType: Dispatch>; - _mocsk_: { name: string; type: "templ" | "squiz" | "reducer" }[]; -}; - -export default function CustomRadioButtons({ setType, _mocsk_ }: Props) { - const theme = useTheme(); - const upSm = useMediaQuery(theme.breakpoints.up("sm")); - const [active, setActive] = useState(0); - - const activeType = (index: number, type: "templ" | "squiz" | "reducer") => { - setActive(index); - setType(type); - }; - - return ( - - {_mocsk_.map(({ name, type }, index) => - active === index ? ( - activeType(index, type)} sx={{ color: "#7E2AEA", cursor: "pointer" }}> - - {name} - - - ) : ( - activeType(index, type)} sx={{ cursor: "pointer" }}> - {name} - - ) - )} - - ); -} diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index cc1c4a3..013b0a5 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -7,7 +7,6 @@ import { Box, IconButton, SvgIcon, - Icon, Badge, } from "@mui/material"; import { IconsCreate } from "@root/lib/IconsCreate"; diff --git a/src/components/StepperSquiz.tsx b/src/components/StepperSquiz.tsx deleted file mode 100644 index 0e0c39d..0000000 --- a/src/components/StepperSquiz.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Typography } from "@mui/material"; -import { useNavigate } from "react-router"; - -type Props = { - text: string; -}; - -export default function StepperSquiz({ text }: Props) { - const navigate = useNavigate(); - return ( - - navigate("/tariffs")} - component="div" - sx={{ cursor: "pointer", fontWeight: "400px", fontSize: "12px", lineHeight: "14px", marginBottom: "19px" }} - > - Все тарифы — - - - {text} - - - ); -} diff --git a/src/components/VideoGIF.tsx b/src/components/VideoGIF.tsx deleted file mode 100644 index e376eff..0000000 --- a/src/components/VideoGIF.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from "react"; -interface Props { - - style: {width:string,height:string} -} -export default function (props:Props) { - -} diff --git a/src/components/icons/UploadIcon.tsx b/src/components/icons/UploadIcon.tsx index 6c0add8..4adbbfa 100644 --- a/src/components/icons/UploadIcon.tsx +++ b/src/components/icons/UploadIcon.tsx @@ -1,7 +1,7 @@ import { Box } from "@mui/material"; -export default function UploadIcon() { +export default function SendIcon() { return ( - + backgroundColor: '"#E6E6EB', + }} + > + -} \ No newline at end of file + ); +} diff --git a/src/components/wideTemplCard.tsx b/src/components/wideTemplCard.tsx index 66ac471..942d12e 100644 --- a/src/components/wideTemplCard.tsx +++ b/src/components/wideTemplCard.tsx @@ -1,19 +1,6 @@ -import { - Box, - Typography, - useMediaQuery, - useTheme, - Button, - SxProps, - Theme, -} from "@mui/material"; +import { Box, Typography, Button, SxProps, Theme } from "@mui/material"; import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; -import CardWithLink from "@components/CardWithLink"; import UnderlinedLink from "@components/UnderlinedLink"; -import SectionWrapper from "@components/SectionWrapper"; -import card1Image from "@root/assets/landing/card1.png"; -import card2Image from "@root/assets/landing/card2.png"; -import card3Image from "@root/assets/landing/card3.png"; import cardImageBig from "@root/assets/landing/card1big.png"; interface Props { @@ -22,9 +9,6 @@ interface Props { } export default function ({ light = true, sx }: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - return ( state.summaryPriceBeforeDiscountsMap); - const summaryPriceAfterDiscountsMap = useCustomTariffsStore(state => state.summaryPriceAfterDiscountsMap); - - const basePrice = Object.values(summaryPriceBeforeDiscountsMap).reduce((a, e) => a + e, 0); - const discountedPrice = Object.values(summaryPriceAfterDiscountsMap).reduce((a, e) => a + e, 0); - - - return ( - - - - Итоговая цена - - - Текст-заполнитель — это текст, который имеет Текст-заполнитель — это текст, который имеет Текст-заполнитель - — это текст, который имеет Текст-заполнитель — это текст, который имеет Текст-заполнитель - - - - - - {currencyFormatter.format(basePrice / 100)} руб. - - - {currencyFormatter.format(discountedPrice / 100)} руб. - - - - Выбрать - - - - ); -} diff --git a/src/pages/Tariffs/ImageTextButtonCard.tsx b/src/pages/Tariffs/ImageTextButtonCard.tsx deleted file mode 100644 index 97fc73f..0000000 --- a/src/pages/Tariffs/ImageTextButtonCard.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { Box, SxProps, Theme, Typography, useTheme } from "@mui/material"; -import CustomButton from "@root/components/CustomButton"; - - -interface Props { - image?: string; - headerText: string; - text: string; - linkHref: string; - sx?: SxProps; -} - -export default function ImageTextButtonCard({ image, headerText, text, linkHref, sx }: Props) { - const theme = useTheme(); - - return ( - - {image && - - } - {headerText} - {text} - - Подробнее - - - ); -} \ No newline at end of file diff --git a/src/pages/Tariffs/TariffCardTimeAndVolume.tsx b/src/pages/Tariffs/TariffCardTimeAndVolume.tsx deleted file mode 100644 index d5e2560..0000000 --- a/src/pages/Tariffs/TariffCardTimeAndVolume.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { Box, SxProps, Theme, useTheme } from "@mui/material"; -import Typography from "@mui/material/Typography"; - -import CustomButton from "@components/CustomButton"; - -interface Props { - icon: React.ReactNode; - headerText: string; - text: string; - money?: number; - sx: SxProps; - href: string; - buttonBorderColor?: string; - buttonTextColor?: string; - moneyColor?: string; - onclick: () => void; - textButton: string; -} - -export default function TariffCardTimeAndVolume({ - icon, - headerText, - text, - sx, - href, - buttonBorderColor, - buttonTextColor, - money = 0, - moneyColor, - onclick, - textButton, -}: Props) { - const theme = useTheme(); - - return ( - - - {icon} - - {money} руб. - - - - - {headerText} - - - {text} - - - {textButton} - - - ); -} diff --git a/src/pages/Tariffs/Tariffs.tsx b/src/pages/Tariffs/Tariffs.tsx index edd1bca..caada69 100644 --- a/src/pages/Tariffs/Tariffs.tsx +++ b/src/pages/Tariffs/Tariffs.tsx @@ -1,14 +1,10 @@ -import { Outlet, Route, Routes, useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import { useMediaQuery, useTheme, Box, Link, Typography } from "@mui/material"; import SectionWrapper from "@components/SectionWrapper"; import CustomIcon from "@components/icons/CustomIcon"; import CalendarIcon from "@components/icons/CalendarIcon"; import PieChartIcon from "@components/icons/PieChartIcon"; import TariffCard from "./TariffCard"; -import card1Image from "@root/assets/landing/card1.png"; -import card2Image from "@root/assets/landing/card2.png"; -import card3Image from "@root/assets/landing/card3.png"; -import ImageTextButtonCard from "./ImageTextButtonCard"; import WideTemplCard from "@components/wideTemplCard"; import TemplCardPhoneLight from "@components/templCardPhoneLight"; @@ -108,32 +104,6 @@ export default function Tariffs() { ) : ( )} - {/**/} - {/* */} - {/* */} - {/* */} - {/**/} ); } From b064649ed7c0337ba45479c88f460dd2328486f5 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Thu, 3 Aug 2023 18:01:28 +0300 Subject: [PATCH 09/41] fix: cart --- src/components/Drawers.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index 013b0a5..0f8c6ae 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -124,8 +124,8 @@ export default function Drawers() { color: "#FFFFFF", background: theme.palette.brightPurple.main, transform: "scale(0.8) translate(50%, -50%)", - top: "10px", - right: "10px", + top: userAccount?.cart.length ? "10px" : "2px", + right: userAccount?.cart.length ? "10px" : "2px", fontWeight: 400, }, }} From 13f2c4048bee99ae4bb1da7058d0ec6a8368683a Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Fri, 4 Aug 2023 11:21:15 +0300 Subject: [PATCH 10/41] fix: icons and inputs --- src/components/ComplexNavText.tsx | 46 ---- src/components/Drawers.tsx | 18 +- src/components/InputTextfield.tsx | 165 ++++++------ src/components/Navbar/NavbarCollapsed.tsx | 20 +- src/components/Select/index.tsx | 2 +- src/components/passwordInput.tsx | 237 +++++++++--------- src/pages/Basket/Basket.tsx | 2 - src/pages/Faq/Faq.tsx | 4 - src/pages/History/index.tsx | 2 - src/pages/Payment/Payment.tsx | 2 - src/pages/SavedTariffs/index.tsx | 7 - src/pages/Support/Support.tsx | 7 - .../TariffConstructor/TariffConstructor.tsx | 2 - src/pages/Tariffs/TariffsPage.tsx | 4 - src/pages/Wallet.tsx | 2 - 15 files changed, 240 insertions(+), 280 deletions(-) delete mode 100644 src/components/ComplexNavText.tsx diff --git a/src/components/ComplexNavText.tsx b/src/components/ComplexNavText.tsx deleted file mode 100644 index 82c04da..0000000 --- a/src/components/ComplexNavText.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Typography, useTheme } from "@mui/material"; -import { useNavigate } from "react-router-dom"; - -interface Props { - text1: string; - text2?: string; -} - -export default function ComplexNavText({ text1, text2 }: Props) { - const theme = useTheme(); - const navigate = useNavigate(); - - return ( - - navigate("/tariffs")} - sx={{ - cursor: "pointer", - fontWeight: 400, - fontSize: "12px", - lineHeight: "14px", - color: theme.palette.grey2.main, - }} - > - {text1} - - {text2 && - - {text2} - - } - - ); -} diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index 0f8c6ae..7f8bbb4 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -80,15 +80,26 @@ export default function Drawers() { ; - TextfieldProps: TextFieldProps; - onChange: (e: React.ChangeEvent) => void; + id: string; + label?: string; + bold?: boolean; + gap?: string; + color?: string; + FormInputSx?: SxProps; + TextfieldProps: TextFieldProps; + onChange: ( + e: React.ChangeEvent + ) => void; } export default function InputTextfield({ - id, - label, - bold = false, - gap = "10px", - onChange, - TextfieldProps, - color, - FormInputSx, + id, + label, + bold = false, + gap = "10px", + onChange, + TextfieldProps, + color, + FormInputSx, }: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); - const labelFont = upMd - ? bold - ? theme.typography.p1 - : { ...theme.typography.body1, fontWeight: 500 } - : theme.typography.body2; + const labelFont = upMd + ? bold + ? theme.typography.p1 + : { ...theme.typography.body1, fontWeight: 500 } + : theme.typography.body2; - const placeholderFont = upMd ? undefined : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" }; + const placeholderFont = upMd + ? undefined + : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" }; - return ( - + {label && ( + - {label && - - {label} - - } - - - ); + {label} + + )} + + + ); } diff --git a/src/components/Navbar/NavbarCollapsed.tsx b/src/components/Navbar/NavbarCollapsed.tsx index 716f66a..73b851a 100644 --- a/src/components/Navbar/NavbarCollapsed.tsx +++ b/src/components/Navbar/NavbarCollapsed.tsx @@ -115,11 +115,12 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { badgeContent={userAccount?.cart.length} sx={{ "& .MuiBadge-badge": { + display: userAccount?.cart.length ? "flex" : "none", color: "#FFFFFF", background: theme.palette.brightPurple.main, - transform: "scale(0.8) translate(50%, -50%)", + transform: "scale(0.7) translate(50%, -50%)", top: "2px", - right: "2px", + right: "3px", fontWeight: 400, }, }} @@ -135,18 +136,25 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { height: "30px", background: theme.palette.background.default, borderRadius: "6px", - "&:hover": { background: theme.palette.background.default }, + "&:hover": { + background: theme.palette.brightPurple.main, + "& .MuiBadge-badge": { + background: theme.palette.background.default, + color: theme.palette.brightPurple.main, + }, + }, }} > setOpened((isOpened) => !isOpened)} > {items.map((item, index) => ( - + {item} ))} diff --git a/src/components/passwordInput.tsx b/src/components/passwordInput.tsx index f7e1b5b..31293d1 100644 --- a/src/components/passwordInput.tsx +++ b/src/components/passwordInput.tsx @@ -1,121 +1,134 @@ import { - FormControl, - IconButton, - InputLabel, - SxProps, - TextField, - TextFieldProps, - Theme, - useMediaQuery, - useTheme, - } from "@mui/material"; - import * as React from 'react'; - import InputAdornment from '@mui/material/InputAdornment'; - import Visibility from '@mui/icons-material/Visibility'; - import VisibilityOff from '@mui/icons-material/VisibilityOff'; - - interface Props { - id: string; - label?: string; - bold?: boolean; - gap?: string; - color?: string; - FormInputSx?: SxProps; - TextfieldProps: TextFieldProps; - onChange: (e: React.ChangeEvent) => void; - } - - export default function ({ - id, - label, - bold = false, - gap = "10px", - onChange, - TextfieldProps, - color, - FormInputSx, - }: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - - const labelFont = upMd - ? bold - ? theme.typography.p1 - : { ...theme.typography.body1, fontWeight: 500 } - : theme.typography.body2; - - const placeholderFont = upMd ? undefined : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" }; + FormControl, + IconButton, + InputLabel, + SxProps, + TextField, + TextFieldProps, + Theme, + useMediaQuery, + useTheme, +} from "@mui/material"; +import * as React from "react"; +import InputAdornment from "@mui/material/InputAdornment"; +import Visibility from "@mui/icons-material/Visibility"; +import VisibilityOff from "@mui/icons-material/VisibilityOff"; - const [showPassword, setShowPassword] = React.useState(false); +interface Props { + id: string; + label?: string; + bold?: boolean; + gap?: string; + color?: string; + FormInputSx?: SxProps; + TextfieldProps: TextFieldProps; + onChange: ( + e: React.ChangeEvent + ) => void; +} - const handleClickShowPassword = () => setShowPassword((show) => !show); +export default function PasswordInput({ + id, + label, + bold = false, + gap = "10px", + onChange, + TextfieldProps, + color, + FormInputSx, +}: Props) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); - const handleMouseDownPassword = (event: React.MouseEvent) => { - event.preventDefault(); - }; - - return ( - - setShowPassword((show) => !show); + + const handleMouseDownPassword = ( + event: React.MouseEvent + ) => { + event.preventDefault(); + }; + + return ( + + - + + + {showPassword ? : } + + + ), + sx: { + padding: "0px", + border: "1px solid" + theme.palette.grey2.main, + backgroundColor: color, + borderRadius: "8px", + height: "48px", color: "black", - transform: "none", - ...labelFont, - }} - > - {label} - - - - {showPassword ? : } - - - ), - sx: { - border: "1px solid" + theme.palette.grey2.main, - backgroundColor: color, - borderRadius: "8px", - height: "48px", - py: 0, - color: "black", - ...placeholderFont, - }, - }} - onChange={onChange} - type={showPassword ? 'text' : 'password'} - /> - - - ); - } - \ No newline at end of file + }, + }} + onChange={onChange} + type={showPassword ? "text" : "password"} + /> + + ); +} diff --git a/src/pages/Basket/Basket.tsx b/src/pages/Basket/Basket.tsx index 5fd6e2b..be7bd5e 100644 --- a/src/pages/Basket/Basket.tsx +++ b/src/pages/Basket/Basket.tsx @@ -3,7 +3,6 @@ import SectionWrapper from "@components/SectionWrapper"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import TotalPrice from "@components/TotalPrice"; import CustomWrapper from "./CustomWrapper"; -import ComplexNavText from "@root/components/ComplexNavText"; import { useCart } from "@root/utils/hooks/useCart"; import { useCustomTariffsStore } from "@root/stores/customTariffs"; @@ -29,7 +28,6 @@ export default function Basket() { mb: upMd ? "70px" : "37px", }} > - {upMd && } - {upMd && ( - - )} - {upMd && } - {upMd && } - {upMd && ( - - )} - {upMd && ( - - )} - {upMd && } - {upMd && ( - - )} {StepperText[unit]} diff --git a/src/pages/Wallet.tsx b/src/pages/Wallet.tsx index d4630cd..ccb71f1 100644 --- a/src/pages/Wallet.tsx +++ b/src/pages/Wallet.tsx @@ -4,7 +4,6 @@ import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import CustomButton from "@components/CustomButton"; import WalletIcon from "@components/icons/WalletIcon"; import SectionWrapper from "@components/SectionWrapper"; -import ComplexNavText from "@components/ComplexNavText"; import { cardShadow } from "@root/utils/themes/shadow"; import { currencyFormatter } from "@root/utils/currencyFormatter"; import { useUserStore } from "@root/stores/user"; @@ -58,7 +57,6 @@ export default function Wallet() { mb: "70px", }} > - {upMd && } Date: Fri, 4 Aug 2023 12:34:14 +0300 Subject: [PATCH 11/41] fix: cart and bell icon hover colors --- src/assets/Icons/bell.svg | 4 ++-- src/components/Drawers.tsx | 25 +++++++++++++++-------- src/components/Navbar/NavbarCollapsed.tsx | 14 +++++++++---- src/lib/IconsCreate.tsx | 23 --------------------- 4 files changed, 28 insertions(+), 38 deletions(-) delete mode 100644 src/lib/IconsCreate.tsx diff --git a/src/assets/Icons/bell.svg b/src/assets/Icons/bell.svg index ca75e06..a6f1b8d 100644 --- a/src/assets/Icons/bell.svg +++ b/src/assets/Icons/bell.svg @@ -1,4 +1,4 @@ - - + + diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index 7f8bbb4..56659c9 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -9,11 +9,9 @@ import { SvgIcon, Badge, } from "@mui/material"; -import { IconsCreate } from "@root/lib/IconsCreate"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import ClearIcon from "@mui/icons-material/Clear"; import { useTickets } from "@frontend/kitui"; -import BasketIcon from "../assets/Icons/BasketIcon.svg"; import SectionWrapper from "./SectionWrapper"; import CustomWrapperDrawer from "./CustomWrapperDrawer"; import CustomButton from "./CustomButton"; @@ -33,7 +31,8 @@ import { useTicketStore, } from "@root/stores/tickets"; -import bellIcon from "@root/assets/Icons/bell.svg"; +import { ReactComponent as BellIcon } from "@root/assets/Icons/bell.svg"; +import { ReactComponent as CartIcon } from "@root/assets/Icons/cart.svg"; export default function Drawers() { const navigate = useNavigate(); @@ -92,6 +91,8 @@ export default function Drawers() { background: theme.palette.background.default, color: theme.palette.brightPurple.main, }, + "& svg > path:first-child": { fill: "#FFFFFF" }, + "& svg > path:last-child": { stroke: "#FFFFFF" }, }, }} > @@ -109,15 +110,18 @@ export default function Drawers() { }, }} > - cart + - path:nth-child(1)": { fill: "#FFFFFF" }, + "& svg > path:nth-child(2)": { fill: "#FFFFFF" }, + "& svg > path:nth-child(3)": { stroke: "#FFFFFF" }, }, }} > @@ -136,15 +143,15 @@ export default function Drawers() { color: "#FFFFFF", background: theme.palette.brightPurple.main, transform: "scale(0.8) translate(50%, -50%)", - top: "10px", - right: "10px", + top: "2px", + right: "2px", fontWeight: 400, }, }} > - + - + path:nth-child(1)": { fill: "#FFFFFF" }, + "& svg > path:nth-child(2)": { fill: "#FFFFFF" }, + "& svg > path:nth-child(3)": { stroke: "#FFFFFF" }, }, }} > @@ -125,7 +129,7 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { }, }} > - cart + @@ -142,6 +146,8 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { background: theme.palette.background.default, color: theme.palette.brightPurple.main, }, + "& svg > path:first-child": { fill: "#FFFFFF" }, + "& svg > path:last-child": { stroke: "#FFFFFF" }, }, }} > @@ -159,7 +165,7 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { }, }} > - cart + diff --git a/src/lib/IconsCreate.tsx b/src/lib/IconsCreate.tsx deleted file mode 100644 index b52ffea..0000000 --- a/src/lib/IconsCreate.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Box } from "@mui/material"; - -interface Props { - bgcolor: string; - svg: string; -} - -export const IconsCreate = ({ bgcolor, svg }: Props) => ( - - svg - -); From d71f19a7c9ea7c4513985a7a0fb108b9eaf4c182 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Fri, 4 Aug 2023 16:49:35 +0300 Subject: [PATCH 12/41] feat: NotificationsModal --- src/components/Drawers.tsx | 38 ++++++++- src/components/NotificationsModal.tsx | 107 ++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 src/components/NotificationsModal.tsx diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index 56659c9..aac6dfd 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -1,4 +1,4 @@ -import { useCallback } from "react"; +import { useState, useRef, useCallback } from "react"; import { Typography, Drawer, @@ -15,6 +15,7 @@ import { useTickets } from "@frontend/kitui"; import SectionWrapper from "./SectionWrapper"; import CustomWrapperDrawer from "./CustomWrapperDrawer"; import CustomButton from "./CustomButton"; +import { NotificationsModal } from "./NotificationsModal"; import { useNavigate } from "react-router"; import { useCart } from "@root/utils/hooks/useCart"; import { currencyFormatter } from "@root/utils/currencyFormatter"; @@ -35,6 +36,9 @@ import { ReactComponent as BellIcon } from "@root/assets/Icons/bell.svg"; import { ReactComponent as CartIcon } from "@root/assets/Icons/cart.svg"; export default function Drawers() { + const [openNotificationsModal, setOpenNotificationsModal] = + useState(false); + const bellRef = useRef(null); const navigate = useNavigate(); const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); @@ -50,6 +54,7 @@ export default function Drawers() { const ticketCount = useTicketStore((state) => state.ticketCount); const ticketApiPage = useTicketStore((state) => state.apiPage); const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage); + const tickets = useTicketStore((state) => state.tickets); useTickets({ url: "https://hub.pena.digital/heruvym/getTickets", @@ -77,11 +82,29 @@ export default function Drawers() { return ( setOpenNotificationsModal((isOpened) => !isOpened)} sx={{ cursor: "pointer", - background: theme.palette.background.default, borderRadius: "6px", + background: openNotificationsModal + ? theme.palette.brightPurple.main + : theme.palette.background.default, + "& .MuiBadge-badge": { + background: openNotificationsModal + ? theme.palette.background.default + : theme.palette.brightPurple.main, + color: openNotificationsModal + ? theme.palette.brightPurple.main + : theme.palette.background.default, + }, + "& svg > path:first-child": { + fill: openNotificationsModal ? "#FFFFFF" : "#9A9AAF", + }, + "& svg > path:last-child": { + stroke: openNotificationsModal ? "#FFFFFF" : "#9A9AAF", + }, "&:hover": { background: theme.palette.brightPurple.main, "& .MuiBox-root": { @@ -113,6 +136,16 @@ export default function Drawers() { + ({ + text: "У вас новое сообщение от техподдержки", + date: new Date(ticket.updated_at).toLocaleDateString(), + watched: ticket.user === ticket.top_message.user_id, + }))} + /> - void; + anchorElement: Element | null; + notifications: Notification[]; +}; + +export const NotificationsModal = ({ + open, + setOpen, + anchorElement, + notifications, +}: NotificationsModalProps) => { + return ( + setOpen(false)} + anchorOrigin={{ vertical: "bottom", horizontal: "left" }} + transformOrigin={{ vertical: "top", horizontal: "right" }} + sx={{ + "& .MuiPopover-paper": { + maxWidth: 600, + width: "100%", + maxHeight: "310px", + 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", + }, + }, + }} + > + + {[...notifications, ...notifications, ...notifications].map( + ({ text, date, watched = true }) => ( + + + {text} + + + {date} + + + ) + )} + + + ); +}; From 5d406febda7af322f6555e8fb23c449a1990f45a Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Fri, 4 Aug 2023 16:51:46 +0300 Subject: [PATCH 13/41] fix: trash --- src/components/NotificationsModal.tsx | 94 +++++++++++++-------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/src/components/NotificationsModal.tsx b/src/components/NotificationsModal.tsx index 1bb325a..2c34c32 100644 --- a/src/components/NotificationsModal.tsx +++ b/src/components/NotificationsModal.tsx @@ -49,58 +49,56 @@ export const NotificationsModal = ({ }} > - {[...notifications, ...notifications, ...notifications].map( - ({ text, date, watched = true }) => ( - ( + + - - {text} - - - {date} - - - ) - )} + {text} + + + {date} + + + ))} ); From 6cec14a6af8d1cc2e9c99764678bc315e3e90b7e Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Fri, 4 Aug 2023 17:14:25 +0300 Subject: [PATCH 14/41] feat: NotificationsModal mobile styles --- src/components/Drawers.tsx | 9 ++++---- src/components/Navbar/NavbarCollapsed.tsx | 26 ++++++++++++++++++----- src/components/NotificationsModal.tsx | 23 +++++++++++++++----- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index aac6dfd..735613f 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -51,15 +51,14 @@ export default function Drawers() { (state) => state.summaryPriceAfterDiscountsMap ); const userAccount = useUserStore((state) => state.userAccount); - const ticketCount = useTicketStore((state) => state.ticketCount); - const ticketApiPage = useTicketStore((state) => state.apiPage); - const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage); - const tickets = useTicketStore((state) => state.tickets); + const { tickets, ticketCount, apiPage, ticketsPerPage } = useTicketStore( + (state) => state + ); useTickets({ url: "https://hub.pena.digital/heruvym/getTickets", ticketsPerPage, - ticketApiPage, + ticketApiPage: apiPage, onNewTickets: useCallback((result) => { if (result.data) updateTickets(result.data); setTicketCount(result.count); diff --git a/src/components/Navbar/NavbarCollapsed.tsx b/src/components/Navbar/NavbarCollapsed.tsx index 009b7f3..bff8752 100644 --- a/src/components/Navbar/NavbarCollapsed.tsx +++ b/src/components/Navbar/NavbarCollapsed.tsx @@ -1,9 +1,10 @@ -import { useState, useEffect, useCallback } from "react"; +import { useState, useRef, useEffect, useCallback } from "react"; import { Box, Badge, Drawer, IconButton, useTheme } from "@mui/material"; import { Link } from "react-router-dom"; import { useTickets } from "@frontend/kitui"; import SectionWrapper from "../SectionWrapper"; +import { NotificationsModal } from "../NotificationsModal"; import { useUserStore } from "@root/stores/user"; import { @@ -29,15 +30,18 @@ interface Props { export default function NavbarCollapsed({ isLoggedIn, children }: Props) { const [open, setOpen] = useState(false); + const [openNotificationsModal, setOpenNotificationsModal] = + useState(false); + const bellRef = useRef(null); const userAccount = useUserStore((state) => state.userAccount); - const ticketCount = useTicketStore((state) => state.ticketCount); - const ticketApiPage = useTicketStore((state) => state.apiPage); - const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage); + const { ticketCount, tickets, apiPage, ticketsPerPage } = useTicketStore( + (state) => state + ); useTickets({ url: "https://hub.pena.digital/heruvym/getTickets", ticketsPerPage, - ticketApiPage, + ticketApiPage: apiPage, onNewTickets: useCallback((result) => { if (result.data) updateTickets(result.data); setTicketCount(result.count); @@ -134,6 +138,8 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { setOpenNotificationsModal((isOpened) => !isOpened)} aria-label="cart" sx={{ width: "30px", @@ -168,6 +174,16 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { + ({ + text: "У вас новое сообщение от техподдержки", + date: new Date(ticket.updated_at).toLocaleDateString(), + watched: ticket.user === ticket.top_message.user_id, + }))} + /> diff --git a/src/components/NotificationsModal.tsx b/src/components/NotificationsModal.tsx index 2c34c32..b99650a 100644 --- a/src/components/NotificationsModal.tsx +++ b/src/components/NotificationsModal.tsx @@ -1,4 +1,11 @@ -import { Popover, List, ListItem, Typography } from "@mui/material"; +import { + Popover, + List, + ListItem, + Typography, + useTheme, + useMediaQuery, +} from "@mui/material"; type Notification = { text: string; @@ -19,6 +26,9 @@ export const NotificationsModal = ({ anchorElement, notifications, }: NotificationsModalProps) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down(650)); + return ( {date} From c135431d82e8e7243cfdeefa8bf717ece23f41c7 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Mon, 7 Aug 2023 13:58:42 +0300 Subject: [PATCH 15/41] fix: mobile performance --- src/components/Navbar/DialogMenu.tsx | 2 +- src/components/NotificationsModal.tsx | 1 + src/components/Select/index.tsx | 1 + src/pages/Tariffs/slider/slider.css | 4 ++++ src/pages/Tariffs/slider/slider.tsx | 18 +++++++++++++++--- src/utils/calcCart/calcCart.test.ts | 4 ---- src/utils/themes/shadow.ts | 9 +-------- 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/components/Navbar/DialogMenu.tsx b/src/components/Navbar/DialogMenu.tsx index 6d2a2d5..c409bfa 100644 --- a/src/components/Navbar/DialogMenu.tsx +++ b/src/components/Navbar/DialogMenu.tsx @@ -123,7 +123,7 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) { {index === activeSubMenuIndex && subMenu.map(({ name, url }) => ( {notifications.map(({ text, date, watched = true }) => ( { const [range, setRange] = useState(3); const [activeRange, setActiveRange] = useState([0, 1, 2]); const theme = useTheme(); + const isMobileHeader = useMediaQuery(theme.breakpoints.down(900)); const isMiddle = useMediaQuery( theme.breakpoints.down(1200) && theme.breakpoints.up(830) ); + const isTablet = useMediaQuery(theme.breakpoints.down(830)); useEffect(() => { @@ -64,6 +71,10 @@ export const Slider = ({ items }: SliderProps) => { ]; }; + const Arrow = ({ currentSlide, slideCount, icon, ...props }: ArrowProps) => ( + {icon} + ); + return ( {(items.length < 4 && !isMiddle && !isTablet) || @@ -87,14 +98,15 @@ export const Slider = ({ items }: SliderProps) => { dots infinite variableWidth + lazyLoad={isMobileHeader ? "progressive" : undefined} slidesToShow={range} - prevArrow={prev} - nextArrow={next} + prevArrow={} />} + nextArrow={} />} beforeChange={(_, active) => setActiveRange(calculateRange(active, items.length)) } customPaging={(slideNumber) => ( -
  • Date: Mon, 7 Aug 2023 14:11:39 +0300 Subject: [PATCH 16/41] fix: slider pagination --- src/pages/Tariffs/slider/slider.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/pages/Tariffs/slider/slider.tsx b/src/pages/Tariffs/slider/slider.tsx index 8380ec3..5641704 100644 --- a/src/pages/Tariffs/slider/slider.tsx +++ b/src/pages/Tariffs/slider/slider.tsx @@ -26,12 +26,9 @@ export const Slider = ({ items }: SliderProps) => { const [range, setRange] = useState(3); const [activeRange, setActiveRange] = useState([0, 1, 2]); const theme = useTheme(); - const isMobileHeader = useMediaQuery(theme.breakpoints.down(900)); - const isMiddle = useMediaQuery( - theme.breakpoints.down(1200) && theme.breakpoints.up(830) - ); - + const isMiddle = useMediaQuery(theme.breakpoints.down(1200)); const isTablet = useMediaQuery(theme.breakpoints.down(830)); + const isMobileHeader = useMediaQuery(theme.breakpoints.down(900)); useEffect(() => { if (isTablet) { @@ -78,7 +75,7 @@ export const Slider = ({ items }: SliderProps) => { return ( {(items.length < 4 && !isMiddle && !isTablet) || - (items.length < 3 && isMiddle) || + (items.length < 3 && isMiddle && !isTablet) || (items.length < 1 && isTablet) ? ( Date: Mon, 7 Aug 2023 16:36:59 +0300 Subject: [PATCH 17/41] fix: fixed Navbar --- src/components/Navbar/DialogMenu.tsx | 3 + src/components/Navbar/Navbar.tsx | 11 +- src/components/Navbar/NavbarCollapsed.tsx | 171 ++++----- src/components/Navbar/NavbarFull.tsx | 3 + src/components/NavbarOld/DialogMenu.tsx | 364 +++++++++---------- src/components/NavbarOld/NavbarCollapsed.tsx | 19 +- src/components/NavbarOld/NavbarFull.tsx | 236 ++++++------ src/pages/Landing/Landing.tsx | 1 + 8 files changed, 425 insertions(+), 383 deletions(-) diff --git a/src/components/Navbar/DialogMenu.tsx b/src/components/Navbar/DialogMenu.tsx index c409bfa..007add9 100644 --- a/src/components/Navbar/DialogMenu.tsx +++ b/src/components/Navbar/DialogMenu.tsx @@ -12,6 +12,7 @@ import { import { useUserStore } from "@root/stores/user"; import { currencyFormatter } from "@root/utils/currencyFormatter"; +import { cardShadow } from "@root/utils/themes/shadow"; import CustomAvatar from "./Avatar"; @@ -45,6 +46,7 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) { const [activeSubMenuIndex, setActiveSubMenuIndex] = useState(-1); const theme = useTheme(); const location = useLocation(); + const isTablet = useMediaQuery(theme.breakpoints.down(900)); const isMobile = useMediaQuery(theme.breakpoints.down(600)); const user = useUserStore((state) => state.user); const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0; @@ -118,6 +120,7 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) { sx={{ backgroundColor: theme.palette.background.paper, width: "100%", + boxShadow: !isTablet ? cardShadow : null, }} > {index === activeSubMenuIndex && diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index de1c449..8a07370 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -1,4 +1,4 @@ -import { useMediaQuery, useTheme } from "@mui/material"; +import { Box, useMediaQuery, useTheme } from "@mui/material"; import NavbarCollapsed from "./NavbarCollapsed"; import NavbarFull from "./NavbarFull"; @@ -14,12 +14,17 @@ export default function Navbar({ isLoggedIn, children }: Props) { const upMd = useMediaQuery(theme.breakpoints.up("md")); return ( - <> + {upMd ? ( {children} ) : ( {children} )} - + ); } diff --git a/src/components/Navbar/NavbarCollapsed.tsx b/src/components/Navbar/NavbarCollapsed.tsx index bff8752..1a2a04a 100644 --- a/src/components/Navbar/NavbarCollapsed.tsx +++ b/src/components/Navbar/NavbarCollapsed.tsx @@ -71,36 +71,80 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { maxWidth="lg" outerContainerSx={{ backgroundColor: theme.palette.navbarbg.main, - position: "sticky", - top: 0, }} sx={{ height: "51px", padding: "0" }} > - - setOpen((isOpened) => !isOpened)} + + - {open ? ( - - ) : ( - - )} - - setOpen((isOpened) => !isOpened)} + sx={{ + p: 0, + width: "30px", + color: theme.palette.primary.main, + }} + > + {open ? ( + + ) : ( + + )} + + + path:nth-child(1)": { fill: "#FFFFFF" }, + "& svg > path:nth-child(2)": { fill: "#FFFFFF" }, + "& svg > path:nth-child(3)": { stroke: "#FFFFFF" }, + }, + }} + > + + + + + + setOpenNotificationsModal((isOpened) => !isOpened)} aria-label="cart" sx={{ width: "30px", @@ -113,80 +157,42 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { background: theme.palette.background.default, color: theme.palette.brightPurple.main, }, - "& svg > path:nth-child(1)": { fill: "#FFFFFF" }, - "& svg > path:nth-child(2)": { fill: "#FFFFFF" }, - "& svg > path:nth-child(3)": { stroke: "#FFFFFF" }, + "& svg > path:first-child": { fill: "#FFFFFF" }, + "& svg > path:last-child": { stroke: "#FFFFFF" }, }, }} > - + - - setOpenNotificationsModal((isOpened) => !isOpened)} - aria-label="cart" - sx={{ - width: "30px", - height: "30px", - background: theme.palette.background.default, - borderRadius: "6px", - "&:hover": { - background: theme.palette.brightPurple.main, - "& .MuiBadge-badge": { - background: theme.palette.background.default, - color: theme.palette.brightPurple.main, - }, - "& svg > path:first-child": { fill: "#FFFFFF" }, - "& svg > path:last-child": { stroke: "#FFFFFF" }, - }, - }} - > - - - - - ({ - text: "У вас новое сообщение от техподдержки", - date: new Date(ticket.updated_at).toLocaleDateString(), - watched: ticket.user === ticket.top_message.user_id, - }))} - /> - - - + ({ + text: "У вас новое сообщение от техподдержки", + date: new Date(ticket.updated_at).toLocaleDateString(), + watched: ticket.user === ticket.top_message.user_id, + }))} + /> + + + + + ref: React.Ref ) { - return ; + return ; }); interface DialogMenuProps { - open: boolean; - handleClose: () => void; + open: boolean; + handleClose: () => void; } export default function DialogMenu({ open, handleClose }: DialogMenuProps) { - const theme = useTheme(); - const location = useLocation(); - const isMobile = useMediaQuery(theme.breakpoints.down(600)); - const user = useUserStore((state) => state.user); - const cash = useUserStore(state => state.userAccount?.wallet.cash) ?? 0; + const theme = useTheme(); + const location = useLocation(); + const isTablet = useMediaQuery(theme.breakpoints.down(900)); + const user = useUserStore((state) => state.user); + const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0; - return ( - + + - ( + + ) : ( + + + + - {isMobile && ( - - icon - - )} - - - - - - - - {arrayMenu.map(({ name, url }, index) => ( - - ))} - - {isMobile ? ( - location.pathname === "/" ? ( - - ) : ( - - - - - Мой баланс - - - {currencyFormatter.format(cash / 100)} - - - - ) - ) : ( - <> - - - icon - - - )} - - - ); + Мой баланс + + + {currencyFormatter.format(cash / 100)} + + + + ) + ) : ( + <> + + + icon + + + )} + + + ); } diff --git a/src/components/NavbarOld/NavbarCollapsed.tsx b/src/components/NavbarOld/NavbarCollapsed.tsx index 838bc32..b804dd9 100644 --- a/src/components/NavbarOld/NavbarCollapsed.tsx +++ b/src/components/NavbarOld/NavbarCollapsed.tsx @@ -30,11 +30,11 @@ export default function NavbarCollapsed({ isLoggedIn }: Props) { component="nav" maxWidth="lg" outerContainerSx={{ + position: "fixed", + top: "0", + zIndex: 2000, backgroundColor: theme.palette.navbarbg.main, - position: "sticky", - top: 0, - zIndex: 1, - // borderBottom: "1px solid #E3E3E3", + borderBottom: "1px solid #E3E3E3", }} sx={{ height: "51px", @@ -44,9 +44,14 @@ export default function NavbarCollapsed({ isLoggedIn }: Props) { alignItems: "center", }} > - - - + + + + + diff --git a/src/components/NavbarOld/NavbarFull.tsx b/src/components/NavbarOld/NavbarFull.tsx index f927b8c..d93532b 100644 --- a/src/components/NavbarOld/NavbarFull.tsx +++ b/src/components/NavbarOld/NavbarFull.tsx @@ -1,5 +1,12 @@ import { Link, useLocation, useNavigate } from "react-router-dom"; -import { Box, Button, Container, IconButton, Typography, useTheme } from "@mui/material"; +import { + Box, + Button, + Container, + IconButton, + Typography, + useTheme, +} from "@mui/material"; import SectionWrapper from "../SectionWrapper"; import LogoutIcon from "../icons/LogoutIcon"; import WalletIcon from "../icons/WalletIcon"; @@ -15,119 +22,130 @@ import { clearCustomTariffs } from "@root/stores/customTariffs"; import { currencyFormatter } from "@root/utils/currencyFormatter"; interface Props { - isLoggedIn: boolean; + isLoggedIn: boolean; } export default function NavbarFull({ isLoggedIn }: Props) { - const theme = useTheme(); - const location = useLocation(); - const navigate = useNavigate(); - const user = useUserStore((state) => state.user); - const cash = useUserStore(state => state.userAccount?.wallet.cash) ?? 0; + const theme = useTheme(); + const location = useLocation(); + const navigate = useNavigate(); + const user = useUserStore((state) => state.user); + const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0; - async function handleLogoutClick() { - try { - await logout(); - clearAuthToken(); - clearUserData(); - clearCustomTariffs(); - navigate("/"); - } catch (error: any) { - const message = getMessageFromFetchError(error, "Не удалось выйти"); - if (message) enqueueSnackbar(message); - } + async function handleLogoutClick() { + try { + await logout(); + clearAuthToken(); + clearUserData(); + clearCustomTariffs(); + navigate("/"); + } catch (error: any) { + const message = getMessageFromFetchError(error, "Не удалось выйти"); + if (message) enqueueSnackbar(message); } + } - return isLoggedIn ? ( - + + + + + + + navigate("/wallet")} > - - - - - navigate("/wallet")} - > - - - - - Мой баланс - - - {currencyFormatter.format(cash / 100)} - - - - - - - - - ) : ( - <> - - - - - - - ); + + + + + Мой баланс + + + {currencyFormatter.format(cash / 100)} + + + + + + + + + ) : ( + <> + + + + + + + ); } diff --git a/src/pages/Landing/Landing.tsx b/src/pages/Landing/Landing.tsx index f6e2277..14cd510 100644 --- a/src/pages/Landing/Landing.tsx +++ b/src/pages/Landing/Landing.tsx @@ -20,6 +20,7 @@ export default function Landing({ templaterOnly = false }: Props) { From af3b78cd372615e5b29cad06ba14b2673e8fa514 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Mon, 7 Aug 2023 18:16:34 +0300 Subject: [PATCH 18/41] fix: NotificationsModal --- src/components/Drawers.tsx | 15 +-- src/components/Navbar/NavbarCollapsed.tsx | 24 +++-- src/components/Navbar/NavbarFull.tsx | 1 - src/components/NavbarOld/DialogMenu.tsx | 6 ++ src/components/NavbarOld/NavbarCollapsed.tsx | 3 +- src/components/NavbarOld/NavbarFull.tsx | 1 - src/components/NotificationsModal.tsx | 105 ++++++++++--------- src/components/Select/index.tsx | 2 +- src/components/Select/select.css | 4 + 9 files changed, 94 insertions(+), 67 deletions(-) diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index 735613f..d314b14 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -51,9 +51,7 @@ export default function Drawers() { (state) => state.summaryPriceAfterDiscountsMap ); const userAccount = useUserStore((state) => state.userAccount); - const { tickets, ticketCount, apiPage, ticketsPerPage } = useTicketStore( - (state) => state - ); + const { tickets, apiPage, ticketsPerPage } = useTicketStore((state) => state); useTickets({ url: "https://hub.pena.digital/heruvym/getTickets", @@ -75,6 +73,10 @@ export default function Drawers() { 0 ); + const notificationsCount = tickets.filter( + ({ user, top_message }) => user !== top_message.user_id + ).length; + const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts + basePrice; const totalPriceAfterDiscounts = cart.priceAfterDiscounts + discountedPrice; @@ -119,10 +121,10 @@ export default function Drawers() { }} > ({ text: "У вас новое сообщение от техподдержки", date: new Date(ticket.updated_at).toLocaleDateString(), + url: `/support/${ticket.id}`, watched: ticket.user === ticket.top_message.user_id, }))} /> diff --git a/src/components/Navbar/NavbarCollapsed.tsx b/src/components/Navbar/NavbarCollapsed.tsx index 1a2a04a..dae9090 100644 --- a/src/components/Navbar/NavbarCollapsed.tsx +++ b/src/components/Navbar/NavbarCollapsed.tsx @@ -34,9 +34,7 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { useState(false); const bellRef = useRef(null); const userAccount = useUserStore((state) => state.userAccount); - const { ticketCount, tickets, apiPage, ticketsPerPage } = useTicketStore( - (state) => state - ); + const { tickets, apiPage, ticketsPerPage } = useTicketStore((state) => state); useTickets({ url: "https://hub.pena.digital/heruvym/getTickets", @@ -55,6 +53,10 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { setOpen(false); }; + const notificationsCount = tickets.filter( + ({ user, top_message }) => user !== top_message.user_id + ).length; + useEffect(() => { if (open) { document.body.style.overflow = "hidden"; @@ -72,12 +74,15 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { outerContainerSx={{ backgroundColor: theme.palette.navbarbg.main, }} - sx={{ height: "51px", padding: "0" }} + sx={{ + height: "51px", + padding: "0", + }} > ({ text: "У вас новое сообщение от техподдержки", date: new Date(ticket.updated_at).toLocaleDateString(), + url: `/support/${ticket.id}`, watched: ticket.user === ticket.top_message.user_id, }))} /> @@ -205,7 +211,7 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) { top: "0", width: 210, height: "100%", - paddingTop: "51px", + marginTop: "51px", }, }} variant="persistent" diff --git a/src/components/Navbar/NavbarFull.tsx b/src/components/Navbar/NavbarFull.tsx index 595a408..e069788 100644 --- a/src/components/Navbar/NavbarFull.tsx +++ b/src/components/Navbar/NavbarFull.tsx @@ -58,7 +58,6 @@ export default function NavbarFull({ isLoggedIn, children }: Props) { disableGutters maxWidth={false} sx={{ - zIndex: 2000, position: "fixed", top: "0", px: "16px", diff --git a/src/components/NavbarOld/DialogMenu.tsx b/src/components/NavbarOld/DialogMenu.tsx index d016182..9dfff81 100644 --- a/src/components/NavbarOld/DialogMenu.tsx +++ b/src/components/NavbarOld/DialogMenu.tsx @@ -63,6 +63,12 @@ export default function DialogMenu({ open, handleClose }: DialogMenuProps) { ml: "auto", mt: "50px", height: "100%", + ".MuiBackdrop-root.MuiModal-backdrop": { + background: "transparent", + }, + ".MuiPaper-root.MuiPaper-rounded": { + background: "#333647", + }, }} open={open} onClose={handleClose} diff --git a/src/components/NavbarOld/NavbarCollapsed.tsx b/src/components/NavbarOld/NavbarCollapsed.tsx index b804dd9..750a61a 100644 --- a/src/components/NavbarOld/NavbarCollapsed.tsx +++ b/src/components/NavbarOld/NavbarCollapsed.tsx @@ -1,12 +1,12 @@ import { useState } from "react"; import { IconButton, useTheme } from "@mui/material"; import MenuIcon from "@mui/icons-material/Menu"; +import { Link } from "react-router-dom"; import SectionWrapper from "../SectionWrapper"; import PenaLogo from "../PenaLogo"; import DialogMenu from "./DialogMenu"; -import { Link } from "react-router-dom"; interface Props { isLoggedIn: boolean; @@ -32,7 +32,6 @@ export default function NavbarCollapsed({ isLoggedIn }: Props) { outerContainerSx={{ position: "fixed", top: "0", - zIndex: 2000, backgroundColor: theme.palette.navbarbg.main, borderBottom: "1px solid #E3E3E3", }} diff --git a/src/components/NavbarOld/NavbarFull.tsx b/src/components/NavbarOld/NavbarFull.tsx index d93532b..ad9875e 100644 --- a/src/components/NavbarOld/NavbarFull.tsx +++ b/src/components/NavbarOld/NavbarFull.tsx @@ -114,7 +114,6 @@ export default function NavbarFull({ isLoggedIn }: Props) { outerContainerSx={{ position: "fixed", top: "0", - zIndex: 2000, backgroundColor: theme.palette.lightPurple.main, borderBottom: "1px solid #E3E3E3", }} diff --git a/src/components/NotificationsModal.tsx b/src/components/NotificationsModal.tsx index 87e688f..c64208e 100644 --- a/src/components/NotificationsModal.tsx +++ b/src/components/NotificationsModal.tsx @@ -6,10 +6,12 @@ import { useTheme, useMediaQuery, } from "@mui/material"; +import { Link } from "react-router-dom"; type Notification = { text: string; date: string; + url: string; watched?: boolean; }; @@ -59,59 +61,68 @@ export const NotificationsModal = ({ }} > - {notifications.map(({ text, date, watched = true }) => ( - ( + setOpen(false)} + style={{ + textDecoration: "none", + color: "inherit", }} > - - {text} - - - {date} - - + + {text} + + + {date} + + + ))} diff --git a/src/components/Select/index.tsx b/src/components/Select/index.tsx index c56a17d..230b9b2 100644 --- a/src/components/Select/index.tsx +++ b/src/components/Select/index.tsx @@ -39,7 +39,7 @@ export const Select = ({ Date: Sun, 6 Aug 2023 01:15:18 +0300 Subject: [PATCH 19/41] create test access --- cypress.config.ts | 11 + cypress/e2e/access.cy.ts | 125 ++++++ package.json | 139 +++--- src/components/passwordInput.tsx | 12 +- src/pages/auth/Signin.tsx | 66 +-- src/pages/auth/Signup.tsx | 358 ++++++++-------- tsconfig.json | 47 +- yarn.lock | 710 ++++++++++++++++++++++++++++++- 8 files changed, 1138 insertions(+), 330 deletions(-) create mode 100644 cypress.config.ts create mode 100644 cypress/e2e/access.cy.ts diff --git a/cypress.config.ts b/cypress.config.ts new file mode 100644 index 0000000..a43fe1b --- /dev/null +++ b/cypress.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "cypress"; + +export default defineConfig({ + e2e: { + viewportWidth: 1200, + viewportHeight: 800, + fixturesFolder: "tests/e2e/fixtures", + supportFile: false, + defaultCommandTimeout: 100, + }, +}); diff --git a/cypress/e2e/access.cy.ts b/cypress/e2e/access.cy.ts new file mode 100644 index 0000000..b065432 --- /dev/null +++ b/cypress/e2e/access.cy.ts @@ -0,0 +1,125 @@ +describe("Форма Входа", () => { + beforeEach(() => { + cy.visit("http://localhost:3000"); + cy.wait(1000); + cy.contains("Личный кабинет").click(); + }); + + it("должна успешно входить с правильными учетными данными", () => { + const login = "valid_user@example.com"; + const password = "valid_password"; + + cy.get("#login").type(login); + cy.get("#password").type(password); + cy.get('button[type="submit"]').click(); + + cy.wait(2000); + cy.url().should("include", "http://localhost:3000/tariffs"); + }); + + it("должна отображать два сообщение об ошибке при отсутствии полей", () => { + cy.get('button[type="submit"]').click(); + + cy.wait(2000); + cy.get("#password-helper-text").should("contain", "Поле обязательно"); + cy.get("#login-helper-text").should("contain", "Поле обязательно"); + }); + + it("должна отображать сообщение об ошибке при отсутствии пароля", () => { + cy.get("#login").type("valid_email@example.com"); + cy.get('button[type="submit"]').click(); + + cy.get("#password-helper-text").should("contain", "Поле обязательно"); + }); + + it("должна отображать сообщение об ошибке при отсутствии Логина", () => { + cy.get("#password").type("valid_password"); + cy.get('button[type="submit"]').click(); + + cy.get("#login-helper-text").should("contain", "Поле обязательно"); + }); +}); + +describe("Форма регистрации", () => { + beforeEach(() => { + cy.visit("http://localhost:3000"); + cy.wait(1000); + cy.contains("Личный кабинет").click(); + cy.contains("Регистрация").click(); + }); + + it("должна регистрировать нового пользователя с правильными данными", () => { + const login = Cypress._.random(1000) + "@example.com"; + const password = "valid_password"; + + cy.get("#login").type(login); + cy.get("#password").type(password); + cy.get("#repeatPassword").type(password); + + cy.get('button[type="submit"]').click(); + cy.wait(5000); + + cy.url().should("include", "http://localhost:3000/tariffs"); + }); + + it("должна отображать ошибку при отсутсвии логина", () => { + cy.get("#password").type("valid_password"); + cy.get("#repeatPassword").type("valid_password"); + + cy.get('button[type="submit"]').click(); + + cy.get("#login-helper-text").should("contain", "Поле обязательно"); + }); + + it("должна отображать ошибку при отсутствии пароля", () => { + cy.get("#login").type("valid_login"); + + cy.get('button[type="submit"]').click(); + + cy.get("#password-helper-text").should("contain", "Поле обязательно"); + }); + + it("должна отображать ошибку при отсутствии поля Повторения пароля", () => { + cy.get("#login").type("valid_login"); + cy.get("#password").type("valid_password"); + + cy.get('button[type="submit"]').click(); + + cy.get("#repeatPassword-helper-text").should("contain", "Повторите пароль"); + }); + + it("должна отображать ошибку при некоректном пароле", () => { + cy.get("#login").type("valid_log"); + cy.get("#password").type("valid@12_-_@@password"); + + cy.get('button[type="submit"]').click(); + + cy.get("#password-helper-text").should("contain", "Некорректные символы"); + + cy.get("#repeatPassword-helper-text").should("contain", "Повторите пароль"); + }); + + it("должна отображать ошибку при несовпадении паролей", () => { + cy.get("#login").type("valid_login"); + cy.get("#password").type("valid_password"); + cy.get("#repeatPassword").type("invalidPassword"); + + cy.get('button[type="submit"]').click(); + + cy.get("#repeatPassword-helper-text").should("contain", "Пароли не совпадают"); + }); + + it("попытка отправки запроса при уже зарегистрированном пользователе", () => { + const login = "valid_user@example.com"; + const password = "valid_password"; + + cy.get("#login").type(login); + cy.get("#password").type(password); + cy.get("#repeatPassword").type(password); + + cy.get('button[type="submit"]').click(); + + cy.wait(5000); + cy.contains("user with this login is exist"); + }); +}); diff --git a/package.json b/package.json index a2f5b46..a53900d 100644 --- a/package.json +++ b/package.json @@ -1,70 +1,73 @@ { - "name": "hub_frontend", - "version": "0.1.0", - "private": true, - "scripts": { - "start": "craco start", - "build": "craco build", - "test": "craco test --env=node --transformIgnorePatterns \"node_modules/(?!@frontend)/\"", - "test:cart": "craco test src/utils/calcCart --transformIgnorePatterns \"node_modules/(?!@frontend)/\"", - "eject": "craco eject" - }, - "dependencies": { - "@emotion/react": "^11.10.5", - "@emotion/styled": "^11.10.5", - "@frontend/kitui": "^1.0.17", - "@mui/icons-material": "^5.10.14", - "@mui/material": "^5.10.14", - "@popperjs/core": "^2.11.8", - "axios": "^1.4.0", - "buffer": "^6.0.3", - "classnames": "^2.3.2", - "formik": "^2.2.9", - "immer": "^10.0.2", - "isomorphic-fetch": "^3.0.0", - "notistack": "^3.0.1", - "pdfjs-dist": "3.6.172", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-pdf": "^7.1.2", - "react-router-dom": "^6.4.3", - "react-slick": "^0.29.0", - "slick-carousel": "^1.8.1", - "web-vitals": "^2.1.0", - "yup": "^1.1.1", - "zustand": "^4.3.8" - }, - "devDependencies": { - "@craco/craco": "^7.1.0", - "@testing-library/jest-dom": "^5.14.1", - "@testing-library/react": "^13.0.0", - "@testing-library/user-event": "^13.2.1", - "@types/jest": "^27.0.1", - "@types/node": "^16.7.13", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "@types/react-slick": "^0.23.10", - "craco-alias": "^3.0.1", - "jest": "^29.5.0", - "react-scripts": "5.0.1", - "typescript": "^4.9.3" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } + "name": "hub_frontend", + "version": "0.1.0", + "private": true, + "scripts": { + "start": "craco start", + "build": "craco build", + "test": "craco test --env=node --transformIgnorePatterns \"node_modules/(?!@frontend)/\"", + "test:cart": "craco test src/utils/calcCart --transformIgnorePatterns \"node_modules/(?!@frontend)/\"", + "eject": "craco eject", + "test:cypress": "start-server-and-test start http://localhost:3000 cypress", + "cypress": "cypress open" + }, + "dependencies": { + "@emotion/react": "^11.10.5", + "@emotion/styled": "^11.10.5", + "@frontend/kitui": "^1.0.17", + "@mui/icons-material": "^5.10.14", + "@mui/material": "^5.10.14", + "@popperjs/core": "^2.11.8", + "axios": "^1.4.0", + "buffer": "^6.0.3", + "classnames": "^2.3.2", + "cypress": "^12.17.3", + "formik": "^2.2.9", + "immer": "^10.0.2", + "isomorphic-fetch": "^3.0.0", + "notistack": "^3.0.1", + "pdfjs-dist": "3.6.172", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-pdf": "^7.1.2", + "react-router-dom": "^6.4.3", + "react-slick": "^0.29.0", + "slick-carousel": "^1.8.1", + "web-vitals": "^2.1.0", + "yup": "^1.1.1", + "zustand": "^4.3.8" + }, + "devDependencies": { + "@craco/craco": "^7.1.0", + "@testing-library/jest-dom": "^5.14.1", + "@testing-library/react": "^13.0.0", + "@testing-library/user-event": "^13.2.1", + "@types/jest": "^27.0.1", + "@types/node": "^16.7.13", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "@types/react-slick": "^0.23.10", + "craco-alias": "^3.0.1", + "jest": "^29.5.0", + "react-scripts": "5.0.1", + "typescript": "^4.9.3" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } } diff --git a/src/components/passwordInput.tsx b/src/components/passwordInput.tsx index 31293d1..06ab1c3 100644 --- a/src/components/passwordInput.tsx +++ b/src/components/passwordInput.tsx @@ -22,9 +22,7 @@ interface Props { color?: string; FormInputSx?: SxProps; TextfieldProps: TextFieldProps; - onChange: ( - e: React.ChangeEvent - ) => void; + onChange: (e: React.ChangeEvent) => void; } export default function PasswordInput({ @@ -46,17 +44,13 @@ export default function PasswordInput({ : { ...theme.typography.body1, fontWeight: 500 } : theme.typography.body2; - const placeholderFont = upMd - ? undefined - : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" }; + const placeholderFont = upMd ? undefined : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" }; const [showPassword, setShowPassword] = React.useState(false); const handleClickShowPassword = () => setShowPassword((show) => !show); - const handleMouseDownPassword = ( - event: React.MouseEvent - ) => { + const handleMouseDownPassword = (event: React.MouseEvent) => { event.preventDefault(); }; diff --git a/src/pages/auth/Signin.tsx b/src/pages/auth/Signin.tsx index 5e94046..7a86bb6 100644 --- a/src/pages/auth/Signin.tsx +++ b/src/pages/auth/Signin.tsx @@ -76,39 +76,39 @@ export default function SigninDialog() { setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen); } - return ( - - + (true); - 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: (values, formikHelpers) => { - makeRequest({ - url: "https://hub.pena.digital/auth/register", - body: { - login: values.login.trim(), - password: values.password.trim(), - phoneNumber: "+7", - }, - useToken: false, - withCredentials: true, - }).then(result => { - setUserId(result._id); - }).catch((error: any) => { - const errorMessage = getMessageFromFetchError(error); - if (errorMessage) enqueueSnackbar(errorMessage); - }).finally(() => { - formikHelpers.setSubmitting(false); - }); + const [isDialogOpen, setIsDialogOpen] = useState(true); + 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: (values, formikHelpers) => { + makeRequest({ + url: "https://hub.pena.digital/auth/register", + body: { + login: values.login.trim(), + password: values.password.trim(), + phoneNumber: "+7", }, - }); + useToken: false, + withCredentials: true, + }) + .then((result) => { + setUserId(result._id); + }) + .catch((error: any) => { + const errorMessage = getMessageFromFetchError(error); + if (errorMessage) enqueueSnackbar(errorMessage); + }) + .finally(() => { + formikHelpers.setSubmitting(false); + }); + }, + }); - useEffect(function redirectIfSignedIn() { - if (user) navigate("/tariffs", { replace: true }); - }, [navigate, user]); + useEffect( + function redirectIfSignedIn() { + if (user) navigate("/tariffs", { replace: true }); + }, + [navigate, user] + ); - function handleClose() { - setIsDialogOpen(false); - setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen); - } + function handleClose() { + setIsDialogOpen(false); + setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen); + } - return ( - + + - - - - - - - - - Регистрация - - - - - - Зарегистрироваться - - - Вход в личный кабинет - - - - ); + + + + + + + Регистрация + + + + + + Зарегистрироваться + + + Вход в личный кабинет + + + + ); } diff --git a/tsconfig.json b/tsconfig.json index 2f8f55e..b6dc4cb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,27 +1,22 @@ { - "extends": "./tsconfig.extend.json", - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx" - }, - "include": [ - "src" - ] -} \ No newline at end of file + "extends": "./tsconfig.extend.json", + "compilerOptions": { + "target": "es5", + "lib": ["es5", "dom", "dom.iterable", "esnext"], + "types": ["node"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src", "**/*.ts"] +} diff --git a/yarn.lock b/yarn.lock index fa6faaf..23ab222 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1203,6 +1203,11 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@craco/craco@^7.1.0": version "7.1.0" resolved "https://registry.npmjs.org/@craco/craco/-/craco-7.1.0.tgz" @@ -1334,6 +1339,38 @@ resolved "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz" integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg== +"@cypress/request@^2.88.11": + version "2.88.12" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.12.tgz#ba4911431738494a85e93fb04498cb38bc55d590" + integrity sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + http-signature "~1.3.6" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + performance-now "^2.1.0" + qs "~6.10.3" + safe-buffer "^5.1.2" + tough-cookie "^4.1.3" + tunnel-agent "^0.6.0" + uuid "^8.3.2" + +"@cypress/xvfb@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + "@emotion/babel-plugin@^11.10.5": version "11.10.5" resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz" @@ -2655,6 +2692,11 @@ resolved "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz" integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg== +"@types/node@^16.18.39": + version "16.18.39" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.39.tgz#aa39a1a87a40ef6098ee69689a1acb0c1b034832" + integrity sha512-8q9ZexmdYYyc5/cfujaXb4YOucpQxAV4RMG0himLyDUOEr8Mr79VrqsFI+cQ2M2h89YIuy95lbxuYjxT4Hk4kQ== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" @@ -2759,6 +2801,16 @@ "@types/mime" "*" "@types/node" "*" +"@types/sinonjs__fake-timers@8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" + integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== + +"@types/sizzle@^2.3.2": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" + integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== + "@types/sockjs@^0.3.33": version "0.3.33" resolved "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz" @@ -2809,6 +2861,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yauzl@^2.9.1": + version "2.10.0" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" + integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw== + dependencies: + "@types/node" "*" + "@typescript-eslint/eslint-plugin@^5.5.0": version "5.43.0" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.43.0.tgz" @@ -3115,6 +3174,14 @@ agent-base@6: dependencies: debug "4" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" @@ -3154,7 +3221,12 @@ ajv@^8.0.0, ajv@^8.6.0, ajv@^8.8.0: require-from-string "^2.0.2" uri-js "^4.2.2" -ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.1: version "4.3.2" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -3208,6 +3280,11 @@ anymatch@^3.0.3, anymatch@~3.1.2: resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== +arch@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + are-we-there-yet@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" @@ -3315,12 +3392,29 @@ asap@~2.0.6: resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + ast-types-flow@^0.0.7: version "0.0.7" resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz" integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== -async@^3.2.3: +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async@^3.2.0, async@^3.2.3: version "3.2.4" resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== @@ -3352,6 +3446,16 @@ available-typed-arrays@^1.0.5: resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + +aws4@^1.8.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" + integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== + axe-core@^4.4.3: version "4.5.2" resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz" @@ -3553,6 +3657,13 @@ batch@0.6.1: resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + bfj@^7.0.2: version "7.0.2" resolved "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz" @@ -3573,7 +3684,12 @@ binary-extensions@^2.0.0: resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bluebird@^3.5.5: +blob-util@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" + integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== + +bluebird@^3.5.5, bluebird@^3.7.2: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -3655,11 +3771,24 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -3683,6 +3812,11 @@ bytes@3.1.2: resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cachedir@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.4.0.tgz#7fef9cf7367233d7c88068fe6e34ed0d355a610d" + integrity sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ== + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" @@ -3748,6 +3882,11 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" @@ -3783,6 +3922,11 @@ char-regex@^2.0.0: resolved "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz" integrity sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw== +check-more-types@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== + check-types@^11.1.1: version "11.1.2" resolved "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz" @@ -3835,6 +3979,35 @@ clean-css@^5.2.2: dependencies: source-map "~0.6.0" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-table3@~0.6.1: + version "0.6.3" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" + integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" @@ -3925,7 +4098,12 @@ colorette@^2.0.10: resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== -combined-stream@^1.0.8: +colorette@^2.0.16: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -3937,6 +4115,11 @@ commander@^2.20.0: resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + commander@^7.2.0: version "7.2.0" resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" @@ -4051,6 +4234,11 @@ core-js@^3.19.2: resolved "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz" integrity sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA== +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" @@ -4096,7 +4284,7 @@ create-require@^1.1.0: resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4310,11 +4498,66 @@ csstype@^3.1.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== +cypress@^12.17.3: + version "12.17.3" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.3.tgz#1e2b19037236fc60e4a4b3a14f0846be17a1fc0e" + integrity sha512-/R4+xdIDjUSLYkiQfwJd630S81KIgicmQOLXotFxVXkl+eTeVO+3bHXxdi5KBh/OgC33HWN33kHX+0tQR/ZWpg== + dependencies: + "@cypress/request" "^2.88.11" + "@cypress/xvfb" "^1.2.4" + "@types/node" "^16.18.39" + "@types/sinonjs__fake-timers" "8.1.1" + "@types/sizzle" "^2.3.2" + arch "^2.2.0" + blob-util "^2.0.2" + bluebird "^3.7.2" + buffer "^5.6.0" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-cursor "^3.1.0" + cli-table3 "~0.6.1" + commander "^6.2.1" + common-tags "^1.8.0" + dayjs "^1.10.4" + debug "^4.3.4" + enquirer "^2.3.6" + eventemitter2 "6.4.7" + execa "4.1.0" + executable "^4.1.1" + extract-zip "2.0.1" + figures "^3.2.0" + fs-extra "^9.1.0" + getos "^3.2.1" + is-ci "^3.0.0" + is-installed-globally "~0.4.0" + lazy-ass "^1.6.0" + listr2 "^3.8.3" + lodash "^4.17.21" + log-symbols "^4.0.0" + minimist "^1.2.8" + ospath "^1.2.2" + pretty-bytes "^5.6.0" + proxy-from-env "1.0.0" + request-progress "^3.0.0" + semver "^7.5.3" + supports-color "^8.1.1" + tmp "~0.2.1" + untildify "^4.0.0" + yauzl "^2.10.0" + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + data-urls@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz" @@ -4324,6 +4567,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +dayjs@^1.10.4: + version "1.11.9" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" + integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== + debug@2.6.9, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" @@ -4338,7 +4586,7 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" -debug@^3.2.7: +debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -4644,6 +4892,14 @@ duplexer@^0.1.2: resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" @@ -4696,6 +4952,13 @@ encodeurl@~1.0.2: resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + enhanced-resolve@^5.10.0: version "5.10.0" resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz" @@ -4709,6 +4972,14 @@ enquire.js@^2.1.6: resolved "https://registry.yarnpkg.com/enquire.js/-/enquire.js-2.1.6.tgz#3e8780c9b8b835084c3f60e166dbc3c2a3c89814" integrity sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw== +enquirer@^2.3.6: + version "2.4.1" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" + integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== + dependencies: + ansi-colors "^4.1.1" + strip-ansi "^6.0.1" + entities@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" @@ -5097,6 +5368,11 @@ etag@~1.8.1: resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +eventemitter2@6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" + integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" @@ -5107,6 +5383,21 @@ events@^3.2.0: resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +execa@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + execa@^5.0.0: version "5.1.1" resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" @@ -5122,6 +5413,13 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +executable@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" @@ -5196,6 +5494,32 @@ express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" @@ -5243,6 +5567,20 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" @@ -5352,6 +5690,11 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + fork-ts-checker-webpack-plugin@^6.5.0: version "6.5.2" resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz" @@ -5389,6 +5732,15 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + formik@^2.2.9: version "2.2.9" resolved "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz" @@ -5426,7 +5778,7 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^9.0.0, fs-extra@^9.0.1: +fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -5522,6 +5874,13 @@ get-package-type@^0.1.0: resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-stream@^5.0.0, get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" @@ -5535,6 +5894,20 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +getos@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" + integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== + dependencies: + async "^3.2.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" @@ -5566,6 +5939,13 @@ glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== + dependencies: + ini "2.0.0" + global-modules@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" @@ -5829,6 +6209,15 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" +http-signature@~1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" + integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== + dependencies: + assert-plus "^1.0.0" + jsprim "^2.0.2" + sshpk "^1.14.1" + https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" @@ -5837,6 +6226,11 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" @@ -5873,7 +6267,7 @@ identity-obj-proxy@^3.0.0: dependencies: harmony-reflect "^1.4.6" -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -5937,6 +6331,11 @@ inherits@2.0.3: resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + ini@^1.3.5: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" @@ -6001,6 +6400,13 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== +is-ci@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" + integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== + dependencies: + ci-info "^3.2.0" + is-core-module@^2.8.1, is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" @@ -6042,6 +6448,14 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-installed-globally@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + is-map@^2.0.1, is-map@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz" @@ -6074,7 +6488,7 @@ is-obj@^1.0.1: resolved "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz" integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== -is-path-inside@^3.0.3: +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -6156,11 +6570,16 @@ is-typed-array@^1.1.10: gopd "^1.0.1" has-tostringtag "^1.0.0" -is-typedarray@^1.0.0: +is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-weakmap@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz" @@ -6216,6 +6635,11 @@ isomorphic-fetch@^3.0.0: node-fetch "^2.6.1" whatwg-fetch "^3.4.1" +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz" @@ -7183,6 +7607,11 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== + jsdom@^16.6.0: version "16.7.0" resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" @@ -7241,7 +7670,7 @@ json-schema-traverse@^1.0.0: resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@^0.4.0: +json-schema@0.4.0, json-schema@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== @@ -7251,6 +7680,11 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + json2mq@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" @@ -7289,6 +7723,16 @@ jsonpointer@^5.0.0: resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz" integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2: version "3.3.3" resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz" @@ -7324,6 +7768,11 @@ language-tags@^1.0.5: dependencies: language-subtag-registry "~0.3.2" +lazy-ass@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== + leven@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" @@ -7355,6 +7804,20 @@ lines-and-columns@^1.1.6: resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +listr2@^3.8.3: + version "3.14.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" + integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.1" + through "^2.3.8" + wrap-ansi "^7.0.0" + loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz" @@ -7416,6 +7879,11 @@ lodash.merge@^4.6.2: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" @@ -7431,6 +7899,24 @@ lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" @@ -7562,7 +8048,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -7620,6 +8106,11 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minimist@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minipass@^3.0.0: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" @@ -7772,7 +8263,7 @@ notistack@^3.0.1: clsx "^1.1.0" goober "^2.0.33" -npm-run-path@^4.0.1: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -7908,14 +8399,14 @@ on-headers@~1.0.2: resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@^1.3.0, once@^1.3.1: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" -onetime@^5.1.2: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -7955,6 +8446,11 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA== + p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" @@ -7990,6 +8486,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-retry@^4.5.0: version "4.6.2" resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz" @@ -8096,6 +8599,11 @@ pdfjs-dist@3.6.172: optionalDependencies: canvas "^2.11.2" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" @@ -8116,7 +8624,7 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatc resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^2.3.0: +pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== @@ -8702,7 +9210,7 @@ prelude-ls@~1.1.2: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== -pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: +pretty-bytes@^5.3.0, pretty-bytes@^5.4.1, pretty-bytes@^5.6.0: version "5.6.0" resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== @@ -8794,6 +9302,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== + proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -8804,6 +9317,14 @@ psl@^1.1.33: resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" @@ -8826,6 +9347,13 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@~6.10.3: + version "6.10.5" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.5.tgz#974715920a80ff6a262264acd2c7e6c2a53282b4" + integrity sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ== + dependencies: + side-channel "^1.0.4" + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" @@ -9215,6 +9743,13 @@ renderkid@^3.0.0: lodash "^4.17.21" strip-ansi "^6.0.1" +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg== + dependencies: + throttleit "^1.0.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" @@ -9291,6 +9826,14 @@ resolve@^2.0.0-next.3: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + retry@^0.13.1: version "0.13.1" resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" @@ -9301,6 +9844,11 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" @@ -9332,12 +9880,19 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^7.5.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -9351,7 +9906,7 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -9449,6 +10004,13 @@ semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: dependencies: lru-cache "^6.0.0" +semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + send@0.18.0: version "0.18.0" resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" @@ -9587,6 +10149,24 @@ slash@^4.0.0: resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + slick-carousel@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/slick-carousel/-/slick-carousel-1.8.1.tgz#a4bfb29014887bb66ce528b90bd0cda262cc8f8d" @@ -9691,6 +10271,21 @@ sprintf-js@~1.0.2: resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +sshpk@^1.14.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + stable@^0.1.8: version "0.1.8" resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" @@ -9891,7 +10486,7 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: +supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -10067,6 +10662,16 @@ throat@^6.0.1: resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz" integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g== + +through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + thunky@^1.0.2: version "1.1.0" resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" @@ -10087,6 +10692,13 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.2: resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tmp@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" @@ -10124,6 +10736,16 @@ tough-cookie@^4.0.0: universalify "^0.2.0" url-parse "^1.5.3" +tough-cookie@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + tr46@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz" @@ -10187,6 +10809,11 @@ tslib@^2.0.3: resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +tslib@^2.1.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410" + integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" @@ -10194,6 +10821,18 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" @@ -10313,6 +10952,11 @@ unquote@~1.1.1: resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz" integrity sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg== +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + upath@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz" @@ -10404,6 +11048,15 @@ vary@~1.1.2: resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" @@ -10873,6 +11526,15 @@ workbox-window@6.5.4: "@types/trusted-types" "^2.0.2" workbox-core "6.5.4" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" @@ -10986,6 +11648,14 @@ yargs@^17.3.1: y18n "^5.0.5" yargs-parser "^21.1.1" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yn@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" From eaf48cc9a065f3887864eadd4ee144c3eb7e68ff Mon Sep 17 00:00:00 2001 From: nflnkr Date: Mon, 7 Aug 2023 18:59:37 +0300 Subject: [PATCH 20/41] fix privilege type imports --- src/model/customTariffs.ts | 3 ++- src/model/privilege.ts | 19 ++----------------- .../TariffConstructor/CustomTariffCard.tsx | 3 +-- src/pages/TariffConstructor/TariffItem.tsx | 3 +-- src/utils/declension.ts | 4 ++-- 5 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/model/customTariffs.ts b/src/model/customTariffs.ts index 19fd7dd..726d332 100644 --- a/src/model/customTariffs.ts +++ b/src/model/customTariffs.ts @@ -1,4 +1,5 @@ -import { PrivilegeWithAmount, PrivilegeWithoutPrice } from "./privilege"; +import { PrivilegeWithAmount } from "@frontend/kitui"; +import { PrivilegeWithoutPrice } from "./privilege"; type ServiceKey = string; diff --git a/src/model/privilege.ts b/src/model/privilege.ts index fdedde3..47aa509 100644 --- a/src/model/privilege.ts +++ b/src/model/privilege.ts @@ -1,21 +1,6 @@ -export interface Privilege { - _id: string; - name: string; - privilegeId: string; - serviceKey: string; - description: string; - type: "day" | "count"; - value: PrivilegeValueType; - price: number; - updatedAt?: string; - isDeleted?: boolean; - createdAt?: string; -}; +import { Privilege, PrivilegeWithAmount } from "@frontend/kitui"; + export type ServiceKeyToPrivilegesMap = Record; -export type PrivilegeValueType = "шаблон" | "день" | "МБ"; - -export type PrivilegeWithAmount = Omit & { amount: number; }; - export type PrivilegeWithoutPrice = Omit; diff --git a/src/pages/TariffConstructor/CustomTariffCard.tsx b/src/pages/TariffConstructor/CustomTariffCard.tsx index e510383..2481ae0 100644 --- a/src/pages/TariffConstructor/CustomTariffCard.tsx +++ b/src/pages/TariffConstructor/CustomTariffCard.tsx @@ -6,7 +6,6 @@ import { useTheme, } from "@mui/material"; import CustomButton from "../../components/CustomButton"; -import { Privilege } from "@root/model/privilege"; import TariffPrivilegeSlider from "./TariffItem"; import { createAndSendTariff, @@ -14,7 +13,7 @@ import { } from "@root/stores/customTariffs"; import { cardShadow } from "@root/utils/themes/shadow"; import { currencyFormatter } from "@root/utils/currencyFormatter"; -import { devlog, getMessageFromFetchError } from "@frontend/kitui"; +import { Privilege, devlog, getMessageFromFetchError } from "@frontend/kitui"; import { enqueueSnackbar } from "notistack"; interface Props { diff --git a/src/pages/TariffConstructor/TariffItem.tsx b/src/pages/TariffConstructor/TariffItem.tsx index 24b2b84..a758e94 100644 --- a/src/pages/TariffConstructor/TariffItem.tsx +++ b/src/pages/TariffConstructor/TariffItem.tsx @@ -1,4 +1,4 @@ -import { useThrottle } from "@frontend/kitui"; +import { Privilege, PrivilegeValueType, useThrottle } from "@frontend/kitui"; import { Box, SliderProps, @@ -10,7 +10,6 @@ import { CustomSlider } from "@root/components/CustomSlider"; import NumberInputWithUnitAdornment from "@root/components/NumberInputWithUnitAdornment"; import CalendarIcon from "@root/components/icons/CalendarIcon"; import PieChartIcon from "@root/components/icons/PieChartIcon"; -import { Privilege, PrivilegeValueType } from "@root/model/privilege"; import { useCartStore } from "@root/stores/cart"; import { setCustomTariffsUserValue, diff --git a/src/utils/declension.ts b/src/utils/declension.ts index 01618e1..f99a4ad 100644 --- a/src/utils/declension.ts +++ b/src/utils/declension.ts @@ -1,4 +1,4 @@ -import { PrivilegeValueType } from "@root/model/privilege"; +import { PrivilegeValueType } from "@frontend/kitui"; function declension(number: number, declensions: string[], cases = [2, 0, 1, 1, 1, 2]) { @@ -22,4 +22,4 @@ export function getDeclension(number: number, word: PrivilegeValueType | "мес case "МБ": return "МБ"; } -}; \ No newline at end of file +}; From cadc903159011ed8220615eb74ce47e679fb37ad Mon Sep 17 00:00:00 2001 From: nflnkr Date: Tue, 8 Aug 2023 15:59:23 +0300 Subject: [PATCH 21/41] refactor hooks --- src/pages/Tariffs/TariffsPage.tsx | 8 ++-- src/utils/hooks/useAllTariffsFetcher.ts | 55 +++++++++++++++++++++++++ src/utils/hooks/useTariffFetcher.ts | 50 ++++++++++++++++++++++ src/utils/hooks/useTariffs.ts | 41 ------------------ 4 files changed, 108 insertions(+), 46 deletions(-) create mode 100644 src/utils/hooks/useAllTariffsFetcher.ts create mode 100644 src/utils/hooks/useTariffFetcher.ts delete mode 100644 src/utils/hooks/useTariffs.ts diff --git a/src/pages/Tariffs/TariffsPage.tsx b/src/pages/Tariffs/TariffsPage.tsx index 2d74ae1..f590646 100644 --- a/src/pages/Tariffs/TariffsPage.tsx +++ b/src/pages/Tariffs/TariffsPage.tsx @@ -2,7 +2,6 @@ import { useState } from "react"; import { useLocation } from "react-router-dom"; import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; import SectionWrapper from "@components/SectionWrapper"; -import { useTariffs } from "@root/utils/hooks/useTariffs"; import { updateTariffs, useTariffStore } from "@root/stores/tariffs"; import { enqueueSnackbar } from "notistack"; import { Select } from "@root/components/Select"; @@ -18,6 +17,7 @@ import { useDiscountStore } from "@root/stores/discounts"; import { useCustomTariffsStore } from "@root/stores/customTariffs"; import { Slider } from "./slider"; import { useCartStore } from "@root/stores/cart"; +import { useAllTariffsFetcher } from "@root/utils/hooks/useAllTariffsFetcher"; const subPages = ["Шаблонизатор", "Опросник", "Сокращатель ссылок"]; @@ -42,10 +42,8 @@ export default function TariffPage() { time: "Тарифы на время", }; - useTariffs({ - apiPage: 0, - tariffsPerPage: 100, - onNewTariffs: updateTariffs, + useAllTariffsFetcher({ + onSuccess: updateTariffs, onError: (error) => { const errorMessage = getMessageFromFetchError(error); if (errorMessage) enqueueSnackbar(errorMessage); diff --git a/src/utils/hooks/useAllTariffsFetcher.ts b/src/utils/hooks/useAllTariffsFetcher.ts new file mode 100644 index 0000000..fbae206 --- /dev/null +++ b/src/utils/hooks/useAllTariffsFetcher.ts @@ -0,0 +1,55 @@ +import { GetTariffsResponse, Tariff, makeRequest } from "@frontend/kitui"; +import { useRef, useLayoutEffect, useEffect } from "react"; + + +export function useAllTariffsFetcher({ + baseUrl = process.env.NODE_ENV === "production" ? "/strator/tariff" : "https://hub.pena.digital/strator/tariff", + onSuccess, + onError, +}: { + baseUrl?: string; + onSuccess: (response: Tariff[]) => void; + onError?: (error: Error) => void; +}) { + const onNewTariffsRef = useRef(onSuccess); + const onErrorRef = useRef(onError); + + useLayoutEffect(() => { + onNewTariffsRef.current = onSuccess; + onErrorRef.current = onError; + }, [onError, onSuccess]); + + useEffect(() => { + const controller = new AbortController(); + + async function getPaginatedTariffs() { + let apiPage = 0; + const tariffsPerPage = 100; + let isDone = false; + + while (!isDone) { + try { + const result = await makeRequest({ + url: baseUrl + `?page=${apiPage}&limit=${tariffsPerPage}`, + method: "get", + useToken: true, + signal: controller.signal, + }); + if (result.tariffs.length > 0) { + onNewTariffsRef.current(result.tariffs); + apiPage++; + } else { + isDone = true; + } + } catch (error) { + onErrorRef.current?.(error as Error); + isDone = true; + } + } + } + + getPaginatedTariffs(); + + return () => controller.abort(); + }, [baseUrl]); +} diff --git a/src/utils/hooks/useTariffFetcher.ts b/src/utils/hooks/useTariffFetcher.ts new file mode 100644 index 0000000..fb4710c --- /dev/null +++ b/src/utils/hooks/useTariffFetcher.ts @@ -0,0 +1,50 @@ +import { Tariff, makeRequest } from "@frontend/kitui"; +import { GetTariffsResponse } from "@root/model/tariff"; +import { useEffect, useLayoutEffect, useRef, useState } from "react"; + + +export function useTariffFetcher({ + baseUrl = process.env.NODE_ENV === "production" ? "/strator/tariff" : "https://hub.pena.digital/strator/tariff", + tariffsPerPage, + apiPage, + onSuccess, + onError, +}: { + baseUrl?: string; + tariffsPerPage: number; + apiPage: number; + 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); + + useLayoutEffect(() => { + onSuccessRef.current = onSuccess; + onErrorRef.current = onError; + }, [onError, onSuccess]); + + useEffect(() => { + const controller = new AbortController(); + + setFetchState("fetching"); + makeRequest({ + url: baseUrl + `?page=${apiPage}&limit=${tariffsPerPage}`, + method: "get", + useToken: true, + signal: controller.signal, + }).then((result) => { + if (result.tariffs.length > 0) { + onSuccessRef.current(result.tariffs); + setFetchState("idle"); + } else setFetchState("all fetched"); + }).catch(error => { + onErrorRef.current?.(error); + }); + + return () => controller.abort(); + }, [apiPage, tariffsPerPage, baseUrl]); + + return fetchState; +} diff --git a/src/utils/hooks/useTariffs.ts b/src/utils/hooks/useTariffs.ts deleted file mode 100644 index 7875392..0000000 --- a/src/utils/hooks/useTariffs.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Tariff, devlog, makeRequest } from "@frontend/kitui"; -import { GetTariffsResponse } from "@root/model/tariff"; -import { useEffect, useLayoutEffect, useRef } from "react"; - - -export function useTariffs({ baseUrl = "https://admin.pena.digital/strator/tariff", tariffsPerPage, apiPage, onNewTariffs, onError }: { - baseUrl?: string; - tariffsPerPage: number; - apiPage: number; - onNewTariffs: (response: Tariff[]) => void; - onError: (error: Error) => void; -}) { - const onNewTariffsRef = useRef(onNewTariffs); - const onErrorRef = useRef(onError); - - useLayoutEffect(() => { - onNewTariffsRef.current = onNewTariffs; - onErrorRef.current = onError; - }, [onError, onNewTariffs]); - - useEffect(() => { - const controller = new AbortController(); - - makeRequest({ - url: baseUrl + `?page=${apiPage}&limit=${tariffsPerPage}`, - method: "get", - useToken: true, - signal: controller.signal, - }).then((result) => { - devlog("Tariffs", result); - if (result.tariffs.length > 0) { - onNewTariffsRef.current(result.tariffs); - } - }).catch(error => { - devlog("Error fetching tariffs", error); - onErrorRef.current(error); - }); - - return () => controller.abort(); - }, [apiPage, tariffsPerPage, baseUrl]); -} From 27d853ac8e08d023e6a89c52628247ce28eec95d Mon Sep 17 00:00:00 2001 From: nflnkr Date: Tue, 8 Aug 2023 15:59:54 +0300 Subject: [PATCH 22/41] add privileges store --- package.json | 2 +- src/index.tsx | 10 +++++++++- src/stores/privileges.ts | 24 ++++++++++++++++++++++++ yarn.lock | 8 ++++---- 4 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 src/stores/privileges.ts diff --git a/package.json b/package.json index a53900d..4eccfc2 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dependencies": { "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", - "@frontend/kitui": "^1.0.17", + "@frontend/kitui": "^1.0.19", "@mui/icons-material": "^5.10.14", "@mui/material": "^5.10.14", "@popperjs/core": "^2.11.8", diff --git a/src/index.tsx b/src/index.tsx index 222fec9..03d2f0b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -24,13 +24,14 @@ import Layout from "./components/Layout"; import { clearUserData, setUser, setUserAccount, useUserStore } from "./stores/user"; import TariffConstructor from "./pages/TariffConstructor/TariffConstructor"; import { useUser } from "./utils/hooks/useUser"; -import { clearAuthToken, getMessageFromFetchError } from "@frontend/kitui"; +import { clearAuthToken, getMessageFromFetchError, usePrivilegeFetcher } from "@frontend/kitui"; import { useUserAccount } from "./utils/hooks/useUserAccount"; import { setCustomTariffs } from "@root/stores/customTariffs"; import { useCustomTariffs } from "@root/utils/hooks/useCustomTariffs"; import { useDiscounts } from "./utils/hooks/useDiscounts"; import { setDiscounts } from "./stores/discounts"; import { pdfjs } from "react-pdf"; +import { setPrivileges } from "./stores/privileges"; pdfjs.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.js", import.meta.url).toString(); @@ -85,6 +86,13 @@ const App = () => { } }); + usePrivilegeFetcher({ + onSuccess: setPrivileges, + onError: error => { + console.log("usePrivilegeFetcher error :>> ", error); + } + }); + if (location.state?.redirectTo) return ; return ( diff --git a/src/stores/privileges.ts b/src/stores/privileges.ts new file mode 100644 index 0000000..e0db849 --- /dev/null +++ b/src/stores/privileges.ts @@ -0,0 +1,24 @@ +import { PrivilegeWithAmount } from "@frontend/kitui"; +import { create } from "zustand"; +import { devtools } from "zustand/middleware"; + + +interface PrivilegeStore { + privileges: PrivilegeWithAmount[]; +} + +const initialState: PrivilegeStore = { + privileges: [], +}; + +const usePrivilegeStore = create()( + devtools( + (get, set) => initialState, + { + name: "Privileges", + enabled: process.env.NODE_ENV === "development", + } + ) +); + +export const setPrivileges = (privileges: PrivilegeStore["privileges"]) => usePrivilegeStore.setState({ privileges }); diff --git a/yarn.lock b/yarn.lock index 23ab222..a7dd621 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1532,10 +1532,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@frontend/kitui@^1.0.17": - version "1.0.17" - resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/@frontend/kitui/-/@frontend/kitui-1.0.17.tgz#a5bddaaa18b168be0e1814d5cfbd86e4030d15af" - integrity sha1-pb3aqhixaL4OGBTVz72G5AMNFa8= +"@frontend/kitui@^1.0.19": + version "1.0.19" + resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/@frontend/kitui/-/@frontend/kitui-1.0.19.tgz#0a1c3e68e1fa595188b8a6a327b1ea709cf6624c" + integrity sha1-Chw+aOH6WVGIuKajJ7HqcJz2Ykw= dependencies: immer "^10.0.2" reconnecting-eventsource "^1.6.2" From 1d6f545e8fac4db492a91deab3d8269bf045150c Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Tue, 8 Aug 2023 18:47:02 +0300 Subject: [PATCH 23/41] feat: support page previous page --- src/components/Menu.tsx | 135 ++++++++++++++------------- src/components/Navbar/DialogMenu.tsx | 3 +- src/components/Navbar/NavbarFull.tsx | 1 + src/pages/Support/Support.tsx | 12 ++- 4 files changed, 80 insertions(+), 71 deletions(-) diff --git a/src/components/Menu.tsx b/src/components/Menu.tsx index b6fd9cd..48a63d8 100644 --- a/src/components/Menu.tsx +++ b/src/components/Menu.tsx @@ -14,7 +14,6 @@ export default function Menu() { const location = useLocation(); const color = location.pathname === "/" ? "white" : "black"; - console.log(location) const arrayMenu: MenuItem[] = [ { @@ -42,49 +41,50 @@ export default function Menu() { overflow: "hidden", }} > - {location.pathname !== "/" ? arrayMenu.map(({ name, url, subMenu = [] }) => ( - setActiveSubMenu(subMenu)} - > - - {name} - - - )) - :arrayMenu.map(({ name, url, subMenu = [] }) => ( - - {name} - - )) - } + {location.pathname !== "/" + ? arrayMenu.map(({ name, url, subMenu = [] }) => ( + setActiveSubMenu(subMenu)} + state={{ previousUrl: location.pathname }} + > + + {name} + + + )) + : arrayMenu.map(({ name, url, subMenu = [] }) => ( + + {name} + + ))} setActiveSubMenu([])} > - {location.pathname !== "/" && activeSubMenu.map(({ name, url }) => ( - - - {name} - - - ))} + {location.pathname !== "/" && + activeSubMenu.map(({ name, url }) => ( + + + {name} + + + ))} ); diff --git a/src/components/Navbar/DialogMenu.tsx b/src/components/Navbar/DialogMenu.tsx index 007add9..4d69d83 100644 --- a/src/components/Navbar/DialogMenu.tsx +++ b/src/components/Navbar/DialogMenu.tsx @@ -85,7 +85,7 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) { key={index} component={Link} to={url} - state={user ? undefined : { backgroundLocation: location }} + state={{ previousUrl: location.pathname }} disableRipple variant="text" onClick={() => @@ -134,6 +134,7 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) { }} to={url} onClick={closeDialogMenu} + state={{ previousUrl: location.pathname }} > (""); const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const ticketId = useParams().ticketId; const ticketApiPage = useTicketStore((state) => state.apiPage); const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage); const token = useToken(); + const location = useLocation(); const fetchState = useTickets({ url: "https://hub.pena.digital/heruvym/getTickets", @@ -44,6 +46,10 @@ export default function Support() { }, []), }); + useEffect(() => { + setPreviousPage(location.state?.previousUrl || "/"); + }, []); + useSSESubscription({ enabled: Boolean(token), url: `https://admin.pena.digital/heruvym/subscribe?Authorization=${token}`, @@ -73,7 +79,7 @@ export default function Support() { }} > Date: Wed, 9 Aug 2023 14:48:21 +0300 Subject: [PATCH 24/41] fix: Support arrow --- src/pages/Support/Support.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Support/Support.tsx b/src/pages/Support/Support.tsx index 37a368b..cfd9154 100644 --- a/src/pages/Support/Support.tsx +++ b/src/pages/Support/Support.tsx @@ -79,7 +79,7 @@ export default function Support() { }} > Date: Wed, 9 Aug 2023 20:10:45 +0300 Subject: [PATCH 25/41] minor fixes --- src/pages/TariffConstructor/TariffConstructor.tsx | 12 ++++++++++++ src/stores/tariffs.ts | 2 +- src/utils/hooks/useAllTariffsFetcher.ts | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/pages/TariffConstructor/TariffConstructor.tsx b/src/pages/TariffConstructor/TariffConstructor.tsx index 9de21aa..fc12204 100644 --- a/src/pages/TariffConstructor/TariffConstructor.tsx +++ b/src/pages/TariffConstructor/TariffConstructor.tsx @@ -7,6 +7,10 @@ import CustomTariffCard from "./CustomTariffCard"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import TotalPrice from "@root/components/TotalPrice"; import { serviceNameByKey } from "@root/utils/serviceKeys"; +import { useAllTariffsFetcher } from "@root/utils/hooks/useAllTariffsFetcher"; +import { updateTariffs } from "@root/stores/tariffs"; +import { getMessageFromFetchError } from "@frontend/kitui"; +import { enqueueSnackbar } from "notistack"; export default function TariffConstructor() { const theme = useTheme(); @@ -30,6 +34,14 @@ export default function TariffConstructor() { 0 ); + useAllTariffsFetcher({ + onSuccess: updateTariffs, + onError: (error) => { + const errorMessage = getMessageFromFetchError(error); + if (errorMessage) enqueueSnackbar(errorMessage); + }, + }); + return ( useTariffStore false, { type: "updateTariffs", - tariffsLength: tariffs.length, + tariffs: tariffs, } ); diff --git a/src/utils/hooks/useAllTariffsFetcher.ts b/src/utils/hooks/useAllTariffsFetcher.ts index fbae206..f820598 100644 --- a/src/utils/hooks/useAllTariffsFetcher.ts +++ b/src/utils/hooks/useAllTariffsFetcher.ts @@ -23,7 +23,7 @@ export function useAllTariffsFetcher({ const controller = new AbortController(); async function getPaginatedTariffs() { - let apiPage = 0; + let apiPage = 1; const tariffsPerPage = 100; let isDone = false; From 52db846e1b5eb2f03110331d8e98320747c8c257 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Wed, 9 Aug 2023 20:14:12 +0300 Subject: [PATCH 26/41] fix custom tariffs --- src/api/tariff.ts | 10 +- src/model/customTariffs.ts | 9 +- .../TariffConstructor/CustomTariffCard.tsx | 246 +++++++++--------- src/stores/customTariffs.ts | 41 +-- 4 files changed, 154 insertions(+), 152 deletions(-) diff --git a/src/api/tariff.ts b/src/api/tariff.ts index f03ad1b..8f0512d 100644 --- a/src/api/tariff.ts +++ b/src/api/tariff.ts @@ -1,10 +1,12 @@ import { Tariff, makeRequest } from "@frontend/kitui"; -import { CreateTariffBody, CustomTariff } from "@root/model/customTariffs"; +import { CreateTariffBody } from "@root/model/customTariffs"; +const apiUrl = process.env.NODE_ENV === "production" ? "/strator" : "https://hub.pena.digital/strator"; + export function createTariff(tariff: CreateTariffBody) { - return makeRequest({ - url: `https://admin.pena.digital/strator/tariff`, + return makeRequest({ + url: `${apiUrl}/tariff`, method: "post", useToken: true, body: tariff, @@ -13,7 +15,7 @@ export function createTariff(tariff: CreateTariffBody) { export function getTariffById(tariffId:string){ return makeRequest({ - url: `https://admin.pena.digital/strator/tariff/${tariffId}`, + url: `${apiUrl}/tariff/${tariffId}`, method: "get", useToken: true, }); diff --git a/src/model/customTariffs.ts b/src/model/customTariffs.ts index 726d332..a85631a 100644 --- a/src/model/customTariffs.ts +++ b/src/model/customTariffs.ts @@ -10,14 +10,9 @@ export type CustomTariffUserValuesMap = Record; -export interface CustomTariff { +export interface CreateTariffBody { name: string; price?: number; isCustom: boolean; - privilegies: PrivilegeWithAmount[]; - updatedAt?: string; - isDeleted?: boolean; - createdAt?: string; + privilegies: PrivilegeWithoutPrice[]; } - -export type CreateTariffBody = Omit & { privilegies: PrivilegeWithoutPrice[]; }; diff --git a/src/pages/TariffConstructor/CustomTariffCard.tsx b/src/pages/TariffConstructor/CustomTariffCard.tsx index 2481ae0..d25a887 100644 --- a/src/pages/TariffConstructor/CustomTariffCard.tsx +++ b/src/pages/TariffConstructor/CustomTariffCard.tsx @@ -1,143 +1,147 @@ import { - Box, - Divider, - Typography, - useMediaQuery, - useTheme, + Box, + Divider, + Typography, + useMediaQuery, + useTheme, } from "@mui/material"; import CustomButton from "../../components/CustomButton"; import TariffPrivilegeSlider from "./TariffItem"; import { - createAndSendTariff, - useCustomTariffsStore, + createAndSendTariff, + useCustomTariffsStore, } from "@root/stores/customTariffs"; import { cardShadow } from "@root/utils/themes/shadow"; import { currencyFormatter } from "@root/utils/currencyFormatter"; import { Privilege, devlog, getMessageFromFetchError } from "@frontend/kitui"; import { enqueueSnackbar } from "notistack"; +import { updateTariffs } from "@root/stores/tariffs"; interface Props { - serviceKey: string; - privileges: Privilege[]; + serviceKey: string; + privileges: Privilege[]; } export default function CustomTariffCard({ serviceKey, privileges }: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const summaryPriceBeforeDiscounts = useCustomTariffsStore( - (state) => state.summaryPriceBeforeDiscountsMap - ); - const summaryPriceAfterDiscounts = useCustomTariffsStore( - (state) => state.summaryPriceAfterDiscountsMap - ); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const summaryPriceBeforeDiscounts = useCustomTariffsStore( + (state) => state.summaryPriceBeforeDiscountsMap + ); + const summaryPriceAfterDiscounts = useCustomTariffsStore( + (state) => state.summaryPriceAfterDiscountsMap + ); - const priceBeforeDiscounts = summaryPriceBeforeDiscounts[serviceKey] ?? 0; - const priceAfterDiscounts = summaryPriceAfterDiscounts[serviceKey] ?? 0; + const priceBeforeDiscounts = summaryPriceBeforeDiscounts[serviceKey] ?? 0; + const priceAfterDiscounts = summaryPriceAfterDiscounts[serviceKey] ?? 0; - async function handleConfirmClick() { - createAndSendTariff(serviceKey) - .then((result) => { - devlog(result); - enqueueSnackbar("Тариф создан"); - }) - .catch((error) => { - const message = getMessageFromFetchError( - error, - "Не удалось создать тариф" - ); - if (message) enqueueSnackbar(message); - }); - } + async function handleConfirmClick() { + createAndSendTariff(serviceKey) + .then((result) => { + devlog(result); + if (result.length === 0) return enqueueSnackbar("Нет тарифов"); + enqueueSnackbar(result.length > 1 ? "Тарифы созданы" : "Тариф создан"); - return ( - - - {privileges.map((privilege) => ( - - ))} - - {!upMd && ( - - )} - + updateTariffs(result); + }) + .catch((error) => { + const message = getMessageFromFetchError( + error, + "Не удалось создать тариф" + ); + if (message) enqueueSnackbar(message); + }); + } + + return ( - - Чем больше пакеты, тем дешевле подписки и опции{" "} - + + {privileges.map((privilege) => ( + + ))} + + {!upMd && ( + + )} + + + + Чем больше пакеты, тем дешевле подписки и опции{" "} + + + + Сумма с учетом скидки + + + + {currencyFormatter.format(priceAfterDiscounts / 100)} + + + {currencyFormatter.format(priceBeforeDiscounts / 100)} + + + + Выбрать + + - - Сумма с учетом скидки - - - - {currencyFormatter.format(priceAfterDiscounts / 100)} - - - {currencyFormatter.format(priceBeforeDiscounts / 100)} - - - - Выбрать - - - - ); + ); } diff --git a/src/stores/customTariffs.ts b/src/stores/customTariffs.ts index 05eb91a..e61cf54 100644 --- a/src/stores/customTariffs.ts +++ b/src/stores/customTariffs.ts @@ -1,10 +1,10 @@ import { createTariff } from "@root/api/tariff"; -import { CustomTariffUserValuesMap, ServiceKeyToPriceMap } from "@root/model/customTariffs"; +import { CreateTariffBody, CustomTariffUserValuesMap, ServiceKeyToPriceMap } from "@root/model/customTariffs"; import { ServiceKeyToPrivilegesMap, PrivilegeWithoutPrice } from "@root/model/privilege"; import { produce } from "immer"; import { create } from "zustand"; import { devtools, persist } from "zustand/middleware"; -import { Discount, findCartDiscount, findLoyaltyDiscount, findPrivilegeDiscount, findServiceDiscount } from "@frontend/kitui"; +import { Discount, Tariff, findCartDiscount, findLoyaltyDiscount, findPrivilegeDiscount, findServiceDiscount } from "@frontend/kitui"; interface CustomTariffsStore { @@ -58,12 +58,12 @@ export const setCustomTariffsUserValue = ( let priceWithoutDiscounts = 0; let priceAfterDiscounts = 0; - state.customTariffsMap[serviceKey].forEach(tariff => { - const amount = state.userValuesMap[serviceKey]?.[tariff._id] ?? 0; - priceWithoutDiscounts += tariff.price * amount; + state.customTariffsMap[serviceKey].forEach(privilege => { + const amount = state.userValuesMap[serviceKey]?.[privilege._id] ?? 0; + priceWithoutDiscounts += privilege.price * amount; - const discount = findPrivilegeDiscount(tariff.privilegeId, tariff.price * amount, discounts); - priceAfterDiscounts += tariff.price * amount * discount.factor; + const discount = findPrivilegeDiscount(privilege.privilegeId, privilege.price * amount, discounts); + priceAfterDiscounts += privilege.price * amount * discount.factor; }); const serviceDiscount = findServiceDiscount(serviceKey, priceAfterDiscounts, discounts); @@ -80,10 +80,10 @@ export const setCustomTariffsUserValue = ( }) ); -export const createAndSendTariff = (serviceKey: string) => { +export const createAndSendTariff = async (serviceKey: string) => { const state = useCustomTariffsStore.getState(); - const privilegies: PrivilegeWithoutPrice[] = []; + const tariffs: CreateTariffBody[] = []; Object.entries(state.userValuesMap[serviceKey]).forEach(([privilegeId, userValue]) => { if (userValue === 0) return; @@ -91,25 +91,26 @@ export const createAndSendTariff = (serviceKey: string) => { const privilege = state.customTariffsMap[serviceKey].find(p => p._id === privilegeId); if (!privilege) throw new Error(`Privilege not found: ${privilegeId}`); - const p2 = { + const p2: PrivilegeWithoutPrice = { ...privilege, privilegeId: privilege._id, amount: userValue, - } as PrivilegeWithoutPrice; - delete (p2 as any).price; + }; - privilegies.push(p2); + tariffs.push({ + name: privilege.name, + isCustom: true, + privilegies: [p2], + }); }); - const name = [...privilegies.map(p => p.name), new Date().toISOString()].join(", "); + if (tariffs.length === 0) return []; - const tariff = { - name, - isCustom: true, - privilegies, - }; + const results = await Promise.allSettled(tariffs.map(tariff => createTariff(tariff))); - return createTariff(tariff); + const fulfilled = results.filter((p): p is PromiseFulfilledResult => p.status === "fulfilled"); + + return fulfilled.map(p => p.value); }; export const clearCustomTariffs = () => useCustomTariffsStore.setState({ ...initialState }); From f20080ef2b6e7a0f8704bf082754570e19e2cd82 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Thu, 10 Aug 2023 12:26:44 +0300 Subject: [PATCH 27/41] fix: cart redesign --- src/assets/Icons/cross.svg | 3 + src/components/CustomWrapperDrawer.tsx | 324 ++++++++++++++----------- src/components/Drawers.tsx | 20 +- src/pages/Basket/CustomWrapper.tsx | 40 ++- 4 files changed, 217 insertions(+), 170 deletions(-) create mode 100644 src/assets/Icons/cross.svg diff --git a/src/assets/Icons/cross.svg b/src/assets/Icons/cross.svg new file mode 100644 index 0000000..a39e226 --- /dev/null +++ b/src/assets/Icons/cross.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/CustomWrapperDrawer.tsx b/src/components/CustomWrapperDrawer.tsx index 4d9fa1c..826b9c6 100644 --- a/src/components/CustomWrapperDrawer.tsx +++ b/src/components/CustomWrapperDrawer.tsx @@ -1,157 +1,207 @@ import { useState } from "react"; -import { Box, SvgIcon, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { + Box, + SvgIcon, + IconButton, + Typography, + useMediaQuery, + useTheme, +} from "@mui/material"; import ClearIcon from "@mui/icons-material/Clear"; import { currencyFormatter } from "@root/utils/currencyFormatter"; import { removeTariffFromCart } from "@root/stores/user"; import { enqueueSnackbar } from "notistack"; import { ServiceCartData, getMessageFromFetchError } from "@frontend/kitui"; +import ExpandIcon from "@components/icons/ExpandIcon"; +import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg"; -const name: Record = { templategen: "Шаблонизатор", squiz: "Опросник", reducer: "Скоращатель ссылок" }; +import type { MouseEvent } from "react"; + +const name: Record = { + templategen: "Шаблонизатор", + squiz: "Опросник", + reducer: "Скоращатель ссылок", +}; interface Props { - serviceData: ServiceCartData; + serviceData: ServiceCartData; } export default function CustomWrapperDrawer({ serviceData }: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const upSm = useMediaQuery(theme.breakpoints.up("sm")); - const [isExpanded, setIsExpanded] = useState(false); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const [isExpanded, setIsExpanded] = useState(false); - function handleItemDeleteClick(tariffId: string) { - removeTariffFromCart(tariffId).then(() => { - enqueueSnackbar("Тариф удален"); - }).catch(error => { - const message = getMessageFromFetchError(error); - if (message) enqueueSnackbar(message); - }); - } + function handleItemDeleteClick(tariffId: string) { + removeTariffFromCart(tariffId) + .then(() => { + enqueueSnackbar("Тариф удален"); + }) + .catch((error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); + }); + } - return ( - - - setIsExpanded((prev) => !prev)} - sx={{ - height: "72px", + const deleteService = (event: MouseEvent) => { + event.stopPropagation(); - display: "flex", - alignItems: "center", - justifyContent: "space-between", - cursor: "pointer", - userSelect: "none", - }} - > - - {name[serviceData.serviceKey]} - + setIsExpanded(false); - - - {currencyFormatter.format(serviceData.price / 100)} - - - - - {isExpanded && - serviceData.privileges.map(privilege => ( - - - {privilege.description} - - - - {currencyFormatter.format(privilege.price / 100)} - - - handleItemDeleteClick(privilege.tariffId)} - component={ClearIcon} - /> - - - ))} - - + serviceData.privileges.forEach(({ tariffId }) => + handleItemDeleteClick(tariffId) ); + }; + + return ( + + + setIsExpanded((prev) => !prev)} + sx={{ + height: "72px", + display: "flex", + gap: "10px", + alignItems: "center", + justifyContent: "space-between", + cursor: "pointer", + userSelect: "none", + }} + > + + + + {name[serviceData.serviceKey]} + + + + {currencyFormatter.format(serviceData.price / 100)} + + + + + + + + {isExpanded && + serviceData.privileges.map((privilege) => ( + + + {privilege.description} + + + + {currencyFormatter.format(privilege.price / 100)} + + handleItemDeleteClick(privilege.tariffId)} + component={ClearIcon} + /> + + + ))} + + + ); } diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index d314b14..1f4ee61 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -6,11 +6,9 @@ import { useTheme, Box, IconButton, - SvgIcon, Badge, } from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; -import ClearIcon from "@mui/icons-material/Clear"; import { useTickets } from "@frontend/kitui"; import SectionWrapper from "./SectionWrapper"; import CustomWrapperDrawer from "./CustomWrapperDrawer"; @@ -34,6 +32,7 @@ import { import { ReactComponent as BellIcon } from "@root/assets/Icons/bell.svg"; import { ReactComponent as CartIcon } from "@root/assets/Icons/cart.svg"; +import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg"; export default function Drawers() { const [openNotificationsModal, setOpenNotificationsModal] = @@ -227,15 +226,22 @@ export default function Drawers() { Корзина - + {cart.services.map((serviceData) => ( - + + + ))} {privilege.description} @@ -166,27 +163,18 @@ export default function CustomWrapper({ serviceData }: Props) { > {currencyFormatter.format(privilege.price / 100)} - {upSm ? ( - handleItemDeleteClick(privilege.tariffId)} - sx={{ - color: theme.palette.text.secondary, - borderBottom: `1px solid ${theme.palette.text.secondary}`, - width: "max-content", - lineHeight: "19px", - cursor: "pointer", - }} - > - Удалить - - ) : ( - handleItemDeleteClick(privilege.tariffId)} - component={ClearIcon} - sx={{ fill: "#7E2AEA" }} - /> - )} + + handleItemDeleteClick(privilege.tariffId)} + > + ))} From dbc2657d0555d956b3ce9cc13dbcb2617d051f48 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Thu, 10 Aug 2023 13:49:46 +0300 Subject: [PATCH 28/41] fix: remove tariffs --- src/components/CustomWrapperDrawer.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/CustomWrapperDrawer.tsx b/src/components/CustomWrapperDrawer.tsx index 826b9c6..f938319 100644 --- a/src/components/CustomWrapperDrawer.tsx +++ b/src/components/CustomWrapperDrawer.tsx @@ -46,14 +46,18 @@ export default function CustomWrapperDrawer({ serviceData }: Props) { }); } - const deleteService = (event: MouseEvent) => { + const deleteService = async (event: MouseEvent) => { event.stopPropagation(); setIsExpanded(false); - serviceData.privileges.forEach(({ tariffId }) => - handleItemDeleteClick(tariffId) - ); + for (const { tariffId } of serviceData.privileges) { + try { + await removeTariffFromCart(tariffId); + } catch {} + } + + enqueueSnackbar("Тарифы удален"); }; return ( From 2036d4894b8f5295cfd9d5128c0e43a8b7bc7951 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Thu, 10 Aug 2023 14:55:49 +0300 Subject: [PATCH 29/41] fix: Basket --- src/pages/Basket/Basket.tsx | 120 +++++++++++++++++------------ src/pages/Basket/CustomWrapper.tsx | 54 +++++++++---- 2 files changed, 111 insertions(+), 63 deletions(-) diff --git a/src/pages/Basket/Basket.tsx b/src/pages/Basket/Basket.tsx index be7bd5e..0982a7d 100644 --- a/src/pages/Basket/Basket.tsx +++ b/src/pages/Basket/Basket.tsx @@ -1,4 +1,10 @@ -import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { + Box, + IconButton, + Typography, + useMediaQuery, + useTheme, +} from "@mui/material"; import SectionWrapper from "@components/SectionWrapper"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import TotalPrice from "@components/TotalPrice"; @@ -6,56 +12,72 @@ import CustomWrapper from "./CustomWrapper"; import { useCart } from "@root/utils/hooks/useCart"; import { useCustomTariffsStore } from "@root/stores/customTariffs"; - export default function Basket() { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const cart = useCart(); - const summaryPriceBeforeDiscountsMap = useCustomTariffsStore(state => state.summaryPriceBeforeDiscountsMap); - const summaryPriceAfterDiscountsMap = useCustomTariffsStore(state => state.summaryPriceAfterDiscountsMap); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const cart = useCart(); + const summaryPriceBeforeDiscountsMap = useCustomTariffsStore( + (state) => state.summaryPriceBeforeDiscountsMap + ); + const summaryPriceAfterDiscountsMap = useCustomTariffsStore( + (state) => state.summaryPriceAfterDiscountsMap + ); - const basePrice = Object.values(summaryPriceBeforeDiscountsMap).reduce((a, e) => a + e, 0); - const discountedPrice = Object.values(summaryPriceAfterDiscountsMap).reduce((a, e) => a + e, 0); + const basePrice = Object.values(summaryPriceBeforeDiscountsMap).reduce( + (a, e) => a + e, + 0 + ); + const discountedPrice = Object.values(summaryPriceAfterDiscountsMap).reduce( + (a, e) => a + e, + 0 + ); - const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts + basePrice; - const totalPriceAfterDiscounts = cart.priceAfterDiscounts + discountedPrice; + const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts + basePrice; + const totalPriceAfterDiscounts = cart.priceAfterDiscounts + discountedPrice; - return ( - - - {!upMd && ( - - - - )} - - Корзина - - - - {cart.services.map(serviceData => - - )} - - - - ); + return ( + + + {!upMd && ( + + + + )} + + Корзина + + + + {cart.services.map((serviceData) => ( + + ))} + + + + ); } diff --git a/src/pages/Basket/CustomWrapper.tsx b/src/pages/Basket/CustomWrapper.tsx index b5af45e..0aec26d 100644 --- a/src/pages/Basket/CustomWrapper.tsx +++ b/src/pages/Basket/CustomWrapper.tsx @@ -3,6 +3,7 @@ import { Box, SvgIcon, Typography, + IconButton, useMediaQuery, useTheme, } from "@mui/material"; @@ -14,6 +15,10 @@ import { removeTariffFromCart } from "@root/stores/user"; import { enqueueSnackbar } from "notistack"; import { ServiceCartData, getMessageFromFetchError } from "@frontend/kitui"; +import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg"; + +import type { MouseEvent } from "react"; + const name: Record = { templategen: "Шаблонизатор", squiz: "Опросник", @@ -41,6 +46,20 @@ export default function CustomWrapper({ serviceData }: Props) { }); } + const deleteService = async (event: MouseEvent) => { + event.stopPropagation(); + + setIsExpanded(false); + + for (const { tariffId } of serviceData.privileges) { + try { + await removeTariffFromCart(tariffId); + } catch {} + } + + enqueueSnackbar("Тарифы удален"); + }; + return ( + + + {name[serviceData.serviceKey]} - {currencyFormatter.format(serviceData.price / 100)} - - - + + + {isExpanded && serviceData.privileges.map((privilege) => ( From c031df17408d40b81e3df709de7710ab77d71984 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 11 Aug 2023 12:38:05 +0300 Subject: [PATCH 30/41] fix tariff card price calc --- package.json | 2 +- src/pages/Tariffs/TariffsPage.tsx | 256 +++++++++++++++--------------- src/utils/calcTariffPrices.ts | 47 ++---- yarn.lock | 8 +- 4 files changed, 143 insertions(+), 170 deletions(-) diff --git a/package.json b/package.json index 4eccfc2..f2a89de 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dependencies": { "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", - "@frontend/kitui": "^1.0.19", + "@frontend/kitui": "^1.0.20", "@mui/icons-material": "^5.10.14", "@mui/material": "^5.10.14", "@popperjs/core": "^2.11.8", diff --git a/src/pages/Tariffs/TariffsPage.tsx b/src/pages/Tariffs/TariffsPage.tsx index f590646..cfce800 100644 --- a/src/pages/Tariffs/TariffsPage.tsx +++ b/src/pages/Tariffs/TariffsPage.tsx @@ -10,11 +10,10 @@ import TariffCard from "./TariffCard"; import NumberIcon from "@root/components/NumberIcon"; import { currencyFormatter } from "@root/utils/currencyFormatter"; import { calcIndividualTariffPrices } from "@root/utils/calcTariffPrices"; -import { getMessageFromFetchError } from "@frontend/kitui"; +import { Tariff, getMessageFromFetchError } from "@frontend/kitui"; import FreeTariffCard from "./FreeTariffCard"; import { addTariffToCart, useUserStore } from "@root/stores/user"; import { useDiscountStore } from "@root/stores/discounts"; -import { useCustomTariffsStore } from "@root/stores/customTariffs"; import { Slider } from "./slider"; import { useCartStore } from "@root/stores/cart"; import { useAllTariffsFetcher } from "@root/utils/hooks/useAllTariffsFetcher"; @@ -22,141 +21,138 @@ import { useAllTariffsFetcher } from "@root/utils/hooks/useAllTariffsFetcher"; const subPages = ["Шаблонизатор", "Опросник", "Сокращатель ссылок"]; export default function TariffPage() { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const isMobile = useMediaQuery(theme.breakpoints.down(600)); - const location = useLocation(); - const tariffs = useTariffStore((state) => state.tariffs); - const [selectedItem, setSelectedItem] = useState(0); - const discounts = useDiscountStore((state) => state.discounts); - const customTariffs = useCustomTariffsStore( - (state) => state.customTariffsMap - ); - const purchasesAmount = - useUserStore((state) => state.userAccount?.wallet.purchasesAmount) ?? 0; - const cart = useCartStore((state) => state.cart); - const unit: string = String(location.pathname).slice(9); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const isMobile = useMediaQuery(theme.breakpoints.down(600)); + const location = useLocation(); + const tariffs = useTariffStore((state) => state.tariffs); + const [selectedItem, setSelectedItem] = useState(0); + const discounts = useDiscountStore((state) => state.discounts); + const purchasesAmount = useUserStore((state) => state.userAccount?.wallet.purchasesAmount) ?? 0; + const cartTariffMap = useCartStore((state) => state.cartTariffMap); - const StepperText: Record = { - volume: "Тарифы на объём", - time: "Тарифы на время", - }; + const unit: string = String(location.pathname).slice(9); + const currentTariffs = Object.values(cartTariffMap).filter((tariff): tariff is Tariff => typeof tariff === "object"); - useAllTariffsFetcher({ - onSuccess: updateTariffs, - onError: (error) => { - const errorMessage = getMessageFromFetchError(error); - if (errorMessage) enqueueSnackbar(errorMessage); - }, - }); + const StepperText: Record = { + volume: "Тарифы на объём", + time: "Тарифы на время", + }; - function handleTariffItemClick(tariffId: string) { - addTariffToCart(tariffId) - .then(() => { - enqueueSnackbar("Тариф добавлен в корзину"); - }) - .catch((error) => { - const message = getMessageFromFetchError(error); - if (message) enqueueSnackbar(message); - }); - } + useAllTariffsFetcher({ + onSuccess: updateTariffs, + onError: (error) => { + const errorMessage = getMessageFromFetchError(error); + if (errorMessage) enqueueSnackbar(errorMessage); + }, + }); - const filteredTariffs = tariffs.filter((tariff) => { - return ( - tariff.privilegies.map((p) => p.type).includes("day") === - (unit === "time") && !tariff.isDeleted - ); - }); + function handleTariffItemClick(tariffId: string) { + addTariffToCart(tariffId) + .then(() => { + enqueueSnackbar("Тариф добавлен в корзину"); + }) + .catch((error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); + }); + } - const tariffElements = filteredTariffs.map((tariff, index) => { - const { price, tariffPriceAfterDiscounts } = calcIndividualTariffPrices( - tariff, - discounts, - customTariffs, - purchasesAmount, - cart - ); + const filteredTariffs = tariffs.filter((tariff) => { + return ( + tariff.privilegies.map((p) => p.type).includes("day") === + (unit === "time") && !tariff.isDeleted + ); + }); + + const tariffElements = filteredTariffs.map((tariff, index) => { + const { priceBeforeDiscounts, priceAfterDiscounts } = calcIndividualTariffPrices( + tariff, + discounts, + purchasesAmount, + currentTariffs, + ); + + return ( + + } + buttonProps={{ + text: "Выбрать", + onClick: () => handleTariffItemClick(tariff._id), + }} + headerText={tariff.name} + text={tariff.privilegies.map((p) => `${p.name} - ${p.amount}`)} + price={ + <> + {priceBeforeDiscounts !== undefined && priceBeforeDiscounts !== priceAfterDiscounts && ( + + {currencyFormatter.format(priceBeforeDiscounts / 100)} + + )} + {priceAfterDiscounts !== undefined && ( + + {currencyFormatter.format(priceAfterDiscounts / 100)} + + )} + + } + /> + ); + }); + + if (tariffElements.length < 6) + tariffElements.push(); + else tariffElements.splice(5, 0, ); return ( - - } - buttonProps={{ - text: "Выбрать", - onClick: () => handleTariffItemClick(tariff._id), - }} - headerText={tariff.name} - text={tariff.privilegies.map((p) => `${p.name} - ${p.amount}`)} - price={ - <> - {price !== undefined && price !== tariffPriceAfterDiscounts && ( - - {currencyFormatter.format(price / 100)} - + + + {StepperText[unit]} + + {isMobile ? ( + - ) : ( - - )} - - {tariffElements} - - - Ранее вы покупали - - - - ); } diff --git a/src/utils/calcTariffPrices.ts b/src/utils/calcTariffPrices.ts index 5c69512..d575612 100644 --- a/src/utils/calcTariffPrices.ts +++ b/src/utils/calcTariffPrices.ts @@ -1,47 +1,24 @@ -import { ServiceKeyToPrivilegesMap } from "@root/model/privilege"; -import { CartData, Discount, Tariff, findCartDiscount, findLoyaltyDiscount, findPrivilegeDiscount, findServiceDiscount } from "@frontend/kitui"; - +import { Discount, Tariff, findDiscountFactor } from "@frontend/kitui"; +import { calcCart } from "./calcCart/calcCart"; export function calcIndividualTariffPrices( tariff: Tariff, discounts: Discount[], - privilegies: ServiceKeyToPrivilegesMap, purchasesAmount: number, - cart: CartData, + currentTariffs: Tariff[], ): { - price: number | undefined; - tariffPriceAfterDiscounts: number | undefined; + priceBeforeDiscounts: number; + priceAfterDiscounts: number; } { - let price = tariff.price || tariff.privilegies.reduce((sum, privilege) => sum + privilege.amount * privilege.price, 0); + const priceBeforeDiscounts = tariff.price || tariff.privilegies.reduce((sum, privilege) => sum + privilege.amount * privilege.price, 0); + let priceAfterDiscounts = priceBeforeDiscounts; - let tariffPriceAfterDiscounts = tariff.privilegies.reduce((sum, privilege) => { - let privilegePrice = privilege.amount * privilege.price; + const cart = calcCart([...currentTariffs, tariff], discounts, purchasesAmount); - let realprivilegie = privilegies[privilege.serviceKey]?.find(e => e._id === privilege.privilegeId); - if (realprivilegie) privilege.privilegeId = realprivilegie.privilegeId; + cart.allAppliedDiscounts.forEach(discount => { + priceAfterDiscounts *= findDiscountFactor(discount); + }); - const privilegeDiscount = findPrivilegeDiscount(privilege.privilegeId, privilege.price * privilege.amount, discounts); - privilegePrice *= privilegeDiscount.factor; - - const serviceCartData = cart.services.find(e => e.serviceKey === privilege.serviceKey); - let serviceprice = 0; - if (serviceCartData) serviceprice = serviceCartData.price; - - const serviceDiscount = findServiceDiscount(privilege.serviceKey, privilegePrice + serviceprice, discounts); - if (serviceDiscount) privilegePrice *= serviceDiscount.factor; - - return sum + privilegePrice; - }, 0); - - const cartDiscount = findCartDiscount(tariffPriceAfterDiscounts + cart.priceAfterDiscounts, discounts); - tariffPriceAfterDiscounts *= cartDiscount.factor; - - const loyalDiscount = findLoyaltyDiscount(purchasesAmount, discounts); - tariffPriceAfterDiscounts *= loyalDiscount.factor; - - return { - price, - tariffPriceAfterDiscounts: tariffPriceAfterDiscounts, - }; + return { priceBeforeDiscounts, priceAfterDiscounts }; } diff --git a/yarn.lock b/yarn.lock index a7dd621..1aa3efc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1532,10 +1532,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@frontend/kitui@^1.0.19": - version "1.0.19" - resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/@frontend/kitui/-/@frontend/kitui-1.0.19.tgz#0a1c3e68e1fa595188b8a6a327b1ea709cf6624c" - integrity sha1-Chw+aOH6WVGIuKajJ7HqcJz2Ykw= +"@frontend/kitui@^1.0.20": + version "1.0.20" + resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/@frontend/kitui/-/@frontend/kitui-1.0.20.tgz#b6713be932a8a6d36d450b347d415988de729bba" + integrity sha1-tnE76TKoptNtRQs0fUFZiN5ym7o= dependencies: immer "^10.0.2" reconnecting-eventsource "^1.6.2" From 9c7aaaef05878055c8bb3f1f75896c5b36b2a6ef Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 11 Aug 2023 12:39:50 +0300 Subject: [PATCH 31/41] rename custom tariff store field --- src/pages/TariffConstructor/TariffConstructor.tsx | 2 +- src/stores/customTariffs.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/TariffConstructor/TariffConstructor.tsx b/src/pages/TariffConstructor/TariffConstructor.tsx index fc12204..a6f8624 100644 --- a/src/pages/TariffConstructor/TariffConstructor.tsx +++ b/src/pages/TariffConstructor/TariffConstructor.tsx @@ -16,7 +16,7 @@ export default function TariffConstructor() { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const customTariffs = useCustomTariffsStore( - (state) => state.customTariffsMap + (state) => state.privilegeByService ); const summaryPriceBeforeDiscountsMap = useCustomTariffsStore( (state) => state.summaryPriceBeforeDiscountsMap diff --git a/src/stores/customTariffs.ts b/src/stores/customTariffs.ts index e61cf54..7f81c41 100644 --- a/src/stores/customTariffs.ts +++ b/src/stores/customTariffs.ts @@ -8,14 +8,14 @@ import { Discount, Tariff, findCartDiscount, findLoyaltyDiscount, findPrivilegeD interface CustomTariffsStore { - customTariffsMap: ServiceKeyToPrivilegesMap; + privilegeByService: ServiceKeyToPrivilegesMap; userValuesMap: CustomTariffUserValuesMap; summaryPriceBeforeDiscountsMap: ServiceKeyToPriceMap; summaryPriceAfterDiscountsMap: ServiceKeyToPriceMap; } const initialState: CustomTariffsStore = { - customTariffsMap: {}, + privilegeByService: {}, userValuesMap: {}, summaryPriceBeforeDiscountsMap: {}, summaryPriceAfterDiscountsMap: {}, @@ -41,7 +41,7 @@ export const useCustomTariffsStore = create()( ) ); -export const setCustomTariffs = (customTariffs: ServiceKeyToPrivilegesMap) => useCustomTariffsStore.setState({ customTariffsMap: customTariffs }); +export const setCustomTariffs = (customTariffs: ServiceKeyToPrivilegesMap) => useCustomTariffsStore.setState({ privilegeByService: customTariffs }); export const setCustomTariffsUserValue = ( serviceKey: string, @@ -58,7 +58,7 @@ export const setCustomTariffsUserValue = ( let priceWithoutDiscounts = 0; let priceAfterDiscounts = 0; - state.customTariffsMap[serviceKey].forEach(privilege => { + state.privilegeByService[serviceKey].forEach(privilege => { const amount = state.userValuesMap[serviceKey]?.[privilege._id] ?? 0; priceWithoutDiscounts += privilege.price * amount; @@ -88,7 +88,7 @@ export const createAndSendTariff = async (serviceKey: string) => { Object.entries(state.userValuesMap[serviceKey]).forEach(([privilegeId, userValue]) => { if (userValue === 0) return; - const privilege = state.customTariffsMap[serviceKey].find(p => p._id === privilegeId); + const privilege = state.privilegeByService[serviceKey].find(p => p._id === privilegeId); if (!privilege) throw new Error(`Privilege not found: ${privilegeId}`); const p2: PrivilegeWithoutPrice = { From 2dfc60f355bbe872bf1eb8919968183c8e3837d7 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 11 Aug 2023 13:50:30 +0300 Subject: [PATCH 32/41] fix custom tariff creation --- .../TariffConstructor/CustomTariffCard.tsx | 5 +-- src/stores/customTariffs.ts | 38 +++++++++---------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/pages/TariffConstructor/CustomTariffCard.tsx b/src/pages/TariffConstructor/CustomTariffCard.tsx index d25a887..c1b7dd5 100644 --- a/src/pages/TariffConstructor/CustomTariffCard.tsx +++ b/src/pages/TariffConstructor/CustomTariffCard.tsx @@ -39,10 +39,9 @@ export default function CustomTariffCard({ serviceKey, privileges }: Props) { createAndSendTariff(serviceKey) .then((result) => { devlog(result); - if (result.length === 0) return enqueueSnackbar("Нет тарифов"); - enqueueSnackbar(result.length > 1 ? "Тарифы созданы" : "Тариф создан"); + enqueueSnackbar("Тариф создан"); - updateTariffs(result); + updateTariffs([result]); }) .catch((error) => { const message = getMessageFromFetchError( diff --git a/src/stores/customTariffs.ts b/src/stores/customTariffs.ts index 7f81c41..cec2d49 100644 --- a/src/stores/customTariffs.ts +++ b/src/stores/customTariffs.ts @@ -1,10 +1,10 @@ import { createTariff } from "@root/api/tariff"; -import { CreateTariffBody, CustomTariffUserValuesMap, ServiceKeyToPriceMap } from "@root/model/customTariffs"; -import { ServiceKeyToPrivilegesMap, PrivilegeWithoutPrice } from "@root/model/privilege"; +import { CustomTariffUserValuesMap, ServiceKeyToPriceMap } from "@root/model/customTariffs"; +import { ServiceKeyToPrivilegesMap } from "@root/model/privilege"; import { produce } from "immer"; import { create } from "zustand"; import { devtools, persist } from "zustand/middleware"; -import { Discount, Tariff, findCartDiscount, findLoyaltyDiscount, findPrivilegeDiscount, findServiceDiscount } from "@frontend/kitui"; +import { Discount, PrivilegeWithAmount, findCartDiscount, findLoyaltyDiscount, findPrivilegeDiscount, findServiceDiscount } from "@frontend/kitui"; interface CustomTariffsStore { @@ -80,37 +80,35 @@ export const setCustomTariffsUserValue = ( }) ); -export const createAndSendTariff = async (serviceKey: string) => { +export const createAndSendTariff = (serviceKey: string) => { const state = useCustomTariffsStore.getState(); - const tariffs: CreateTariffBody[] = []; + const privilegies: PrivilegeWithAmount[] = []; Object.entries(state.userValuesMap[serviceKey]).forEach(([privilegeId, userValue]) => { if (userValue === 0) return; - const privilege = state.privilegeByService[serviceKey].find(p => p._id === privilegeId); - if (!privilege) throw new Error(`Privilege not found: ${privilegeId}`); + const privilegeWithoutAmount = state.privilegeByService[serviceKey].find(p => p._id === privilegeId); + if (!privilegeWithoutAmount) throw new Error(`Privilege not found: ${privilegeId}`); - const p2: PrivilegeWithoutPrice = { - ...privilege, - privilegeId: privilege._id, + const privilege: PrivilegeWithAmount = { + ...privilegeWithoutAmount, + privilegeId: privilegeWithoutAmount._id, amount: userValue, }; - tariffs.push({ - name: privilege.name, - isCustom: true, - privilegies: [p2], - }); + privilegies.push(privilege); }); - if (tariffs.length === 0) return []; + const name = [...privilegies.map(p => p.name), new Date().toISOString()].join(", "); - const results = await Promise.allSettled(tariffs.map(tariff => createTariff(tariff))); + const tariff = { + name, + isCustom: true, + privilegies, + }; - const fulfilled = results.filter((p): p is PromiseFulfilledResult => p.status === "fulfilled"); - - return fulfilled.map(p => p.value); + return createTariff(tariff); }; export const clearCustomTariffs = () => useCustomTariffsStore.setState({ ...initialState }); From bbb02f09ebd068f89887cf0ff81f657c18c7f01a Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 11 Aug 2023 16:56:53 +0300 Subject: [PATCH 33/41] custom tariffs are now added to cart --- .../TariffConstructor/CustomTariffCard.tsx | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/pages/TariffConstructor/CustomTariffCard.tsx b/src/pages/TariffConstructor/CustomTariffCard.tsx index c1b7dd5..65e993b 100644 --- a/src/pages/TariffConstructor/CustomTariffCard.tsx +++ b/src/pages/TariffConstructor/CustomTariffCard.tsx @@ -13,9 +13,10 @@ import { } from "@root/stores/customTariffs"; import { cardShadow } from "@root/utils/themes/shadow"; import { currencyFormatter } from "@root/utils/currencyFormatter"; -import { Privilege, devlog, getMessageFromFetchError } from "@frontend/kitui"; +import { Privilege, getMessageFromFetchError } from "@frontend/kitui"; import { enqueueSnackbar } from "notistack"; import { updateTariffs } from "@root/stores/tariffs"; +import { addTariffToCart } from "@root/stores/user"; interface Props { serviceKey: string; @@ -36,20 +37,17 @@ export default function CustomTariffCard({ serviceKey, privileges }: Props) { const priceAfterDiscounts = summaryPriceAfterDiscounts[serviceKey] ?? 0; async function handleConfirmClick() { - createAndSendTariff(serviceKey) - .then((result) => { - devlog(result); - enqueueSnackbar("Тариф создан"); - - updateTariffs([result]); - }) - .catch((error) => { - const message = getMessageFromFetchError( - error, - "Не удалось создать тариф" - ); - if (message) enqueueSnackbar(message); - }); + try { + const tariff = await createAndSendTariff(serviceKey); + updateTariffs([tariff]); + await addTariffToCart(tariff._id); + } catch (error) { + const message = getMessageFromFetchError( + error, + "Не удалось создать тариф" + ); + if (message) enqueueSnackbar(message); + } } return ( From 7efdb80de79cdf8af585e7f45db90364aa39a7af Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 11 Aug 2023 19:09:32 +0300 Subject: [PATCH 34/41] fix tariff card layouts --- src/pages/Tariffs/FreeTariffCard.tsx | 8 +- src/pages/Tariffs/TariffCard.tsx | 236 ++++++++++++--------------- src/pages/Tariffs/TariffsPage.tsx | 20 +-- 3 files changed, 122 insertions(+), 142 deletions(-) diff --git a/src/pages/Tariffs/FreeTariffCard.tsx b/src/pages/Tariffs/FreeTariffCard.tsx index dbef472..477063c 100644 --- a/src/pages/Tariffs/FreeTariffCard.tsx +++ b/src/pages/Tariffs/FreeTariffCard.tsx @@ -18,7 +18,13 @@ export default function FreeTariffCard() { sx={{ backgroundColor: "#7E2AEA", color: "white", - minHeight: "206px", + }} + buttonProps={{ + text: "Выбрать", + sx: { + color: "white", + borderColor: "white", + }, }} /> ); diff --git a/src/pages/Tariffs/TariffCard.tsx b/src/pages/Tariffs/TariffCard.tsx index dbd7b80..223c1f1 100644 --- a/src/pages/Tariffs/TariffCard.tsx +++ b/src/pages/Tariffs/TariffCard.tsx @@ -1,148 +1,124 @@ import { - Box, - Typography, - Tooltip, - SxProps, - Theme, - useTheme, + Box, + Typography, + Tooltip, + SxProps, + Theme, + useTheme, } from "@mui/material"; import CustomButton from "@components/CustomButton"; import { MouseEventHandler, ReactNode } from "react"; import { cardShadow } from "@root/utils/themes/shadow"; interface Props { - icon: ReactNode; - headerText: string; - text: string | string[]; - sx?: SxProps; - buttonProps?: { + icon: ReactNode; + headerText: string; + text: string | string[]; sx?: SxProps; - onClick?: MouseEventHandler; - text?: string; - }; - price?: ReactNode; + buttonProps?: { + sx?: SxProps; + onClick?: MouseEventHandler; + text?: string; + }; + price?: ReactNode; } export default function TariffCard({ - icon, - headerText, - text, - sx, - price, - buttonProps, + icon, + headerText, + text, + sx, + price, + buttonProps, }: Props) { - const theme = useTheme(); + const theme = useTheme(); - return ( - - - {icon} - {price && ( - - {price} - - )} - - - - {headerText} - - - {Array.isArray(text) ? ( + text = Array.isArray(text) ? text : [text]; + + return ( - {text.map((line, index) => ( - - - {line} - - - ))} - - ) : ( - - - {text} - - - )} - {buttonProps && ( - - {buttonProps.text} - - )} - - ); + + {icon} + {price && ( + + {price} + + )} + + {headerText}} placement="top"> + + {headerText} + + + ( + {line} + ))} + placement="top" + > + + {text.map((line, index) => ( + {line} + ))} + + + {buttonProps && ( + + {buttonProps.text} + + )} + + ); } diff --git a/src/pages/Tariffs/TariffsPage.tsx b/src/pages/Tariffs/TariffsPage.tsx index cfce800..8c93b50 100644 --- a/src/pages/Tariffs/TariffsPage.tsx +++ b/src/pages/Tariffs/TariffsPage.tsx @@ -20,6 +20,11 @@ import { useAllTariffsFetcher } from "@root/utils/hooks/useAllTariffsFetcher"; const subPages = ["Шаблонизатор", "Опросник", "Сокращатель ссылок"]; +const StepperText: Record = { + volume: "Тарифы на объём", + time: "Тарифы на время", +}; + export default function TariffPage() { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); @@ -34,11 +39,6 @@ export default function TariffPage() { const unit: string = String(location.pathname).slice(9); const currentTariffs = Object.values(cartTariffMap).filter((tariff): tariff is Tariff => typeof tariff === "object"); - const StepperText: Record = { - volume: "Тарифы на объём", - time: "Тарифы на время", - }; - useAllTariffsFetcher({ onSuccess: updateTariffs, onError: (error) => { @@ -91,16 +91,14 @@ export default function TariffPage() { text={tariff.privilegies.map((p) => `${p.name} - ${p.amount}`)} price={ <> - {priceBeforeDiscounts !== undefined && priceBeforeDiscounts !== priceAfterDiscounts && ( + {priceBeforeDiscounts !== priceAfterDiscounts && ( {currencyFormatter.format(priceBeforeDiscounts / 100)} )} - {priceAfterDiscounts !== undefined && ( - - {currencyFormatter.format(priceAfterDiscounts / 100)} - - )} + + {currencyFormatter.format(priceAfterDiscounts / 100)} + } /> From 7d746259e74c2852bfcebe54bfdb2682bdedf10a Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 11 Aug 2023 21:11:28 +0300 Subject: [PATCH 35/41] fix drawer total --- src/components/Drawers.tsx | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index 1f4ee61..a03d6be 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -22,7 +22,6 @@ import { openCartDrawer, useCartStore, } from "@root/stores/cart"; -import { useCustomTariffsStore } from "@root/stores/customTariffs"; import { useUserStore } from "@root/stores/user"; import { updateTickets, @@ -43,12 +42,6 @@ export default function Drawers() { const upMd = useMediaQuery(theme.breakpoints.up("md")); const isDrawerOpen = useCartStore((state) => state.isDrawerOpen); const cart = useCart(); - const summaryPriceBeforeDiscountsMap = useCustomTariffsStore( - (state) => state.summaryPriceBeforeDiscountsMap - ); - const summaryPriceAfterDiscountsMap = useCustomTariffsStore( - (state) => state.summaryPriceAfterDiscountsMap - ); const userAccount = useUserStore((state) => state.userAccount); const { tickets, apiPage, ticketsPerPage } = useTicketStore((state) => state); @@ -63,21 +56,12 @@ export default function Drawers() { onError: () => {}, }); - const basePrice = Object.values(summaryPriceBeforeDiscountsMap).reduce( - (a, e) => a + e, - 0 - ); - const discountedPrice = Object.values(summaryPriceAfterDiscountsMap).reduce( - (a, e) => a + e, - 0 - ); - const notificationsCount = tickets.filter( ({ user, top_message }) => user !== top_message.user_id ).length; - const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts + basePrice; - const totalPriceAfterDiscounts = cart.priceAfterDiscounts + discountedPrice; + const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts; + const totalPriceAfterDiscounts = cart.priceAfterDiscounts; return ( From b579ae39669cfb3bf4d268a6fe70b6f2bc251adb Mon Sep 17 00:00:00 2001 From: nflnkr Date: Sat, 12 Aug 2023 17:44:39 +0300 Subject: [PATCH 36/41] fix cart total --- src/pages/Basket/Basket.tsx | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/pages/Basket/Basket.tsx b/src/pages/Basket/Basket.tsx index 0982a7d..cb90dc5 100644 --- a/src/pages/Basket/Basket.tsx +++ b/src/pages/Basket/Basket.tsx @@ -10,30 +10,14 @@ import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import TotalPrice from "@components/TotalPrice"; import CustomWrapper from "./CustomWrapper"; import { useCart } from "@root/utils/hooks/useCart"; -import { useCustomTariffsStore } from "@root/stores/customTariffs"; export default function Basket() { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const cart = useCart(); - const summaryPriceBeforeDiscountsMap = useCustomTariffsStore( - (state) => state.summaryPriceBeforeDiscountsMap - ); - const summaryPriceAfterDiscountsMap = useCustomTariffsStore( - (state) => state.summaryPriceAfterDiscountsMap - ); - const basePrice = Object.values(summaryPriceBeforeDiscountsMap).reduce( - (a, e) => a + e, - 0 - ); - const discountedPrice = Object.values(summaryPriceAfterDiscountsMap).reduce( - (a, e) => a + e, - 0 - ); - - const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts + basePrice; - const totalPriceAfterDiscounts = cart.priceAfterDiscounts + discountedPrice; + const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts; + const totalPriceAfterDiscounts = cart.priceAfterDiscounts; return ( Date: Sat, 12 Aug 2023 17:55:41 +0300 Subject: [PATCH 37/41] rename basket to cart --- src/index.tsx | 4 ++-- src/pages/{Basket/Basket.tsx => Cart/Cart.tsx} | 2 +- src/pages/{Basket => Cart}/CustomWrapper.tsx | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename src/pages/{Basket/Basket.tsx => Cart/Cart.tsx} (97%) rename src/pages/{Basket => Cart}/CustomWrapper.tsx (100%) diff --git a/src/index.tsx b/src/index.tsx index 03d2f0b..a012a30 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -12,7 +12,7 @@ import Tariffs from "./pages/Tariffs/Tariffs"; import SigninDialog from "./pages/auth/Signin"; import SignupDialog from "./pages/auth/Signup"; import History from "./pages/History"; -import Basket from "./pages/Basket/Basket"; +import Cart from "./pages/Cart/Cart"; import TariffPage from "./pages/Tariffs/TariffsPage"; import SavedTariffs from "./pages/SavedTariffs"; import lightTheme from "@utils/themes/light"; @@ -116,7 +116,7 @@ const App = () => { } /> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/src/pages/Basket/Basket.tsx b/src/pages/Cart/Cart.tsx similarity index 97% rename from src/pages/Basket/Basket.tsx rename to src/pages/Cart/Cart.tsx index cb90dc5..9c83d09 100644 --- a/src/pages/Basket/Basket.tsx +++ b/src/pages/Cart/Cart.tsx @@ -11,7 +11,7 @@ import TotalPrice from "@components/TotalPrice"; import CustomWrapper from "./CustomWrapper"; import { useCart } from "@root/utils/hooks/useCart"; -export default function Basket() { +export default function Cart() { const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const cart = useCart(); diff --git a/src/pages/Basket/CustomWrapper.tsx b/src/pages/Cart/CustomWrapper.tsx similarity index 100% rename from src/pages/Basket/CustomWrapper.tsx rename to src/pages/Cart/CustomWrapper.tsx From 859cee709715cac13890aab178965400fa330eff Mon Sep 17 00:00:00 2001 From: nflnkr Date: Sat, 12 Aug 2023 17:56:51 +0300 Subject: [PATCH 38/41] add custom tariff notification --- src/pages/TariffConstructor/CustomTariffCard.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/TariffConstructor/CustomTariffCard.tsx b/src/pages/TariffConstructor/CustomTariffCard.tsx index 65e993b..d8b4315 100644 --- a/src/pages/TariffConstructor/CustomTariffCard.tsx +++ b/src/pages/TariffConstructor/CustomTariffCard.tsx @@ -41,6 +41,7 @@ export default function CustomTariffCard({ serviceKey, privileges }: Props) { const tariff = await createAndSendTariff(serviceKey); updateTariffs([tariff]); await addTariffToCart(tariff._id); + enqueueSnackbar("Тариф добавлен в корзину"); } catch (error) { const message = getMessageFromFetchError( error, From a469508c4d14e9a2d192315f342897a76ee31c94 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Sat, 12 Aug 2023 22:14:03 +0300 Subject: [PATCH 39/41] fix expand icon size --- src/components/icons/ExpandIcon.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/components/icons/ExpandIcon.tsx b/src/components/icons/ExpandIcon.tsx index 281dc7a..924ed78 100644 --- a/src/components/icons/ExpandIcon.tsx +++ b/src/components/icons/ExpandIcon.tsx @@ -1,16 +1,26 @@ -import { useTheme } from "@mui/material"; +import { useTheme , Box} from "@mui/material"; interface Props { isExpanded: boolean; + } export default function ExpandIcon({ isExpanded }: Props) { const theme = useTheme(); return ( - + + - ); -} \ No newline at end of file + + ) +} From b6f04a5c2a4d0618738a46765643b12649995aba Mon Sep 17 00:00:00 2001 From: nflnkr Date: Sat, 12 Aug 2023 22:15:05 +0300 Subject: [PATCH 40/41] rework cart calc and components --- package.json | 2 +- src/components/CustomTariffAccordion.tsx | 159 ++++++++++ src/components/CustomWrapperDrawer.tsx | 366 ++++++++++++----------- src/components/Drawers.tsx | 1 + src/pages/Cart/CustomWrapper.tsx | 364 +++++++++++----------- src/stores/customTariffs.ts | 10 +- src/utils/calcCart/calcCart.test.ts | 1 + src/utils/calcCart/calcCart.ts | 42 +-- yarn.lock | 8 +- 9 files changed, 571 insertions(+), 382 deletions(-) create mode 100644 src/components/CustomTariffAccordion.tsx diff --git a/package.json b/package.json index f2a89de..cc8a55c 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dependencies": { "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", - "@frontend/kitui": "^1.0.20", + "@frontend/kitui": "1.0.21", "@mui/icons-material": "^5.10.14", "@mui/material": "^5.10.14", "@popperjs/core": "^2.11.8", diff --git a/src/components/CustomTariffAccordion.tsx b/src/components/CustomTariffAccordion.tsx new file mode 100644 index 0000000..4ff582e --- /dev/null +++ b/src/components/CustomTariffAccordion.tsx @@ -0,0 +1,159 @@ +import { useState } from "react"; +import { + Box, Typography, + IconButton, + useMediaQuery, + useTheme +} from "@mui/material"; +import ExpandIcon from "@components/icons/ExpandIcon"; +import { currencyFormatter } from "@root/utils/currencyFormatter"; +import { removeTariffFromCart } from "@root/stores/user"; +import { enqueueSnackbar } from "notistack"; +import { TariffCartData, getMessageFromFetchError } from "@frontend/kitui"; +import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg"; + + +interface Props { + tariffCartData: TariffCartData; +} + +export default function CustomTariffAccordion({ tariffCartData }: Props) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const [isExpanded, setIsExpanded] = useState(false); + + function handleDeleteClick() { + removeTariffFromCart(tariffCartData.tariffId) + .then(() => { + enqueueSnackbar("Тариф удален"); + }) + .catch((error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); + }); + } + + return ( + + + setIsExpanded((prev) => !prev)} + sx={{ + height: "72px", + pr: "20px", + pl: "30px", + display: "flex", + gap: "15px", + alignItems: "center", + justifyContent: "space-between", + cursor: "pointer", + userSelect: "none", + }} + > + + + + + Кастомный тариф + + + + {currencyFormatter.format(tariffCartData.price / 100)} + + + + + + + {isExpanded && tariffCartData.privileges.map((privilege) => ( + + + {privilege.description} + + + + {currencyFormatter.format(privilege.price / 100)} + + + + ))} + + + ); +} diff --git a/src/components/CustomWrapperDrawer.tsx b/src/components/CustomWrapperDrawer.tsx index f938319..fc79d11 100644 --- a/src/components/CustomWrapperDrawer.tsx +++ b/src/components/CustomWrapperDrawer.tsx @@ -1,11 +1,11 @@ import { useState } from "react"; import { - Box, - SvgIcon, - IconButton, - Typography, - useMediaQuery, - useTheme, + Box, + SvgIcon, + IconButton, + Typography, + useMediaQuery, + useTheme, } from "@mui/material"; import ClearIcon from "@mui/icons-material/Clear"; @@ -18,194 +18,204 @@ import ExpandIcon from "@components/icons/ExpandIcon"; import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg"; import type { MouseEvent } from "react"; +import CustomTariffAccordion from "@root/components/CustomTariffAccordion"; const name: Record = { - templategen: "Шаблонизатор", - squiz: "Опросник", - reducer: "Скоращатель ссылок", + templategen: "Шаблонизатор", + squiz: "Опросник", + reducer: "Скоращатель ссылок", }; interface Props { - serviceData: ServiceCartData; + serviceData: ServiceCartData; } export default function CustomWrapperDrawer({ serviceData }: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const upSm = useMediaQuery(theme.breakpoints.up("sm")); - const [isExpanded, setIsExpanded] = useState(false); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const [isExpanded, setIsExpanded] = useState(false); - function handleItemDeleteClick(tariffId: string) { - removeTariffFromCart(tariffId) - .then(() => { - enqueueSnackbar("Тариф удален"); - }) - .catch((error) => { - const message = getMessageFromFetchError(error); - if (message) enqueueSnackbar(message); - }); - } - - const deleteService = async (event: MouseEvent) => { - event.stopPropagation(); - - setIsExpanded(false); - - for (const { tariffId } of serviceData.privileges) { - try { - await removeTariffFromCart(tariffId); - } catch {} + function handleItemDeleteClick(tariffId: string) { + removeTariffFromCart(tariffId) + .then(() => { + enqueueSnackbar("Тариф удален"); + }) + .catch((error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); + }); } - enqueueSnackbar("Тарифы удален"); - }; + const deleteService = async (event: MouseEvent) => { + event.stopPropagation(); - return ( - - + setIsExpanded(false); + + for (const { tariffId } of serviceData.tariffs) { + try { + await removeTariffFromCart(tariffId); + } catch { } + } + + enqueueSnackbar("Тарифы удален"); + }; + + return ( setIsExpanded((prev) => !prev)} - sx={{ - height: "72px", - display: "flex", - gap: "10px", - alignItems: "center", - justifyContent: "space-between", - cursor: "pointer", - userSelect: "none", - }} + sx={{ + overflow: "hidden", + borderRadius: "12px", + width: "100%", + }} > - - - - {name[serviceData.serviceKey]} - - - {currencyFormatter.format(serviceData.price / 100)} - - - - - - - - {isExpanded && - serviceData.privileges.map((privilege) => ( - - - {privilege.description} - - - setIsExpanded((prev) => !prev)} + sx={{ + height: "72px", + display: "flex", + gap: "10px", + alignItems: "center", + justifyContent: "space-between", + cursor: "pointer", + userSelect: "none", + }} > - {currencyFormatter.format(privilege.price / 100)} - - handleItemDeleteClick(privilege.tariffId)} - component={ClearIcon} - /> - + + + + {name[serviceData.serviceKey]} + + + + {currencyFormatter.format(serviceData.price / 100)} + + + + + + + + {isExpanded && + serviceData.tariffs.map(tariff => { + const privilege = tariff.privileges[0]; + + return tariff.privileges.length > 1 ? ( + + ) : ( + + + {privilege.description} + + + + {currencyFormatter.format(privilege.price / 100)} + + handleItemDeleteClick(privilege.tariffId)} + component={ClearIcon} + /> + + + ); + })} - ))} - - - ); + + ); } diff --git a/src/components/Drawers.tsx b/src/components/Drawers.tsx index a03d6be..9f9fd7d 100644 --- a/src/components/Drawers.tsx +++ b/src/components/Drawers.tsx @@ -216,6 +216,7 @@ export default function Drawers() { {cart.services.map((serviceData) => ( = { - templategen: "Шаблонизатор", - squiz: "Опросник", - reducer: "Сокращатель ссылок", + templategen: "Шаблонизатор", + squiz: "Опросник", + reducer: "Сокращатель ссылок", }; interface Props { - serviceData: ServiceCartData; + serviceData: ServiceCartData; } export default function CustomWrapper({ serviceData }: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const upSm = useMediaQuery(theme.breakpoints.up("sm")); - const [isExpanded, setIsExpanded] = useState(false); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const [isExpanded, setIsExpanded] = useState(false); - function handleItemDeleteClick(tariffId: string) { - removeTariffFromCart(tariffId) - .then(() => { - enqueueSnackbar("Тариф удален"); - }) - .catch((error) => { - const message = getMessageFromFetchError(error); - if (message) enqueueSnackbar(message); - }); - } - - const deleteService = async (event: MouseEvent) => { - event.stopPropagation(); - - setIsExpanded(false); - - for (const { tariffId } of serviceData.privileges) { - try { - await removeTariffFromCart(tariffId); - } catch {} + function handleItemDeleteClick(tariffId: string) { + removeTariffFromCart(tariffId) + .then(() => { + enqueueSnackbar("Тариф удален"); + }) + .catch((error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); + }); } - enqueueSnackbar("Тарифы удален"); - }; + const deleteService = async (event: MouseEvent) => { + event.stopPropagation(); - return ( - - + setIsExpanded(false); + + for (const { tariffId } of serviceData.tariffs) { + try { + await removeTariffFromCart(tariffId); + } catch { } + } + + enqueueSnackbar("Тарифы удален"); + }; + + return ( setIsExpanded((prev) => !prev)} - sx={{ - height: "72px", - px: "20px", - display: "flex", - gap: "15px", - alignItems: "center", - justifyContent: "space-between", - cursor: "pointer", - userSelect: "none", - }} + sx={{ + overflow: "hidden", + borderRadius: "12px", + boxShadow: cardShadow, + }} > - - - - - {name[serviceData.serviceKey]} - - - - {currencyFormatter.format(serviceData.price / 100)} - - - - - - - {isExpanded && - serviceData.privileges.map((privilege) => ( - - {privilege.description} - - - setIsExpanded((prev) => !prev)} + sx={{ + height: "72px", + px: "20px", + display: "flex", + gap: "15px", + alignItems: "center", + justifyContent: "space-between", + cursor: "pointer", + userSelect: "none", + }} > - {currencyFormatter.format(privilege.price / 100)} - - - handleItemDeleteClick(privilege.tariffId)} - > - - + + + + + {name[serviceData.serviceKey]} + + + + {currencyFormatter.format(serviceData.price / 100)} + + + + + + + {isExpanded && + serviceData.tariffs.map(tariff => { + const privilege = tariff.privileges[0]; + + return tariff.privileges.length > 1 ? ( + + ) : ( + + + {privilege.description} + + + + {currencyFormatter.format(tariff.price / 100)} + + + handleItemDeleteClick(privilege.tariffId)} + > + + + + ); + })} - ))} - - - ); + + ); } diff --git a/src/stores/customTariffs.ts b/src/stores/customTariffs.ts index cec2d49..b298d2a 100644 --- a/src/stores/customTariffs.ts +++ b/src/stores/customTariffs.ts @@ -4,7 +4,7 @@ import { ServiceKeyToPrivilegesMap } from "@root/model/privilege"; import { produce } from "immer"; import { create } from "zustand"; import { devtools, persist } from "zustand/middleware"; -import { Discount, PrivilegeWithAmount, findCartDiscount, findLoyaltyDiscount, findPrivilegeDiscount, findServiceDiscount } from "@frontend/kitui"; +import { Discount, PrivilegeWithAmount, findCartDiscount, findDiscountFactor, findLoyaltyDiscount, findPrivilegeDiscount, findServiceDiscount } from "@frontend/kitui"; interface CustomTariffsStore { @@ -63,17 +63,17 @@ export const setCustomTariffsUserValue = ( priceWithoutDiscounts += privilege.price * amount; const discount = findPrivilegeDiscount(privilege.privilegeId, privilege.price * amount, discounts); - priceAfterDiscounts += privilege.price * amount * discount.factor; + priceAfterDiscounts += privilege.price * amount * findDiscountFactor(discount); }); const serviceDiscount = findServiceDiscount(serviceKey, priceAfterDiscounts, discounts); - priceAfterDiscounts *= serviceDiscount.factor; + priceAfterDiscounts *= findDiscountFactor(serviceDiscount); const cartDiscount = findCartDiscount(currentCartTotal, discounts); - priceAfterDiscounts *= cartDiscount.factor; + priceAfterDiscounts *= findDiscountFactor(cartDiscount); const loyaltyDiscount = findLoyaltyDiscount(purchasesAmount, discounts); - priceAfterDiscounts *= loyaltyDiscount.factor; + priceAfterDiscounts *= findDiscountFactor(loyaltyDiscount); state.summaryPriceBeforeDiscountsMap[serviceKey] = priceWithoutDiscounts; state.summaryPriceAfterDiscountsMap[serviceKey] = priceAfterDiscounts; diff --git a/src/utils/calcCart/calcCart.test.ts b/src/utils/calcCart/calcCart.test.ts index 559d6f0..c68c663 100644 --- a/src/utils/calcCart/calcCart.test.ts +++ b/src/utils/calcCart/calcCart.test.ts @@ -1,3 +1,4 @@ +// @ts-nocheck TODO fix tests import { CartData, Discount, Tariff } from "@frontend/kitui"; import { calcCart } from "./calcCart"; diff --git a/src/utils/calcCart/calcCart.ts b/src/utils/calcCart/calcCart.ts index 5136da8..1e7df16 100644 --- a/src/utils/calcCart/calcCart.ts +++ b/src/utils/calcCart/calcCart.ts @@ -1,4 +1,4 @@ -import { CartData, Discount, PrivilegeCartData, Tariff, applyCartDiscount, applyLoyaltyDiscount, applyPrivilegeDiscounts, applyServiceDiscounts } from "@frontend/kitui"; +import { CartData, Discount, PrivilegeCartData, Tariff, TariffCartData, applyCartDiscount, applyLoyaltyDiscount, applyPrivilegeDiscounts, applyServiceDiscounts } from "@frontend/kitui"; export function calcCart(tariffs: Tariff[], discounts: Discount[], purchasesAmount: number): CartData { @@ -13,25 +13,31 @@ export function calcCart(tariffs: Tariff[], discounts: Discount[], purchasesAmou }; tariffs.forEach(tariff => { - if (tariff.price && tariff.price > 0) cartData.priceBeforeDiscounts += tariff.price; + let serviceData = cartData.services.find(service => service.serviceKey === tariff.privilegies[0].serviceKey); + if (!serviceData) { + serviceData = { + serviceKey: tariff.privilegies[0].serviceKey, + tariffs: [], + price: 0, + appliedServiceDiscount: null, + }; + cartData.services.push(serviceData); + } + + const tariffCartData: TariffCartData = { + price: tariff.price ?? 0, + isCustom: tariff.isCustom, + privileges: [], + tariffId: tariff._id, + }; + serviceData.tariffs.push(tariffCartData); tariff.privilegies.forEach(privilege => { - let serviceData = cartData.services.find(service => service.serviceKey === privilege.serviceKey); - if (!serviceData) { - serviceData = { - serviceKey: privilege.serviceKey, - privileges: [], - price: 0, - appliedServiceDiscount: null, - }; - cartData.services.push(serviceData); - } - const privilegePrice = privilege.amount * privilege.price; - if (!tariff.price) cartData.priceBeforeDiscounts += privilegePrice; + if (!tariff.price) tariffCartData.price += privilegePrice; - const privilegeData: PrivilegeCartData = { + const privilegeCartData: PrivilegeCartData = { tariffId: tariff._id, serviceKey: privilege.serviceKey, privilegeId: privilege.privilegeId, @@ -41,11 +47,13 @@ export function calcCart(tariffs: Tariff[], discounts: Discount[], purchasesAmou tariffName: tariff.name, }; - serviceData.privileges.push(privilegeData); - serviceData.price += privilegePrice; + tariffCartData.privileges.push(privilegeCartData); cartData.priceAfterDiscounts += privilegePrice; cartData.itemCount++; }); + + cartData.priceBeforeDiscounts += tariffCartData.price; + serviceData.price += tariffCartData.price; }); applyPrivilegeDiscounts(cartData, discounts); diff --git a/yarn.lock b/yarn.lock index 1aa3efc..6576da1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1532,10 +1532,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@frontend/kitui@^1.0.20": - version "1.0.20" - resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/@frontend/kitui/-/@frontend/kitui-1.0.20.tgz#b6713be932a8a6d36d450b347d415988de729bba" - integrity sha1-tnE76TKoptNtRQs0fUFZiN5ym7o= +"@frontend/kitui@1.0.21": + version "1.0.21" + resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/@frontend/kitui/-/@frontend/kitui-1.0.21.tgz#572b3e774ba42b65c4b946704aeb68237b4b3cd5" + integrity sha1-Vys+d0ukK2XEuUZwSutoI3tLPNU= dependencies: immer "^10.0.2" reconnecting-eventsource "^1.6.2" From d7cb83474f081974d6a6ee752a19e09c50e3eae3 Mon Sep 17 00:00:00 2001 From: Nastya Date: Sat, 12 Aug 2023 23:57:11 +0300 Subject: [PATCH 41/41] =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=82=D0=B0=D1=80=D0=B8=D1=84=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BD=D0=B5=20=D0=BF=D1=83=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=B9=20=D0=BC=D0=B0=D1=81=D1=81=D0=B8=D0=B2=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=B2=D0=B8=D0=BB=D0=B5=D0=B3=D0=B8=D0=B9=20=D0=B2=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D0=B5=20=D1=82=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D1=84=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Tariffs/TariffsPage.tsx | 52 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/pages/Tariffs/TariffsPage.tsx b/src/pages/Tariffs/TariffsPage.tsx index 8c93b50..b9a5b0b 100644 --- a/src/pages/Tariffs/TariffsPage.tsx +++ b/src/pages/Tariffs/TariffsPage.tsx @@ -65,42 +65,42 @@ export default function TariffPage() { ); }); - const tariffElements = filteredTariffs.map((tariff, index) => { + const tariffElements = filteredTariffs.filter((tariff)=>tariff.privilegies.length > 0).map((tariff, index) => { const { priceBeforeDiscounts, priceAfterDiscounts } = calcIndividualTariffPrices( tariff, discounts, purchasesAmount, currentTariffs, ); - + return ( - } - buttonProps={{ - text: "Выбрать", - onClick: () => handleTariffItemClick(tariff._id), - }} - headerText={tariff.name} - text={tariff.privilegies.map((p) => `${p.name} - ${p.amount}`)} - price={ - <> - {priceBeforeDiscounts !== priceAfterDiscounts && ( - - {currencyFormatter.format(priceBeforeDiscounts / 100)} - - )} - - {currencyFormatter.format(priceAfterDiscounts / 100)} - - - } + + } + buttonProps={{ + text: "Выбрать", + onClick: () => handleTariffItemClick(tariff._id), + }} + headerText={tariff.name} + text={tariff.privilegies.map((p) => `${p.name} - ${p.amount}`)} + price={ + <> + {priceBeforeDiscounts !== priceAfterDiscounts && ( + + {currencyFormatter.format(priceBeforeDiscounts / 100)} + + )} + + {currencyFormatter.format(priceAfterDiscounts / 100)} + + + } /> ); });