change cart and tariffs calculation logic

use swr for cart tariffs datafetching
remove console clutter
remove cart store
some fixes
This commit is contained in:
nflnkr 2024-03-18 17:02:28 +03:00
parent c730993fef
commit 0990dc081f
27 changed files with 13197 additions and 36825 deletions

24027
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -15,7 +15,7 @@
"dependencies": {
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@frontend/kitui": "1.0.62",
"@frontend/kitui": "^1.0.66",
"@mui/icons-material": "^5.10.14",
"@mui/material": "^5.10.14",
"@popperjs/core": "^2.11.8",
@ -36,6 +36,7 @@
"react-router-dom": "^6.15.0",
"react-slick": "^0.29.0",
"slick-carousel": "^1.8.1",
"swr": "^2.2.5",
"use-debounce": "^10.0.0",
"web-vitals": "^2.1.0",
"yup": "^1.1.1",

@ -50,7 +50,6 @@ export async function getHistory(): Promise<[GetHistoryResponse | null, string?]
historyResponse.records = checked || []
console.log("historyResponse ", historyResponse)
return [historyResponse]
} catch (nativeError) {
const [error] = parseAxiosError(nativeError)

@ -1,11 +1,12 @@
import { Tariff, makeRequest } from "@frontend/kitui"
import { CreateTariffBody } from "@root/model/customTariffs"
import { parseAxiosError } from "@root/utils/parse-error"
import { Tariff, makeRequest } from "@frontend/kitui";
import { CreateTariffBody } from "@root/model/customTariffs";
import { parseAxiosError } from "@root/utils/parse-error";
import type { ServiceKeyToPrivilegesMap } from "@root/model/privilege"
import type { GetTariffsResponse } from "@root/model/tariff"
import type { ServiceKeyToPrivilegesMap } from "@root/model/privilege";
import type { GetTariffsResponse } from "@root/model/tariff";
import { removeTariffFromCart } from "@root/stores/user";
const apiUrl = process.env.REACT_APP_DOMAIN + "/strator"
const apiUrl = process.env.REACT_APP_DOMAIN + "/strator";
export async function getTariffs(
apiPage: number,
@ -18,13 +19,13 @@ export async function getTariffs(
method: "get",
useToken: true,
signal,
})
});
return [tariffsResponse]
return [tariffsResponse];
} catch (nativeError) {
const [error] = parseAxiosError(nativeError)
const [error] = parseAxiosError(nativeError);
return [null, `Не удалось получить список тарифов. ${error}`]
return [null, `Не удалось получить список тарифов. ${error}`];
}
}
@ -35,13 +36,13 @@ export async function createTariff(tariff: CreateTariffBody): Promise<[Tariff |
method: "post",
useToken: true,
body: tariff,
})
});
return [createTariffResponse]
return [createTariffResponse];
} catch (nativeError) {
const [error] = parseAxiosError(nativeError)
const [error] = parseAxiosError(nativeError);
return [null, `Не удалось создать тариф. ${error}`]
return [null, `Не удалось создать тариф. ${error}`];
}
}
@ -51,13 +52,13 @@ export async function getTariffById(tariffId: string): Promise<[Tariff | null, s
url: `${apiUrl}/tariff/${tariffId}`,
method: "get",
useToken: true,
})
});
return [getTariffByIdResponse]
return [getTariffByIdResponse];
} catch (nativeError) {
const [error, status] = parseAxiosError(nativeError)
const [error, status] = parseAxiosError(nativeError);
return [null, `Не удалось получить тарифы. ${error}`, status]
return [null, `Не удалось получить тарифы. ${error}`, status];
}
}
@ -70,17 +71,47 @@ export async function getCustomTariffs(
signal,
method: "get",
useToken: true,
})
});
const tempCustomTariffsResponse = {
...customTariffsResponse,
squiz: customTariffsResponse.squiz
};
return [tempCustomTariffsResponse]
return [tempCustomTariffsResponse];
} catch (nativeError) {
const [error] = parseAxiosError(nativeError)
const [error] = parseAxiosError(nativeError);
return [null, `Не удалось получить мои тарифы. ${error}`]
return [null, `Не удалось получить мои тарифы. ${error}`];
}
}
export async function getTariffArray(tariffIds: string[] | undefined) {
if (!tariffIds) return null;
const responses = await Promise.allSettled(tariffIds.map(tariffId =>
makeRequest<never, Tariff>({
url: `${apiUrl}/tariff/${tariffId}`,
method: "get",
useToken: true,
})
));
const tariffs: Tariff[] = [];
responses.forEach((response, index) => {
switch (response.status) {
case "fulfilled": {
tariffs.push(response.value);
break;
}
case "rejected": {
const [, status] = parseAxiosError(response.reason);
if (status === 404) removeTariffFromCart(tariffIds[index]);
break;
}
}
});
return tariffs;
}

@ -6,18 +6,17 @@ import { removeTariffFromCart } from "@root/stores/user"
import { enqueueSnackbar } from "notistack"
import {
CloseButton,
TariffCartData,
getMessageFromFetchError,
} from "@frontend/kitui"
import type { MouseEvent } from "react"
import { TariffCartData } from "@root/utils/calcCart/utils";
interface Props {
tariffCartData: TariffCartData;
outsideFactor: number;
}
export default function CustomTariffAccordion({ tariffCartData, outsideFactor }: Props) {
export default function CustomTariffAccordion({ tariffCartData }: Props) {
const theme = useTheme()
const upMd = useMediaQuery(theme.breakpoints.up("md"))
const upSm = useMediaQuery(theme.breakpoints.up("sm"))
@ -36,7 +35,6 @@ export default function CustomTariffAccordion({ tariffCartData, outsideFactor }:
})
}
console.log('DRAWER', outsideFactor)
return (
<Box
sx={{
@ -103,7 +101,7 @@ console.log('DRAWER', outsideFactor)
fontWeight: 500,
}}
>
{currencyFormatter.format(tariffCartData.price * outsideFactor / 100)}
{currencyFormatter.format(tariffCartData.price / 100)}
</Typography>
</Box>
<CloseButton
@ -150,7 +148,7 @@ console.log('DRAWER', outsideFactor)
fontWeight: 500,
}}
>
{currencyFormatter.format(privilege.price * outsideFactor / 100)}
{currencyFormatter.format(privilege.price / 100)}
</Typography>
</Box>
</Box>

