264 lines
6.9 KiB
TypeScript
264 lines
6.9 KiB
TypeScript
import { calcCart } from "@frontend/kitui";
|
||
import Input from "@kitUI/input";
|
||
import {
|
||
Alert,
|
||
Box,
|
||
Button,
|
||
Checkbox,
|
||
FormControlLabel,
|
||
Paper,
|
||
Table,
|
||
TableBody,
|
||
TableCell,
|
||
TableHead,
|
||
TableRow,
|
||
Typography,
|
||
useMediaQuery,
|
||
useTheme,
|
||
} from "@mui/material";
|
||
import { useDiscounts } from "@root/api/discounts/swr";
|
||
import { useAllPromocodes } from "@root/api/promocode/swr";
|
||
import { requestDiscounts } from "@root/services/discounts.service";
|
||
import { requestPrivileges } from "@root/services/privilegies.service";
|
||
import { setCartData, useCartStore } from "@root/stores/cart";
|
||
import { useTariffStore } from "@root/stores/tariffs";
|
||
import { createDiscountFromPromocode } from "@root/utils/createDiscountFromPromocode";
|
||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
||
import { useState } from "react";
|
||
import CartItemRow from "./CartItemRow";
|
||
|
||
export default function Cart() {
|
||
const theme = useTheme();
|
||
const mobile = useMediaQuery(theme.breakpoints.down(400));
|
||
const discounts = useDiscounts();
|
||
const promocodes = useAllPromocodes();
|
||
const cartData = useCartStore((store) => store.cartData);
|
||
const tariffs = useTariffStore((state) => state.tariffs);
|
||
const [couponField, setCouponField] = useState<string>("");
|
||
const [loyaltyField, setLoyaltyField] = useState<string>("");
|
||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||
const [isNonCommercial, setIsNonCommercial] = useState<boolean>(false);
|
||
const selectedTariffIds = useTariffStore((state) => state.selectedTariffIds);
|
||
|
||
async function handleCalcCartClick() {
|
||
await requestPrivileges();
|
||
await requestDiscounts();
|
||
|
||
const cartTariffs = tariffs.filter((tariff) => selectedTariffIds.includes(tariff._id));
|
||
|
||
let loyaltyValue = parseInt(loyaltyField);
|
||
|
||
if (!isFinite(loyaltyValue)) loyaltyValue = 0;
|
||
|
||
const promocode = promocodes.find((promocode) => {
|
||
if (promocode.dueTo < Date.now() / 1000) return false;
|
||
|
||
return promocode.codeword === couponField.trim();
|
||
});
|
||
|
||
const userId = crypto.randomUUID();
|
||
|
||
const discountsWithPromocodeDiscount = promocode
|
||
? [...discounts, createDiscountFromPromocode(promocode, userId)]
|
||
: discounts;
|
||
|
||
try {
|
||
const cartData = calcCart(cartTariffs, discountsWithPromocodeDiscount, loyaltyValue, userId);
|
||
|
||
setErrorMessage(null);
|
||
setCartData(cartData);
|
||
} catch (error: any) {
|
||
setErrorMessage(error.message);
|
||
setCartData(null);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<Box
|
||
component="section"
|
||
sx={{
|
||
border: "1px solid white",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
alignItems: "center",
|
||
width: "100%",
|
||
pb: "20px",
|
||
borderRadius: "4px",
|
||
}}
|
||
>
|
||
<Typography variant="caption">корзина</Typography>
|
||
<Paper
|
||
variant="bar"
|
||
sx={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
gap: "20px",
|
||
flexDirection: mobile ? "column" : undefined,
|
||
}}
|
||
>
|
||
<FormControlLabel
|
||
checked={isNonCommercial}
|
||
onChange={(e, checked) => setIsNonCommercial(checked)}
|
||
control={
|
||
<Checkbox
|
||
sx={{
|
||
color: theme.palette.secondary.main,
|
||
"&.Mui-checked": {
|
||
color: theme.palette.secondary.main,
|
||
},
|
||
}}
|
||
/>
|
||
}
|
||
label="НКО"
|
||
sx={{
|
||
color: theme.palette.secondary.main,
|
||
}}
|
||
/>
|
||
<Box
|
||
sx={{
|
||
border: "1px solid white",
|
||
padding: "3px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
}}
|
||
>
|
||
<Input
|
||
label="промокод"
|
||
size="small"
|
||
value={couponField}
|
||
onChange={(e) => setCouponField(e.target.value)}
|
||
InputProps={{
|
||
style: {
|
||
backgroundColor: theme.palette.content.main,
|
||
color: theme.palette.secondary.main,
|
||
},
|
||
}}
|
||
InputLabelProps={{
|
||
style: {
|
||
color: theme.palette.secondary.main,
|
||
},
|
||
}}
|
||
/>
|
||
</Box>
|
||
{/* {cartTotal?.couponState &&
|
||
(cartTotal.couponState === "applied" ? (
|
||
<Alert severity="success">Купон применен!</Alert>
|
||
) : (
|
||
<Alert severity="error">Подходящий купон не найден!</Alert>
|
||
))
|
||
} */}
|
||
<Box
|
||
sx={{
|
||
border: "1px solid white",
|
||
padding: "3px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
ml: mobile ? 0 : "auto",
|
||
}}
|
||
>
|
||
<Input
|
||
label="лояльность"
|
||
size="small"
|
||
type="number"
|
||
value={loyaltyField}
|
||
onChange={(e) => setLoyaltyField(e.target.value)}
|
||
InputProps={{
|
||
style: {
|
||
backgroundColor: theme.palette.content.main,
|
||
color: theme.palette.secondary.main,
|
||
},
|
||
}}
|
||
InputLabelProps={{
|
||
style: {
|
||
color: theme.palette.secondary.main,
|
||
},
|
||
}}
|
||
/>
|
||
</Box>
|
||
<Button onClick={handleCalcCartClick}>рассчитать</Button>
|
||
</Paper>
|
||
|
||
{cartData?.services.length && (
|
||
<>
|
||
<Table
|
||
sx={{
|
||
width: "90%",
|
||
margin: "5px",
|
||
border: "2px solid",
|
||
borderColor: theme.palette.secondary.main,
|
||
}}
|
||
aria-label="simple table"
|
||
>
|
||
<TableHead>
|
||
<TableRow
|
||
sx={{
|
||
borderBottom: "2px solid",
|
||
borderColor: theme.palette.grayLight.main,
|
||
height: "100px",
|
||
}}
|
||
>
|
||
<TableCell>
|
||
<Typography variant="h4" sx={{ color: theme.palette.secondary.main }}>
|
||
Имя
|
||
</Typography>
|
||
</TableCell>
|
||
<TableCell>
|
||
<Typography variant="h4" sx={{ color: theme.palette.secondary.main }}>
|
||
Описание
|
||
</Typography>
|
||
</TableCell>
|
||
<TableCell>
|
||
<Typography variant="h4" sx={{ color: theme.palette.secondary.main }}>
|
||
Скидки
|
||
</Typography>
|
||
</TableCell>
|
||
<TableCell>
|
||
<Typography variant="h4" sx={{ color: theme.palette.secondary.main }}>
|
||
стоимость
|
||
</Typography>
|
||
</TableCell>
|
||
</TableRow>
|
||
</TableHead>
|
||
<TableBody>
|
||
{cartData.services.flatMap((service) =>
|
||
service.tariffs.map((tariffCartData) => {
|
||
const appliedDiscounts = tariffCartData.privileges
|
||
.flatMap((privilege) => Array.from(privilege.appliedDiscounts))
|
||
.sort((a, b) => a.Layer - b.Layer);
|
||
|
||
return (
|
||
<CartItemRow
|
||
key={tariffCartData.id}
|
||
tariffCartData={tariffCartData}
|
||
appliedDiscounts={appliedDiscounts}
|
||
/>
|
||
);
|
||
})
|
||
)}
|
||
</TableBody>
|
||
</Table>
|
||
|
||
<Typography
|
||
id="transition-modal-title"
|
||
variant="h6"
|
||
sx={{
|
||
fontWeight: "normal",
|
||
textAlign: "center",
|
||
marginTop: "10px",
|
||
}}
|
||
>
|
||
ИТОГО: <span>{currencyFormatter.format(cartData.priceAfterDiscounts / 100)}</span>
|
||
</Typography>
|
||
</>
|
||
)}
|
||
|
||
{errorMessage !== null && (
|
||
<Alert variant="filled" severity="error" sx={{ mt: "20px" }}>
|
||
{errorMessage}
|
||
</Alert>
|
||
)}
|
||
</Box>
|
||
);
|
||
}
|