@ -5,7 +5,7 @@ import ClearIcon from "@mui/icons-material/Clear";
import { currencyFormatter } from "@root/utils/currencyFormatter";
import { useUserStore, removeTariffFromCart, setCart } from "@root/stores/user";
import { enqueueSnackbar } from "notistack";
import { ServiceCartData, getMessageFromFetchError } from "@frontend/kitui";
import { getMessageFromFetchError } from "@frontend/kitui";
import ExpandIcon from "@components/icons/ExpandIcon";
import { deleteCart } from "@root/api/cart";
@ -13,6 +13,7 @@ import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg";
import type { MouseEvent } from "react";
import CustomTariffAccordion from "@root/components/CustomTariffAccordion";
import { ServiceCartData } from "@root/utils/calcCart/utils";
const name: Record<string, string> = {
templategen: "Шаблонизатор",
@ -23,10 +24,9 @@ const name: Record<string, string> = {
interface Props {
serviceData: ServiceCartData;
outsideFactor: number;
}
export default function CustomWrapperDrawer({ serviceData, outsideFactor }: Props) {
export default function CustomWrapperDrawer({ serviceData }: Props) {
const [isExpanded, setIsExpanded] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const theme = useTheme();
@ -138,7 +138,7 @@ export default function CustomWrapperDrawer({ serviceData, outsideFactor }: Prop
fontWeight: 500,
}}
>
{currencyFormatter.format(serviceData.price * outsideFactor / 100)}
{currencyFormatter.format(serviceData.price / 100)}
</Typography>
</Box>
</Box>
@ -169,9 +169,8 @@ export default function CustomWrapperDrawer({ serviceData, outsideFactor }: Prop
serviceData.tariffs.map((tariff) => {
const privilege = tariff.privileges[0];
console.log('CD', tariff, outsideFactor)
return tariff.privileges.length > 1 ? (
<CustomTariffAccordion outsideFactor={outsideFactor} key={tariff.id} tariffCartData={tariff} />
<CustomTariffAccordion key={tariff.id} tariffCartData={tariff} />
) : (
<Box
key={tariff.id + privilege.privilegeId}
@ -211,7 +210,7 @@ console.log('CD', tariff, outsideFactor)
fontWeight: 500,
}}
>
{currencyFormatter.format((tariff.price || privilege.price) * outsideFactor / 100)}
{currencyFormatter.format((tariff.price || privilege.price) / 100)}
</Typography>
<SvgIcon
sx={{

@ -6,7 +6,6 @@ import { NotificationsModal } from "./NotificationsModal";
import { Loader } from "./Loader";
import { useCart } from "@root/utils/hooks/useCart";
import { currencyFormatter } from "@root/utils/currencyFormatter";
import { closeCartDrawer, openCartDrawer, useCartStore } from "@root/stores/cart";
import { setUserAccount, useUserStore } from "@root/stores/user";
import { useTicketStore } from "@root/stores/tickets";
@ -29,7 +28,7 @@ function Drawers() {
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isDrawerOpen = useCartStore((state) => state.isDrawerOpen);
const [isDrawerOpen, setIsDrawerOpen] = useState<boolean>(false);
const cart = useCart();
const userAccount = useUserStore((state) => state.userAccount);
const tickets = useTicketStore((state) => state.tickets);
@ -52,7 +51,7 @@ function Drawers() {
setLoading(false);
closeCartDrawer()
setIsDrawerOpen(false);
navigate("payment")
if (!payCartError.includes("insufficient funds: ")) enqueueSnackbar(payCartError);
return
@ -63,7 +62,7 @@ function Drawers() {
}
setLoading(false);
closeCartDrawer();
setIsDrawerOpen(false);;
}
function handleReplenishWallet() {
@ -135,7 +134,7 @@ function Drawers() {
}))}
/>
<IconButton
onClick={openCartDrawer}
onClick={() => setIsDrawerOpen(true)}
component="div"
sx={{
cursor: "pointer",
@ -173,7 +172,7 @@ function Drawers() {
<CartIcon />
</Badge>
</IconButton>
<Drawer anchor={"right"} open={isDrawerOpen} onClose={closeCartDrawer} sx={{ background: "rgba(0, 0, 0, 0.55)" }}>
<Drawer anchor={"right"} open={isDrawerOpen} onClose={() => setIsDrawerOpen(false)} sx={{ background: "rgba(0, 0, 0, 0.55)" }}>
<SectionWrapper
maxWidth="lg"
sx={{
@ -205,15 +204,14 @@ function Drawers() {
>
Корзина
</Typography>
<IconButton onClick={closeCartDrawer} sx={{ p: 0 }}>
<IconButton onClick={() => setIsDrawerOpen(false)} sx={{ p: 0 }}>
<CrossIcon />
</IconButton>
</Box>
<Box sx={{ pl: "20px", pr: "20px" }}>
{cart.services.map((serviceData) => {
console.log('aaaaaaaaaaAAAAAAAAAAAAAA', (cart.appliedCartPurchasesDiscount?.Target?.Factor || 1)*(cart.appliedLoyaltyDiscount?.Target?.Factor || 1), (cart.appliedCartPurchasesDiscount?.Target?.Factor || 1),(cart.appliedLoyaltyDiscount?.Target?.Factor || 1))
return (
<CustomWrapperDrawer key={serviceData.serviceKey} outsideFactor={(cart.appliedCartPurchasesDiscount?.Target?.Factor || 1)*(cart.appliedLoyaltyDiscount?.Target?.Factor || 1)} serviceData={serviceData} />
<CustomWrapperDrawer key={serviceData.serviceKey} serviceData={serviceData} />
)})}
<Box
sx={{

@ -19,8 +19,6 @@ interface Props {
export default function TotalPrice({ priceAfterDiscounts, priceBeforeDiscounts, isConstructor = false }: Props) {
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const isMobile = useMediaQuery(theme.breakpoints.down(550));
const cart = useCart();
const [notEnoughMoneyAmount, setNotEnoughMoneyAmount] = useState<number>(0);
const [loading, setLoading] = useState<boolean>(false);
const navigate = useNavigate();
@ -169,7 +167,7 @@ export default function TotalPrice({ priceAfterDiscounts, priceBeforeDiscounts,
)}
<Button
variant="pena-contained-dark"
disabled = {cart.priceAfterDiscounts === 0}
disabled = {priceAfterDiscounts === 0}
onClick={() => (notEnoughMoneyAmount === 0 ? !loading && handlePayClick() : handleReplenishWallet())}
>
{loading ? <Loader size={24} /> : notEnoughMoneyAmount === 0 ? "Оплатить" : "Пополнить"}

@ -5,8 +5,3 @@ export interface GetTariffsResponse {
totalPages: number;
tariffs: Tariff[];
}
export interface FullTariff extends Tariff {
description?: string;
order?: number;
}

@ -1,28 +1,26 @@
import { Box, IconButton, Typography, Badge, useMediaQuery, useTheme } from "@mui/material"
import SectionWrapper from "@components/SectionWrapper"
import ArrowBackIcon from "@mui/icons-material/ArrowBack"
import TotalPrice from "@components/TotalPrice"
import CustomWrapper from "./CustomWrapper"
import { useCart } from "@root/utils/hooks/useCart"
import { useLocation } from "react-router-dom"
import { usePrevLocation } from "@root/utils/hooks/handleCustomBackNavigation"
import { handleComponentError } from "@root/utils/handleComponentError"
import { withErrorBoundary } from "react-error-boundary"
import { Box, IconButton, Typography, Badge, useMediaQuery, useTheme } from "@mui/material";
import SectionWrapper from "@components/SectionWrapper";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import TotalPrice from "@components/TotalPrice";
import CustomWrapper from "./CustomWrapper";
import { useCart } from "@root/utils/hooks/useCart";
import { useLocation } from "react-router-dom";
import { usePrevLocation } from "@root/utils/hooks/handleCustomBackNavigation";
import { handleComponentError } from "@root/utils/handleComponentError";
import { withErrorBoundary } from "react-error-boundary";
function Cart() {
const theme = useTheme()
const upMd = useMediaQuery(theme.breakpoints.up("md"))
const isMobile = useMediaQuery(theme.breakpoints.down(550))
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
const cart = useCart()
const location = useLocation()
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const isMobile = useMediaQuery(theme.breakpoints.down(550));
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const cart = useCart();
const location = useLocation();
const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts
const totalPriceAfterDiscounts = cart.priceAfterDiscounts
console.log('CART', totalPriceAfterDiscounts, totalPriceBeforeDiscounts, cart)
const handleCustomBackNavigation = usePrevLocation(location)
const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts;
const totalPriceAfterDiscounts = cart.priceAfterDiscounts;
const handleCustomBackNavigation = usePrevLocation(location);
return (
<SectionWrapper
@ -65,7 +63,6 @@ console.log('CART', totalPriceAfterDiscounts, totalPriceBeforeDiscounts, cart)
<CustomWrapper
key={serviceData.serviceKey}
serviceData={serviceData}
outsideFactor={(cart.appliedCartPurchasesDiscount?.Target?.Factor || 1)*(cart.appliedLoyaltyDiscount?.Target?.Factor || 1)}
first={index === 0}
last={index === cart.services.length - 1}
/>
@ -73,10 +70,10 @@ console.log('CART', totalPriceAfterDiscounts, totalPriceBeforeDiscounts, cart)
</Box>
<TotalPrice priceBeforeDiscounts={totalPriceBeforeDiscounts} priceAfterDiscounts={totalPriceAfterDiscounts} />
</SectionWrapper>
)
);
}
export default withErrorBoundary(Cart, {
fallback: <Typography mt="8px" textAlign="center">Ошибка при отображении корзины</Typography>,
onError: handleComponentError,
})
});

@ -5,10 +5,11 @@ import ClearIcon from "@mui/icons-material/Clear"
import { currencyFormatter } from "@root/utils/currencyFormatter"
import { removeTariffFromCart } from "@root/stores/user"
import { enqueueSnackbar } from "notistack"
import { CloseButton, ServiceCartData, getMessageFromFetchError } from "@frontend/kitui"
import { CloseButton, getMessageFromFetchError } from "@frontend/kitui"
import type { MouseEvent } from "react"
import CustomTariffAccordion from "@root/components/CustomTariffAccordion"
import { ServiceCartData } from "@root/utils/calcCart/utils";
const name: Record<string, string> = {
templategen: "Шаблонизатор",
@ -19,12 +20,11 @@ const name: Record<string, string> = {
interface Props {
serviceData: ServiceCartData;
outsideFactor: number;
last?: boolean;
first?: boolean;
}
export default function CustomWrapper({ serviceData, last, first, outsideFactor }: Props) {
export default function CustomWrapper({ serviceData, last, first }: Props) {
const theme = useTheme()
const upMd = useMediaQuery(theme.breakpoints.up("md"))
const upSm = useMediaQuery(theme.breakpoints.up("sm"))
@ -55,7 +55,6 @@ export default function CustomWrapper({ serviceData, last, first, outsideFactor
enqueueSnackbar("Тарифы удалены")
}
console.log('SDDD', serviceData)
return (
<Box
sx={{
@ -128,7 +127,7 @@ console.log('SDDD', serviceData)
fontWeight: 500,
}}
>
{currencyFormatter.format(serviceData.price * outsideFactor / 100)}
{currencyFormatter.format(serviceData.price / 100)}
</Typography>
</Box>
<CloseButton style={{ height: "22 px", width: "22px" }} onClick={deleteService} />
@ -139,7 +138,7 @@ console.log('SDDD', serviceData)
return tariff.privileges.length > 1 ? (
<CustomTariffAccordion outsideFactor={outsideFactor} key={tariff.id} tariffCartData={tariff} />
<CustomTariffAccordion key={tariff.id} tariffCartData={tariff} />
) : (
<Box
key={tariff.id + privilege.privilegeId}
@ -178,7 +177,7 @@ console.log('SDDD', serviceData)
fontWeight: 500,
}}
>
{currencyFormatter.format(tariff.price * outsideFactor / 100)}
{currencyFormatter.format(tariff.price / 100)}
</Typography>
</Box>
<Box

@ -55,7 +55,6 @@ export default function CustomTariffCard({ serviceKey, privileges }: Props) {
}
}
console.log('COMP', priceAfterDiscounts, priceBeforeDiscounts)
return (
<Box
sx={{

@ -1,29 +1,28 @@
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material"
import { Link } from "react-router-dom"
import SectionWrapper from "@components/SectionWrapper"
import { useCustomTariffsStore } from "@root/stores/customTariffs"
import ComplexHeader from "@root/components/ComplexHeader"
import CustomTariffCard from "./CustomTariffCard"
import ArrowBackIcon from "@mui/icons-material/ArrowBack"
import TotalPrice from "@root/components/TotalPrice"
import { serviceNameByKey } from "@root/utils/serviceKeys"
import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker"
import { withErrorBoundary } from "react-error-boundary"
import { handleComponentError } from "@root/utils/handleComponentError"
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material";
import { Link } from "react-router-dom";
import SectionWrapper from "@components/SectionWrapper";
import { useCustomTariffsStore } from "@root/stores/customTariffs";
import ComplexHeader from "@root/components/ComplexHeader";
import CustomTariffCard from "./CustomTariffCard";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import TotalPrice from "@root/components/TotalPrice";
import { serviceNameByKey } from "@root/utils/serviceKeys";
import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker";
import { withErrorBoundary } from "react-error-boundary";
import { handleComponentError } from "@root/utils/handleComponentError";
import { useCart } from "@root/utils/hooks/useCart";
function TariffConstructor() {
const theme = useTheme()
const upMd = useMediaQuery(theme.breakpoints.up("md"))
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const cart = useCart();
const customTariffs = useCustomTariffsStore((state) => state.privilegeByService);
const customTariffs = useCustomTariffsStore((state) => state.privilegeByService)
const summaryPriceBeforeDiscountsMap = useCustomTariffsStore((state) => state.summaryPriceBeforeDiscountsMap)
const summaryPriceAfterDiscountsMap = useCustomTariffsStore((state) => state.summaryPriceAfterDiscountsMap)
const basePrice = cart.priceBeforeDiscounts;
const discountedPrice = cart.priceAfterDiscounts;
const basePrice = Object.values(summaryPriceBeforeDiscountsMap).reduce((a, e) => a + e, 0)
const discountedPrice = Object.values(summaryPriceAfterDiscountsMap).reduce((a, e) => a + e, 0)
const handleCustomBackNavigation = useHistoryTracker()
const handleCustomBackNavigation = useHistoryTracker();
return (
<SectionWrapper
@ -43,9 +42,8 @@ function TariffConstructor() {
}}
>
{Object.entries(customTariffs).filter(([serviceKey]) => serviceKey === "squiz").map(([serviceKey, privileges], index) => {
console.log("serviceKey ",serviceKey)
console.log(Object.entries(customTariffs))
return <Box key={index}>
return (
<Box key={serviceKey}>
<Box
sx={{
mb: "40px",
@ -70,6 +68,7 @@ function TariffConstructor() {
</Box>
<CustomTariffCard serviceKey={serviceKey} privileges={privileges} />
</Box>
);
})}
</Box>
<Link
@ -86,10 +85,10 @@ function TariffConstructor() {
</Link>
<TotalPrice priceBeforeDiscounts={basePrice} priceAfterDiscounts={discountedPrice} isConstructor={true} />
</SectionWrapper>
)
);
}
export default withErrorBoundary(TariffConstructor, {
fallback: <Typography mt="8px" textAlign="center">Ошибка при отображении моих тарифов</Typography>,
onError: handleComponentError,
})
});

@ -1,15 +1,14 @@
import { Privilege, PrivilegeValueType, useThrottle } from "@frontend/kitui"
import { Box, SliderProps, Typography, useMediaQuery, useTheme } from "@mui/material"
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 { useCartStore } from "@root/stores/cart"
import { setCustomTariffsUserValue, useCustomTariffsStore } from "@root/stores/customTariffs"
import { useDiscountStore } from "@root/stores/discounts"
import { useUserStore } from "@root/stores/user"
import { getDeclension } from "@root/utils/declension"
import { useEffect, useState } from "react"
import { Privilege, useThrottle } from "@frontend/kitui";
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
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 { setCustomTariffsUserValue, useCustomTariffsStore } from "@root/stores/customTariffs";
import { useUserStore } from "@root/stores/user";
import { getDeclension } from "@root/utils/declension";
import { useCartTariffs } from "@root/utils/hooks/useCartTariffs";
import { useEffect, useState } from "react";
import { useDebouncedCallback } from 'use-debounce';
const sliderSettingsByType = {
@ -17,69 +16,66 @@ const sliderSettingsByType = {
шаблон: { max: 5000, min: 0 },
МБ: { max: 5000, min: 0 },
заявка: { max: 5000, min: 0 }
}
type PrivilegeName = "день" | "шаблон" | "МБ" | "заявка"
};
type PrivilegeName = "день" | "шаблон" | "МБ" | "заявка";
interface Props {
privilege: Privilege;
}
export default function TariffPrivilegeSlider({ privilege }: Props) {
const theme = useTheme()
const upMd = useMediaQuery(theme.breakpoints.up("md"))
const userValue = useCustomTariffsStore((state) => state.userValuesMap[privilege.serviceKey]?.[privilege._id]) ?? sliderSettingsByType[privilege.value]?.min
const discounts = useDiscountStore((state) => state.discounts)
const currentCartTotal = useCartStore((state) => state.cart.priceAfterDiscounts)
const purchasesAmount = useUserStore((state) => state.userAccount?.wallet.spent) ?? sliderSettingsByType[privilege.value]?.min
const isUserNko = useUserStore(state => state.userAccount?.status) === "nko"
const [value, setValue] = useState<number>(userValue)
const throttledValue = useThrottle(value, 200)
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const userValue = useCustomTariffsStore((state) => state.userValuesMap[privilege.serviceKey]?.[privilege._id]) ?? sliderSettingsByType[privilege.value]?.min;
const purchasesAmount = useUserStore((state) => state.userAccount?.wallet.spent) ?? sliderSettingsByType[privilege.value]?.min;
const cartTariffs = useCartTariffs();
const [value, setValue] = useState<number>(userValue);
const throttledValue = useThrottle(value, 200);
useEffect(
function setStoreValue() {
setCustomTariffsUserValue(
cartTariffs ?? [],
privilege.serviceKey,
privilege._id,
throttledValue,
discounts,
currentCartTotal,
purchasesAmount,
isUserNko,
)
);
},
[currentCartTotal, discounts, purchasesAmount, privilege._id, privilege.serviceKey, throttledValue, isUserNko]
)
[cartTariffs, purchasesAmount, privilege._id, privilege.serviceKey, throttledValue]
);
function handleSliderChange(measurement: PrivilegeName) {
return (value: number | number[]) => {
if (Number(value) < Number(sliderSettingsByType[measurement]?.min)) {
setValue(0)
return
setValue(0);
return;
}
if (Array.isArray(value)) throw new Error("Slider uses multiple values instead of one")
if (Array.isArray(value)) throw new Error("Slider uses multiple values instead of one");
setValue(value)
}
setValue(value);
};
}
const quantityText = `${value} ${getDeclension(value, privilege.value)}`
const quantityText = `${value} ${getDeclension(value, privilege.value)}`;
const setNotSmallNumber = useDebouncedCallback(() => {
if (value === sliderSettingsByType[privilege.value]?.min) return
if (value === sliderSettingsByType[privilege.value]?.min) return;
if (Number(value) < Number(sliderSettingsByType[privilege.value]?.min)) {
setValue(sliderSettingsByType[privilege.value]?.min)
setValue(sliderSettingsByType[privilege.value]?.min);
}
if (privilege.value === "день" && Number(value) < 30 && Number(value) !== 0) {
setValue(30)
setValue(30);
}
if (privilege.value !== "день" && Number(value) < 100 && Number(value) !== 0) {
setValue(100)
setValue(100);
}
}, 600)
}, 600);
const quantityElement = (
<Box
@ -110,20 +106,20 @@ export default function TariffPrivilegeSlider({ privilege }: Props) {
privilege={privilege}
adornmentText={getDeclension(0, privilege.value)}
onChange={(value) => {
setValue(value)
setNotSmallNumber()
setValue(value);
setNotSmallNumber();
}}
/>
</Box>
</Box>
)
);
const icon =
privilege.type === "day" ? (
<CalendarIcon color={theme.palette.orange.main} bgcolor="#FEDFD0" />
) : (
<PieChartIcon color={theme.palette.orange.main} bgcolor="#FEDFD0" />
)
);
return (
<Box>
@ -161,7 +157,7 @@ export default function TariffPrivilegeSlider({ privilege }: Props) {
</Box>
<CustomSlider
value={value}
min={sliderSettingsByType[privilege.value]?.min }
min={sliderSettingsByType[privilege.value]?.min}
max={sliderSettingsByType[privilege.value]?.max || 100}
onChange={handleSliderChange(privilege.value)}
firstStep={privilege.value === "день" ? 30 : 100}
@ -169,5 +165,5 @@ export default function TariffPrivilegeSlider({ privilege }: Props) {
{!upMd && quantityElement}
</Box>
</Box>
)
);
}

@ -1,28 +1,23 @@
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material";
import SectionWrapper from "@components/SectionWrapper";
import { useTariffStore } from "@root/stores/tariffs";
import { enqueueSnackbar } from "notistack";
import { Select } from "@root/components/Select";
import { Tabs } from "@root/components/Tabs";
import TariffCard from "./TariffCard";
import NumberIcon from "@root/components/NumberIcon";
import { currencyFormatter } from "@root/utils/currencyFormatter";
import { calcIndividualTariffPrices } from "@root/utils/calcTariffPrices";
import { Tariff, getMessageFromFetchError } from "@frontend/kitui";
import FreeTariffCard from "./FreeTariffCard";
import { addTariffToCart, useUserStore } from "@root/stores/user";
import { useDiscountStore } from "@root/stores/discounts";
import { Slider } from "./slider";
import { useCartStore } from "@root/stores/cart";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import { usePrevLocation } from "@root/utils/hooks/handleCustomBackNavigation";
import { withErrorBoundary } from "react-error-boundary";
import { handleComponentError } from "@root/utils/handleComponentError";
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material";
import NumberIcon from "@root/components/NumberIcon";
import { useDiscountStore } from "@root/stores/discounts";
import { useHistoryStore } from "@root/stores/history";
import { useRecentlyPurchasedTariffs } from "@utils/hooks/useRecentlyPurchasedTariffs";
import { FullTariff } from "@root/model/tariff"
import { useTariffStore } from "@root/stores/tariffs";
import { addTariffToCart, useUserStore } from "@root/stores/user";
import { calcIndividualTariffPrices } from "@root/utils/calcTariffPrices";
import { currencyFormatter } from "@root/utils/currencyFormatter";
import { handleComponentError } from "@root/utils/handleComponentError";
import { usePrevLocation } from "@root/utils/hooks/handleCustomBackNavigation";
import { useCartTariffs } from "@root/utils/hooks/useCartTariffs";
import { enqueueSnackbar } from "notistack";
import { useState } from "react";
import { withErrorBoundary } from "react-error-boundary";
import { useLocation } from "react-router-dom";
import FreeTariffCard from "./FreeTariffCard";
import TariffCard from "./TariffCard";
const subPages = ["Опросник", /*"Шаблонизатор",*/ /*"Сокращатель ссылок"*/];
@ -43,15 +38,13 @@ function TariffPage() {
const [selectedItem, setSelectedItem] = useState<number>(0);
const discounts = useDiscountStore((state) => state.discounts);
const purchasesAmount = useUserStore((state) => state.userAccount?.wallet.spent) ?? 0;
const cartTariffMap = useCartStore((state) => state.cartTariffMap);
const isUserNko = useUserStore((state) => state.userAccount?.status) === "nko";
const historyData = useHistoryStore((state) => state.history);
const currentTariffs = useCartTariffs();
const { recentlyPurchased } = useRecentlyPurchasedTariffs();
const handleCustomBackNavigation = usePrevLocation(location);
const unit: string = String(location.pathname).slice(9);
const currentTariffs = Object.values(cartTariffMap).filter((tariff): tariff is FullTariff => typeof tariff === "object");
function handleTariffItemClick(tariffId: string) {
addTariffToCart(tariffId)
@ -64,7 +57,6 @@ function TariffPage() {
});
}
console.log("tariffstariffstariffstariffs", tariffs)
const filteredTariffs = tariffs.filter((tariff) => {
if (tariff.privileges[0] === undefined) return false
if (
@ -92,7 +84,7 @@ function TariffPage() {
return historyTariffIds.includes(tariff._id);
});
const createTariffElements = (filteredTariffs: FullTariff[], addFreeTariff = false) => {
const createTariffElements = (filteredTariffs: Tariff[], addFreeTariff = false) => {
console.log(filteredTariffs)
const tariffElements = filteredTariffs
.filter((tariff) => tariff.privileges.length > 0)
@ -101,14 +93,9 @@ function TariffPage() {
tariff,
discounts,
purchasesAmount,
currentTariffs,
currentTariffs ?? [],
isUserNko
);
console.log('TECCCCCCCC',priceBeforeDiscounts, priceAfterDiscounts,tariff,
discounts,
purchasesAmount,
currentTariffs,
isUserNko)
return (
<TariffCard

@ -1,91 +0,0 @@
import { CartData, Discount, Tariff } from "@frontend/kitui"
import { calcCart } from "@root/utils/calcCart/calcCart"
import { produce } from "immer"
import { create } from "zustand"
import { devtools } from "zustand/middleware"
interface CartStore {
cartTariffMap: Record<string, Tariff | "loading" | "not found">;
cart: CartData;
isDrawerOpen: boolean;
}
export const useCartStore = create<CartStore>()(
devtools(
(get, set) => ({
cartTariffMap: {},
cart: {
services: [],
priceBeforeDiscounts: 0,
priceAfterDiscounts: 0,
itemCount: 0,
allAppliedDiscounts: [],
appliedCartPurchasesDiscount: null,
appliedLoyaltyDiscount: null,
},
isDrawerOpen: false,
}),
{
name: "Cart",
enabled: process.env.NODE_ENV === "development",
trace: true,
actionsBlacklist: "rejected",
}
)
)
export const setCartTariffStatus = (tariffId: string, status: "loading" | "not found") =>
useCartStore.setState(
produce<CartStore>((state) => {
state.cartTariffMap[tariffId] = status
}),
false,
{
type: "setCartTariffStatus",
tariffId,
status,
}
)
export const addCartTariffs = (tariffs: Tariff[], discounts: Discount[], purchasesAmount: number, isUserNko: boolean) =>
useCartStore.setState(
produce<CartStore>((state) => {
tariffs.forEach((tariff) => {
// if (tariff.isCustom && tariff.privileges[0].serviceKey !== "custom") {
// tariff.privileges[0].serviceKey = "custom"
// }
//
state.cartTariffMap[tariff._id] = tariff
})
const cartTariffs = Object.values(state.cartTariffMap).filter(
(tariff): tariff is Tariff => typeof tariff === "object"
)
state.cart = calcCart(cartTariffs, discounts, purchasesAmount, isUserNko)
}),
false,
{
type: tariffs.length > 0 ? "addCartTariffs" : "rejected",
tariffs,
}
)
export const removeMissingCartTariffs = (tariffIds: string[], discounts: Discount[], purchasesAmount: number, isUserNko: boolean) =>
useCartStore.setState(
produce<CartStore>((state) => {
for (const key in state.cartTariffMap) {
if (!tariffIds.includes(key)) delete state.cartTariffMap[key]
}
const cartTariffs = Object.values(state.cartTariffMap).filter(
(tariff): tariff is Tariff => typeof tariff === "object"
)
state.cart = calcCart(cartTariffs, discounts, purchasesAmount, isUserNko)
}),
false,
{
type: "rejected",
}
)
export const openCartDrawer = () => useCartStore.setState({ isDrawerOpen: true })
export const closeCartDrawer = () => useCartStore.setState({ isDrawerOpen: false })

@ -1,13 +1,13 @@
import { createTariff } from "@root/api/tariff"
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, PrivilegeWithAmount/* , findCartDiscount */, findDiscountFactor/* , findLoyaltyDiscount *//* , findPrivilegeDiscount *//* , findServiceDiscount */} from "@frontend/kitui"
import { findNkoDiscount, calcCart } from "@root/utils/calcCart/calcCart"
import { useCartStore } from "./cart"
import { FullTariff } from "@root/model/tariff"
import { PrivilegeWithAmount, Tariff } from "@frontend/kitui";
import { createTariff } from "@root/api/tariff";
import { CustomTariffUserValuesMap, ServiceKeyToPriceMap } from "@root/model/customTariffs";
import { ServiceKeyToPrivilegesMap } from "@root/model/privilege";
import { calcCustomTariffPrice } from "@root/utils/calcCart/calcCustomTariffPrice";
import { produce } from "immer";
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
import { useDiscountStore } from "./discounts";
import { useUserStore } from "./user";
interface CustomTariffsStore {
@ -22,52 +22,7 @@ const initialState: CustomTariffsStore = {
userValuesMap: {},
summaryPriceBeforeDiscountsMap: {},
summaryPriceAfterDiscountsMap: {},
}
function findLoyaltyDiscount(
purchasesAmount: number,
discounts: Discount[],
): Discount | null {
const applicableDiscounts = discounts.filter(discount => {
return (
discount.Layer === 4 &&
discount.Condition.UserType !== "nko" &&
purchasesAmount >= Number(discount.Condition.PurchasesAmount)
);
});
if (!applicableDiscounts.length) return null;
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
return Number(current.Condition.PurchasesAmount) > Number(prev.Condition.PurchasesAmount) ? current : prev;
});
return maxValueDiscount;
}
function findServiceDiscount(
serviceKey: string,
currentPrice: number,
discounts: Discount[],
): Discount | null {
const applicableDiscounts = discounts.filter(discount => {
return (
discount.Layer === 2 &&
discount.Condition.Group === serviceKey &&
currentPrice >= Number(discount.Condition.PriceFrom)
);
});
if (!applicableDiscounts.length) return null;
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
return Number(current.Condition.PriceFrom) > Number(prev.Condition.PriceFrom) ? current : prev;
});
return maxValueDiscount;
}
};
export const useCustomTariffsStore = create<CustomTariffsStore>()(
persist(
@ -87,165 +42,66 @@ export const useCustomTariffsStore = create<CustomTariffsStore>()(
migrate: () => initialState,
}
)
)
);
function findPrivilegeDiscount(
privilegeId: string,
privilegePrice: number,
discounts: Discount[],
): Discount | null {
const applicableDiscounts = discounts.filter(discount => {
const conditionMinPrice = parseFloat(discount.Condition.Term);
if (!isFinite(conditionMinPrice)) throw new Error(`Couldn't parse Discount.Condition.Term: ${discount.Condition.Term}`);
return (
discount.Layer === 1 &&
privilegeId === discount.Condition.Product &&
privilegePrice >= conditionMinPrice
);
});
if (!applicableDiscounts.length) return null;
const maxValueDiscount = applicableDiscounts.reduce((prev, current) =>
parseFloat(current.Condition.Term) > parseFloat(prev.Condition.Term) ? current : prev
);
return maxValueDiscount;
}
export const setCustomTariffs = (customTariffs: ServiceKeyToPrivilegesMap) => useCustomTariffsStore.setState({ privilegeByService: customTariffs })
function findCartDiscount(
cartPurchasesAmount: number,
discounts: Discount[],
): Discount | null {
const applicableDiscounts = discounts.filter(discount => {
return (
discount.Layer === 3 &&
cartPurchasesAmount >= discount.Condition.CartPurchasesAmount
);
});
if (!applicableDiscounts.length) return null;
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
return Number(current.Condition.CartPurchasesAmount) > Number(prev.Condition.CartPurchasesAmount) ? current : prev;
});
return maxValueDiscount;
}
export const setCustomTariffs = (customTariffs: ServiceKeyToPrivilegesMap) => useCustomTariffsStore.setState({ privilegeByService: customTariffs });
export const setCustomTariffsUserValue = (
cartTariffs: Tariff[],
serviceKey: string,
privilegeId: string,
value: number,
discounts: Discount[],
currentCartTotal: number,
purchasesAmount: number,
isUserNko: boolean,
) => useCustomTariffsStore.setState(
produce<CustomTariffsStore>(state => {
state.userValuesMap[serviceKey] ??= {}
state.userValuesMap[serviceKey][privilegeId] = value
state.userValuesMap[serviceKey] ??= {};
state.userValuesMap[serviceKey][privilegeId] = value;
const isUserNko = useUserStore.getState().userAccount?.status === "nko";
const discounts = useDiscountStore.getState().discounts;
const { priceBeforeDiscounts, priceAfterDiscounts } = calcCustomTariffPrice(
state.userValuesMap[serviceKey],
state.privilegeByService[serviceKey],
cartTariffs,
discounts,
purchasesAmount,
isUserNko
);
const privilegeWithoutAmount = state.privilegeByService[serviceKey].find(p => p._id === privilegeId)
if (!privilegeWithoutAmount) throw new Error(`Privilege not found: ${privilegeId}`)
let priceWithoutDiscounts = 0
let priceAfterDiscounts = 0
const privileges = new Array<PrivilegeWithAmount>()
Object.keys(state.userValuesMap[serviceKey]).forEach(e => {
const pwa = state.privilegeByService[serviceKey].find(p => p._id === e)
if (!pwa) return
if (state.userValuesMap[serviceKey][e] > 0)
privileges.push({
...pwa,
amount: state.userValuesMap[serviceKey][e]
state.summaryPriceBeforeDiscountsMap[serviceKey] = priceBeforeDiscounts;
state.summaryPriceAfterDiscountsMap[serviceKey] = priceAfterDiscounts;
})
})
let tariff: FullTariff = {
_id: "",
name: "",
price: 0,
description: "",
isCustom: true,
isDeleted: false,
privileges: privileges,
}
const cart = calcCart([...Object.values(useCartStore.getState().cartTariffMap as Record<string, FullTariff>), tariff], discounts, purchasesAmount, isUserNko)
const nkoDiscount = findNkoDiscount(discounts)
if (isUserNko && nkoDiscount) {
state.privilegeByService[serviceKey].forEach(privilege => {
const amount = state.userValuesMap[serviceKey]?.[privilege._id] ?? 0
priceWithoutDiscounts += privilege.price * amount
})
priceAfterDiscounts = priceWithoutDiscounts * findDiscountFactor(nkoDiscount)
} else {
state.privilegeByService[serviceKey].forEach(privilege => {
const amount = state.userValuesMap[serviceKey]?.[privilege._id] ?? 0
console.log(amount, serviceKey, privilege._id)
priceWithoutDiscounts += privilege.price * amount
const discount = cart.allAppliedDiscounts?.find(e => e.Condition.Product === privilege.privilegeId)
console.log(discount, privilege.price*amount, findDiscountFactor(discount))
//const discount = findPrivilegeDiscount(privilege.privilegeId, amount, discounts)
priceAfterDiscounts += privilege.price * amount * findDiscountFactor(discount)
})
console.log(priceAfterDiscounts)
const serviceDiscount = cart.allAppliedDiscounts?.find(e => e.Condition.Group === serviceKey)
priceAfterDiscounts *= findDiscountFactor(serviceDiscount)
console.log(serviceDiscount, serviceKey, priceAfterDiscounts, discounts)
const cartDiscount = findCartDiscount(currentCartTotal+priceAfterDiscounts, discounts)
priceAfterDiscounts *= findDiscountFactor(cartDiscount)
const loyaltyDiscount = findLoyaltyDiscount(purchasesAmount, discounts)
priceAfterDiscounts *= findDiscountFactor(loyaltyDiscount)
}
console.log('cTC', cart)
state.summaryPriceBeforeDiscountsMap[serviceKey] = priceWithoutDiscounts
state.summaryPriceAfterDiscountsMap[serviceKey] = priceAfterDiscounts
})
)
);
export const createAndSendTariff = (serviceKey: string) => {
const state = useCustomTariffsStore.getState()
const state = useCustomTariffsStore.getState();
const privileges: PrivilegeWithAmount[] = []
const privileges: PrivilegeWithAmount[] = [];
Object.entries(state.userValuesMap[serviceKey]).forEach(([privilegeId, userValue]) => {
if (userValue === 0) return
if (userValue === 0) return;
const privilegeWithoutAmount = state.privilegeByService[serviceKey].find(p => p._id === privilegeId)
if (!privilegeWithoutAmount) 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 privilege: PrivilegeWithAmount = {
...privilegeWithoutAmount,
privilegeId: privilegeWithoutAmount.privilegeId,
amount: userValue,
}
};
privileges.push(privilege)
})
privileges.push(privilege);
});
const name = [...privileges.map(p => p.name), new Date().toISOString()].join(", ")
const name = [...privileges.map(p => p.name), new Date().toISOString()].join(", ");
const tariff = {
name,
isCustom: true,
privileges,
}
};
return createTariff(tariff)
}
return createTariff(tariff);
};
export const clearCustomTariffs = () => useCustomTariffsStore.setState({ ...initialState })
export const clearCustomTariffs = () => useCustomTariffsStore.setState({ ...initialState });

@ -1,6 +1,5 @@
import { CartData, Discount } from "@frontend/kitui"
import { CartData, Discount, Tariff } from "@frontend/kitui"
import { calcCart } from "./calcCart"
import { FullTariff } from "@root/model/tariff"
describe("Cart calculations", () => {
@ -825,7 +824,7 @@ describe("Cart calculations", () => {
})
})
const templategenTariff1: FullTariff = {
const templategenTariff1: Tariff = {
_id: "t1",
name: "templategenTariff1",
price: 0,
@ -849,7 +848,7 @@ const templategenTariff1: FullTariff = {
updatedAt: ""
}
const templategenTariff2: FullTariff = {
const templategenTariff2: Tariff = {
_id: "t5",
name: "templategenTariff2",
price: 0,
@ -873,7 +872,7 @@ const templategenTariff2: FullTariff = {
updatedAt: ""
}
const squizTariff: FullTariff = {
const squizTariff: Tariff = {
_id: "t2",
name: "squizTariff",
price: 0,
@ -897,7 +896,7 @@ const squizTariff: FullTariff = {
updatedAt: ""
}
const reducerTariff: FullTariff = {
const reducerTariff: Tariff = {
_id: "t3",
name: "reducerTariff",
description: "test",

@ -1,149 +1,40 @@
import { CartData, Discount, PrivilegeCartData, Tariff, TariffCartData, findPrivilegeDiscount, findDiscountFactor, applyLoyaltyDiscount} from "@frontend/kitui";
import { Discount, Tariff, findDiscountFactor } from "@frontend/kitui";
import { CartData, PrivilegeCartData, TariffCartData, findCartDiscount, findLoyaltyDiscount, findNkoDiscount, findPrivilegeDiscount, findServiceDiscount } from "./utils";
function applyPrivilegeDiscounts(
cartData: CartData,
discounts: Discount[],
) {
const privMap = new Map()
cartData.services.forEach(service => service.tariffs.forEach(tariff => tariff.privileges.forEach(p => {
privMap.set(p.privilegeId, p.amount + (privMap.get(p.privilegeId)||0))
})))
cartData.services.forEach(service => {
service.tariffs.forEach(tariff => {
tariff.privileges.forEach(privilege => {
const privilegeDiscount = findPrivilegeDiscount(privilege.privilegeId, privMap.get(privilege.privilegeId)||0, discounts);
if (!privilegeDiscount) return;
const discountAmount = privilege.price * (1 - findDiscountFactor(privilegeDiscount));
privilege.price -= discountAmount;
cartData.allAppliedDiscounts.push(privilegeDiscount);
privilege.appliedPrivilegeDiscount = privilegeDiscount;
tariff.price -= discountAmount;
service.price -= discountAmount;
cartData.priceAfterDiscounts -= discountAmount;
});
});
});
}
function findServiceDiscount(
serviceKey: string,
currentPrice: number,
discounts: Discount[],
): Discount | null {
const applicableDiscounts = discounts.filter(discount => {
return (
discount.Layer === 2 &&
discount.Condition.Group === serviceKey &&
currentPrice >= Number(discount.Condition.PriceFrom)
);
});
if (!applicableDiscounts.length) return null;
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
return Number(current.Condition.PriceFrom) > Number(prev.Condition.PriceFrom) ? current : prev;
});
return maxValueDiscount;
}
function findCartDiscount(
cartPurchasesAmount: number,
discounts: Discount[],
): Discount | null {
const applicableDiscounts = discounts.filter(discount => {
return (
discount.Layer === 3 &&
cartPurchasesAmount >= Number(discount.Condition.CartPurchasesAmount)
);
});
if (!applicableDiscounts.length) return null;
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
return Number(current.Condition.CartPurchasesAmount) > Number(prev.Condition.CartPurchasesAmount) ? current : prev;
});
return maxValueDiscount;
}
function applyCartDiscount(
cartData: CartData,
discounts: Discount[],
) {
const cartDiscount = findCartDiscount(cartData.priceAfterDiscounts, discounts);
if (!cartDiscount) return;
cartData.priceAfterDiscounts *= findDiscountFactor(cartDiscount);
cartData.allAppliedDiscounts.push(cartDiscount);
cartData.appliedCartPurchasesDiscount = cartDiscount;
}
function applyServiceDiscounts(
cartData: CartData,
discounts: Discount[],
) {
const privMap = new Map()
cartData.services.forEach(service => {
service.tariffs.forEach(tariff => tariff.privileges.forEach(p => {
privMap.set(p.serviceKey, p.price + (privMap.get(p.serviceKey)||0))
}))
})
cartData.services.forEach(service => {
service.tariffs.map(tariff => {
tariff.privileges.forEach(privilege => {
const privilegeDiscount = findServiceDiscount(privilege.serviceKey, privMap.get(privilege.serviceKey), discounts);
if (!privilegeDiscount) return;
const discountAmount = privilege.price * (1 - findDiscountFactor(privilegeDiscount));
privilege.price -= discountAmount;
cartData.allAppliedDiscounts.push(privilegeDiscount);
service.appliedServiceDiscount = privilegeDiscount;
tariff.price -= discountAmount;
service.price -= discountAmount;
cartData.priceAfterDiscounts -= discountAmount;
});
});
});
}
export function calcCart(tariffs: Tariff[], discounts: Discount[], purchasesAmount: number, isUserNko?: boolean): CartData {
export function calcCart(tariffs: Tariff[], discounts: Discount[], purchasesAmount: number, isUserNko?: boolean, userId: string = ""): CartData {
const cartData: CartData = {
services: [],
priceBeforeDiscounts: 0,
priceAfterDiscounts: 0,
itemCount: 0,
appliedCartPurchasesDiscount: null,
appliedLoyaltyDiscount: null,
allAppliedDiscounts: [],
}
appliedDiscountsByPrivilegeId: new Map(),
};
const privilegeAmountById = new Map<string, number>();
const servicePriceByKey = new Map<string, number>();
const allAppliedDiscounts = new Set<Discount>();
// Формируем корзину
tariffs.forEach(tariff => {
if (tariff.privileges === undefined) return
if (tariff.privileges === undefined) return;
if (
(tariff.price || 0) > 0
&& tariff.privileges.length !== 1
) throw new Error("Price is defined for tariff with several")
) throw new Error("Price is defined for tariff with several privileges");
let serviceData =cartData.services.find(service => (service.serviceKey === "custom" && tariff.isCustom))
if (!serviceData && !tariff.isCustom) serviceData = cartData.services.find(service => service.serviceKey === tariff.privileges[0]?.serviceKey)
let serviceData = cartData.services.find(service => (service.serviceKey === "custom" && tariff.isCustom));
if (!serviceData && !tariff.isCustom) serviceData = cartData.services.find(service => service.serviceKey === tariff.privileges[0]?.serviceKey);
if (!serviceData) {
serviceData = {
serviceKey: tariff.isCustom ?"custom" :tariff.privileges[0]?.serviceKey,
serviceKey: tariff.isCustom ? "custom" : tariff.privileges[0]?.serviceKey,
tariffs: [],
price: 0,
appliedServiceDiscount: null,
}
cartData.services.push(serviceData)
};
cartData.services.push(serviceData);
}
const tariffCartData: TariffCartData = {
@ -152,13 +43,12 @@ export function calcCart(tariffs: Tariff[], discounts: Discount[], purchasesAmou
privileges: [],
id: tariff._id,
name: tariff.name,
}
serviceData.tariffs.push(tariffCartData)
};
serviceData.tariffs.push(tariffCartData);
tariff.privileges.forEach(privilege => {
let privilegePrice = privilege.amount * privilege.price
if (!tariff.price) tariffCartData.price += privilegePrice
else privilegePrice = tariff.price
let privilegePrice = privilege.amount * privilege.price;
if (tariff.price) privilegePrice = tariff.price;
const privilegeCartData: PrivilegeCartData = {
serviceKey: privilege.serviceKey,
@ -166,47 +56,158 @@ export function calcCart(tariffs: Tariff[], discounts: Discount[], purchasesAmou
description: privilege.description,
price: privilegePrice,
amount: privilege.amount,
appliedPrivilegeDiscount: null,
}
};
tariffCartData.privileges.push(privilegeCartData)
cartData.priceAfterDiscounts += privilegePrice
cartData.itemCount++
})
privilegeAmountById.set(
privilege.privilegeId,
privilege.amount + (privilegeAmountById.get(privilege.privilegeId) || 0)
);
servicePriceByKey.set(
privilege.serviceKey,
privilegePrice + (servicePriceByKey.get(privilege.serviceKey) || 0)
);
cartData.appliedDiscountsByPrivilegeId.set(privilege.privilegeId, new Set());
cartData.priceBeforeDiscounts += tariffCartData.price
serviceData.price += tariffCartData.price
})
tariffCartData.privileges.push(privilegeCartData);
});
});
const nkoDiscount = findNkoDiscount(discounts)
cartData.priceBeforeDiscounts = Array.from(servicePriceByKey.values()).reduce((a, b) => a + b, 0);
const nkoDiscount = findNkoDiscount(discounts);
if (isUserNko && nkoDiscount) {
applyNkoDiscount(cartData, nkoDiscount)
} else {
applyPrivilegeDiscounts(cartData, discounts)
applyServiceDiscounts(cartData, discounts)
applyCartDiscount(cartData, discounts)
applyLoyaltyDiscount(cartData, discounts, purchasesAmount)
cartData.priceAfterDiscounts *= nkoDiscount.Target.Factor;
allAppliedDiscounts.add(nkoDiscount);
cartData.services.forEach(service => {
service.tariffs.forEach(tariff => {
tariff.privileges.forEach(privilege => {
const privilegeAppliedDiscounts = cartData.appliedDiscountsByPrivilegeId.get(privilege.privilegeId);
if (!privilegeAppliedDiscounts) throw new Error(`Privilege id ${privilege.privilegeId} not found in appliedDiscountsByPrivilegeId`);
privilegeAppliedDiscounts.add(nkoDiscount);
});
});
});
applyDiscountsToCart(cartData);
cartData.allAppliedDiscounts = Array.from(allAppliedDiscounts);
return cartData;
}
cartData.allAppliedDiscounts = Array.from(new Set(cartData.allAppliedDiscounts))
// Ищем и собираем скидки в appliedDiscountsByPrivilegeId
console.log('CD', cartData)
return cartData
cartData.services.forEach(service => {
service.tariffs.forEach(tariff => {
tariff.privileges.forEach(privilege => {
const privilegeDiscount = findPrivilegeDiscount(privilege.privilegeId, privilegeAmountById.get(privilege.privilegeId) || 0, discounts, userId);
if (!privilegeDiscount) return;
allAppliedDiscounts.add(privilegeDiscount);
const privilegeAppliedDiscounts = cartData.appliedDiscountsByPrivilegeId.get(privilege.privilegeId);
if (!privilegeAppliedDiscounts) throw new Error(`Privilege id ${privilege.privilegeId} not found in appliedDiscountsByPrivilegeId`);
privilegeAppliedDiscounts.add(privilegeDiscount);
});
});
});
cartData.services.forEach(service => {
service.tariffs.map(tariff => {
tariff.privileges.forEach(privilege => {
const servicePrice = servicePriceByKey.get(privilege.serviceKey);
if (!servicePrice) return;
const serviceDiscount = findServiceDiscount(privilege.serviceKey, servicePrice, discounts, userId);
if (!serviceDiscount) return;
allAppliedDiscounts.add(serviceDiscount);
service.appliedServiceDiscount = serviceDiscount;
const privilegeAppliedDiscounts = cartData.appliedDiscountsByPrivilegeId.get(privilege.privilegeId);
if (!privilegeAppliedDiscounts) throw new Error(`Privilege id ${privilege.privilegeId} not found in appliedDiscountsByPrivilegeId`);
privilegeAppliedDiscounts.add(serviceDiscount);
});
});
});
const intermediateCartData = structuredClone(cartData);
applyDiscountsToCart(intermediateCartData);
const intermediateCartPriceAfterDiscounts = intermediateCartData.priceAfterDiscounts;
const cartDiscount = findCartDiscount(intermediateCartPriceAfterDiscounts, discounts);
if (cartDiscount) {
cartData.services.forEach(service => {
service.tariffs.forEach(tariff => {
tariff.privileges.forEach(privilege => {
const privilegeAppliedDiscounts = cartData.appliedDiscountsByPrivilegeId.get(privilege.privilegeId);
if (!privilegeAppliedDiscounts) throw new Error(`Privilege id ${privilege.privilegeId} not found in appliedDiscountsByPrivilegeId`);
if (!Array.from(privilegeAppliedDiscounts)[0]?.Condition.User) privilegeAppliedDiscounts.add(cartDiscount);
});
});
});
allAppliedDiscounts.add(cartDiscount);
cartData.appliedCartPurchasesDiscount = cartDiscount;
}
const loyalDiscount = findLoyaltyDiscount(purchasesAmount, discounts);
if (loyalDiscount) {
cartData.services.forEach(service => {
service.tariffs.forEach(tariff => {
tariff.privileges.forEach(privilege => {
const privilegeAppliedDiscounts = cartData.appliedDiscountsByPrivilegeId.get(privilege.privilegeId);
if (!privilegeAppliedDiscounts) throw new Error(`Privilege id ${privilege.privilegeId} not found in appliedDiscountsByPrivilegeId`);
if (!Array.from(privilegeAppliedDiscounts)[0]?.Condition.User) privilegeAppliedDiscounts.add(loyalDiscount);
});
});
});
allAppliedDiscounts.add(loyalDiscount);
cartData.appliedLoyaltyDiscount = loyalDiscount;
}
// Применяем скидки
applyDiscountsToCart(cartData);
cartData.allAppliedDiscounts = Array.from(allAppliedDiscounts);
return Object.freeze(cartData);
}
function applyNkoDiscount(cartData: CartData, discount: Discount) {
cartData.priceAfterDiscounts *= discount.Target.Factor
cartData.allAppliedDiscounts.push(discount)
}
export function findNkoDiscount(discounts: Discount[]): Discount | null {
const applicableDiscounts = discounts.filter(discount => discount.Condition.UserType === "nko")
if (!applicableDiscounts.length) return null
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
return current.Condition.CartPurchasesAmount > prev.Condition.CartPurchasesAmount ? current : prev
})
return maxValueDiscount
function applyDiscountsToCart(cartData: CartData) {
cartData.services.forEach(service => {
let servicePrice = 0;
service.tariffs.forEach(tariff => {
let privilegePriceSum = 0;
let tariffDiscountFactor = 1;
tariff.privileges.forEach(privilege => {
const discounts = cartData.appliedDiscountsByPrivilegeId.get(privilege.privilegeId) ?? [];
const discountsFactor = Array.from(discounts).reduce((factor, discount) => factor * findDiscountFactor(discount), 1);
privilege.price *= discountsFactor;
tariffDiscountFactor *= discountsFactor;
privilegePriceSum += privilege.price;
});
if (tariff.price) tariff.price *= tariffDiscountFactor;
else tariff.price = privilegePriceSum;
servicePrice += tariff.price;
});
service.price = servicePrice;
cartData.priceAfterDiscounts += servicePrice;
});
}

@ -0,0 +1,49 @@
import { Discount, Privilege, PrivilegeWithAmount, Tariff } from "@frontend/kitui";
import { CustomTariffUserValues } from "@root/model/customTariffs";
import { calcCart } from "./calcCart";
export function calcCustomTariffPrice(
customTariffUserValues: CustomTariffUserValues,
servicePrivileges: Privilege[],
cartTariffs: Tariff[],
discounts: Discount[],
purchasesAmount: number,
isUserNko: boolean,
) {
const privileges = new Array<PrivilegeWithAmount>();
const priceBeforeDiscounts = servicePrivileges.reduce((price, privilege) => {
const amount = customTariffUserValues?.[privilege._id] ?? 0;
return price + privilege.price * amount;
}, 0);
Object.keys(customTariffUserValues).forEach(privilegeId => {
const pwa = servicePrivileges.find(p => p._id === privilegeId);
if (!pwa) return;
if (customTariffUserValues[privilegeId] > 0) privileges.push({
...pwa,
amount: customTariffUserValues[privilegeId]
});
});
const customTariff: Tariff = {
_id: crypto.randomUUID(),
name: "",
price: 0,
description: "",
isCustom: true,
isDeleted: false,
privileges: privileges,
};
const cart = calcCart([...cartTariffs, customTariff], discounts, purchasesAmount, isUserNko);
const customService = cart.services.flatMap(service => service.tariffs).find(tariff => tariff.id === customTariff._id);
if (!customService) throw new Error("Custom service not found in cart");
return {
priceBeforeDiscounts,
priceAfterDiscounts: customService.price,
};
}

182
src/utils/calcCart/utils.ts Normal file

@ -0,0 +1,182 @@
import { Discount, findDiscountFactor } from "@frontend/kitui";
export type CartData = {
services: ServiceCartData[];
priceBeforeDiscounts: number;
priceAfterDiscounts: number;
appliedCartPurchasesDiscount: Discount | null;
appliedLoyaltyDiscount: Discount | null;
allAppliedDiscounts: Discount[];
appliedDiscountsByPrivilegeId: Map<string, Set<Discount>>;
};
export type ServiceCartData = {
serviceKey: string;
tariffs: TariffCartData[];
price: number;
appliedServiceDiscount: Discount | null;
};
export type TariffCartData = {
name: string;
id: string;
price: number;
isCustom: boolean;
privileges: PrivilegeCartData[];
};
export type PrivilegeCartData = {
serviceKey: string;
privilegeId: string;
description: string;
price: number;
amount: number;
};
export function findPrivilegeDiscount(
privilegeId: string,
privilegePrice: number,
discounts: Discount[],
userId: string,
): Discount | null {
const applicableDiscounts = discounts.filter(discount => {
if (discount.Condition.User !== "" && discount.Condition.User === userId) return true;
const conditionMinPrice = parseFloat(discount.Condition.Term);
if (!isFinite(conditionMinPrice)) throw new Error(`Couldn't parse Discount.Condition.Term: ${discount.Condition.Term}`);
return (
discount.Layer === 1 &&
privilegeId === discount.Condition.Product &&
privilegePrice >= conditionMinPrice
);
});
if (!applicableDiscounts.length) return null;
let maxValueDiscount: Discount = applicableDiscounts[0];
for (const discount of applicableDiscounts) {
if (discount.Condition.User !== "" && discount.Condition.User === userId) {
maxValueDiscount = discount;
break;
}
if (Number(discount.Condition.Term) > Number(maxValueDiscount.Condition.Term)) {
maxValueDiscount = discount;
}
}
return maxValueDiscount;
}
export function findServiceDiscount(
serviceKey: string,
currentPrice: number,
discounts: Discount[],
userId: string,
): Discount | null {
const applicableDiscounts = discounts.filter(discount => {
if (discount.Condition.User !== "" && discount.Condition.User === userId) return true;
return (
discount.Layer === 2 &&
discount.Condition.Group === serviceKey &&
currentPrice >= Number(discount.Condition.PriceFrom)
);
});
if (!applicableDiscounts.length) return null;
let maxValueDiscount: Discount = applicableDiscounts[0];
for (const discount of applicableDiscounts) {
if (discount.Condition.User !== "" && discount.Condition.User === userId) {
maxValueDiscount = discount;
break;
}
if (Number(discount.Condition.PriceFrom) > Number(maxValueDiscount.Condition.PriceFrom)) {
maxValueDiscount = discount;
}
}
return maxValueDiscount;
}
export function applyCartDiscount(
cartData: CartData,
discounts: Discount[],
) {
const cartDiscount = findCartDiscount(cartData.priceAfterDiscounts, discounts);
if (!cartDiscount) return;
cartData.priceAfterDiscounts *= findDiscountFactor(cartDiscount);
cartData.allAppliedDiscounts.push(cartDiscount);
cartData.appliedCartPurchasesDiscount = cartDiscount;
}
export function findCartDiscount(
cartPurchasesAmount: number,
discounts: Discount[],
): Discount | null {
const applicableDiscounts = discounts.filter(discount => {
return (
discount.Layer === 3 &&
cartPurchasesAmount >= Number(discount.Condition.CartPurchasesAmount)
);
});
if (!applicableDiscounts.length) return null;
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
return Number(current.Condition.CartPurchasesAmount) > Number(prev.Condition.CartPurchasesAmount) ? current : prev;
});
return maxValueDiscount;
}
export function applyLoyaltyDiscount(
cartData: CartData,
discounts: Discount[],
purchasesAmount: number,
) {
const loyalDiscount = findLoyaltyDiscount(purchasesAmount, discounts);
if (!loyalDiscount) return;
cartData.priceAfterDiscounts *= findDiscountFactor(loyalDiscount);
cartData.allAppliedDiscounts.push(loyalDiscount);
cartData.appliedLoyaltyDiscount = loyalDiscount;
}
export function findLoyaltyDiscount(
purchasesAmount: number,
discounts: Discount[],
): Discount | null {
const applicableDiscounts = discounts.filter(discount => {
return (
discount.Layer === 4 &&
discount.Condition.UserType !== "nko" &&
purchasesAmount >= discount.Condition.PurchasesAmount
);
});
if (!applicableDiscounts.length) return null;
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
return current.Condition.PurchasesAmount > prev.Condition.PurchasesAmount ? current : prev;
});
return maxValueDiscount;
}
export function findNkoDiscount(discounts: Discount[]): Discount | null {
const applicableDiscounts = discounts.filter(discount => discount.Condition.UserType === "nko");
if (!applicableDiscounts.length) return null;
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
return current.Condition.CartPurchasesAmount > prev.Condition.CartPurchasesAmount ? current : prev;
});
return maxValueDiscount;
}

@ -1,8 +1,8 @@
import { Discount, Tariff, findDiscountFactor } from "@frontend/kitui"
import { calcCart } from "./calcCart/calcCart"
import { Discount, Tariff, findDiscountFactor } from "@frontend/kitui";
import { calcCart } from "./calcCart/calcCart";
export function calcIndividualTariffPrices(
tariff: Tariff,
targetTariff: Tariff,
discounts: Discount[],
purchasesAmount: number,
currentTariffs: Tariff[],
@ -11,31 +11,23 @@ export function calcIndividualTariffPrices(
priceBeforeDiscounts: number;
priceAfterDiscounts: number;
} {
const priceBeforeDiscounts =
tariff.price || tariff.privileges.reduce((sum, privilege) => sum + privilege.amount * privilege.price, 0)
let priceAfterDiscounts = 0
const priceBeforeDiscounts = targetTariff.price || targetTariff.privileges.reduce(
(sum, privilege) => sum + privilege.amount * privilege.price,
0
);
const cart = calcCart([...currentTariffs, tariff], discounts, purchasesAmount, isUserNko)
console.log('CITP', cart)
if (cart.allAppliedDiscounts[0]?.Target.Overhelm) return {priceBeforeDiscounts: priceBeforeDiscounts, priceAfterDiscounts: priceBeforeDiscounts * cart.allAppliedDiscounts[0].Target.Factor }
cart.services.forEach(s => {
if (s.serviceKey === tariff.privileges[0].serviceKey) {
let processed = false
s.tariffs.forEach(t => {
if (t.id === tariff._id && !processed) {
processed = true
t.privileges.forEach(p => priceAfterDiscounts+=p.price)
}
})
}
})
priceAfterDiscounts*=findDiscountFactor(cart.appliedLoyaltyDiscount)
priceAfterDiscounts*=findDiscountFactor(cart.appliedCartPurchasesDiscount)
const cart = calcCart([...currentTariffs, targetTariff], discounts, purchasesAmount, isUserNko);
if (priceAfterDiscounts === 0) priceAfterDiscounts = cart.priceAfterDiscounts
// cart.allAppliedDiscounts.forEach((discount) => {
// priceAfterDiscounts *= findDiscountFactor(discount)
// })
//priceAfterDiscounts = cart.priceAfterDiscounts
return { priceBeforeDiscounts, priceAfterDiscounts }
const tariffCartData = cart.services.flatMap(service => service.tariffs).find(tariff => tariff.id === targetTariff._id);
if (!tariffCartData) throw new Error(`Target tariff ${targetTariff._id} not found in cart`);
if (cart.allAppliedDiscounts[0]?.Target.Overhelm) return {
priceBeforeDiscounts: priceBeforeDiscounts,
priceAfterDiscounts: priceBeforeDiscounts * cart.allAppliedDiscounts[0].Target.Factor
};
return {
priceBeforeDiscounts,
priceAfterDiscounts: tariffCartData.price,
};
}

@ -6,7 +6,7 @@ interface ComponentError {
timestamp: number;
message: string;
callStack: string | undefined;
componentStack: string;
componentStack: string | null | undefined;
}
export function handleComponentError(error: Error, info: ErrorInfo) {

@ -1,83 +1,20 @@
import { Tariff, devlog } from "@frontend/kitui"
import { getTariffById } from "@root/api/tariff"
import {
addCartTariffs,
removeMissingCartTariffs,
setCartTariffStatus,
useCartStore,
} from "@root/stores/cart"
import { calcCart } from "@root/utils/calcCart/calcCart"
import { useDiscountStore } from "@root/stores/discounts"
import { useTariffStore } from "@root/stores/tariffs"
import { removeTariffFromCart, useUserStore } from "@root/stores/user"
import { useEffect } from "react"
import { useDiscountStore } from "@root/stores/discounts";
import { useUserStore } from "@root/stores/user";
import { useMemo } from "react";
import { calcCart } from "../calcCart/calcCart";
import { CartData } from "../calcCart/utils";
import { useCartTariffs } from "./useCartTariffs";
export function useCart() {
const tariffs = useTariffStore((state) => state.tariffs)
const cartTariffMap = useCartStore((state) => state.cartTariffMap)
const cartTariffIds = useUserStore((state) => state.userAccount?.cart)
const cart = useCartStore((state) => state.cart)
const discounts = useDiscountStore((state) => state.discounts)
const purchasesAmount =
useUserStore((state) => state.userAccount?.wallet.spent) ?? 0
const isUserNko = useUserStore(state => state.userAccount?.status) === "nko"
export function useCart(): CartData {
const cartTariffs = useCartTariffs();
const discounts = useDiscountStore((state) => state.discounts);
const purchasesAmount = useUserStore((state) => state.userAccount?.wallet.spent) ?? 0;
const isUserNko = useUserStore(state => state.userAccount?.status) === "nko";
useEffect(() => {
function addTariffsToCart() {
const knownTariffs: Tariff[] = []
const cart = useMemo(() => {
return calcCart(cartTariffs ?? [], discounts, purchasesAmount, isUserNko);
}, [cartTariffs, discounts, purchasesAmount, isUserNko]);
cartTariffIds?.forEach(async (tariffId) => {
if (typeof cartTariffMap[tariffId] === "object") return
const tariff = tariffs.find((tariff) => tariff._id === tariffId)
if (tariff) return knownTariffs.push(tariff)
if (!cartTariffMap[tariffId]) {
setCartTariffStatus(tariffId, "loading")
const [tariffByIdResponse, tariffByIdError, tariffByIdStatus] =
await getTariffById(tariffId)
if (tariffByIdError) {
devlog(tariffByIdError)
setCartTariffStatus(tariffId, "not found")
if (tariffByIdStatus === 404) {
try {
await removeTariffFromCart(tariffId)
devlog(
`Unexistant tariff with id ${tariffId} deleted from cart`
)
} catch (error) {
devlog("Error deleting unexistant tariff from cart", error)
}
}
return
}
if (tariffByIdResponse) {
addCartTariffs([tariffByIdResponse], discounts, purchasesAmount, isUserNko)
}
}
})
if (knownTariffs.length > 0)
addCartTariffs(knownTariffs, discounts, purchasesAmount, isUserNko)
}
addTariffsToCart()
}, [cartTariffIds, cartTariffMap, discounts, isUserNko, purchasesAmount, tariffs])
useEffect(
function cleanUpCart() {
if (cartTariffIds)
removeMissingCartTariffs(cartTariffIds, discounts, purchasesAmount, isUserNko)
},
[cartTariffIds, discounts, isUserNko, purchasesAmount]
)
const currentTariffs = Object.values(cartTariffMap).filter((tariff): tariff is Tariff => typeof tariff === "object");
return calcCart(currentTariffs, discounts,purchasesAmount,isUserNko)
return cart;
}

@ -0,0 +1,17 @@
import { getTariffArray } from "@root/api/tariff";
import { useUserStore } from "@root/stores/user";
import useSWR from "swr";
export function useCartTariffs() {
const cartTariffIds = useUserStore((state) => state.userAccount?.cart);
const { data } = useSWR(
["cartTariffs", cartTariffIds],
key => getTariffArray(key[1]),
{
keepPreviousData: true,
}
);
return data;
}

@ -1,7 +1,7 @@
{
"extends": "./tsconfig.extend.json",
"compilerOptions": {
"target": "es5",
"target": "ESNext",
"lib": ["es5", "dom", "dom.iterable", "esnext"],
"types": ["node"],
"allowJs": true,

8057
yarn.lock

File diff suppressed because it is too large Load Diff