rename and refactor cart

This commit is contained in:
nflnkr 2023-03-06 16:25:33 +03:00
parent ef6ddb8b8a
commit ca2a6eba30
6 changed files with 332 additions and 737 deletions

230
src/kitUI/Cart/Cart.tsx Normal file

@ -0,0 +1,230 @@
import theme from "@theme";
import { Button, Paper, Box, Typography, TableHead, TableRow, TableCell, TableBody, Table, Tooltip } from "@mui/material";
import Input from "@kitUI/input";
import { useCartStore } from "@root/stores/cart";
import { useState } from "react";
import { GridSelectionModel } from "@mui/x-data-grid";
import { testUser } from "@root/stores/mocks/user";
import { useDiscountStore } from "@root/stores/discounts";
import { calcCartData, createCartItem, findDiscountById, findDiscountFactorById, formatDiscountFactor } from "./calc";
import { useTariffStore } from "@root/stores/tariffs";
import { AnyDiscount, CartItemTotal } from "@root/model/cart";
interface Props {
selectedTariffs: GridSelectionModel;
}
export default function Cart({ selectedTariffs }: Props) {
const tariffs = useTariffStore(store => store.tariffs);
const discounts = useDiscountStore(store => store.discounts);
const cartTotal = useCartStore(state => state.cartTotal);
const setCartTotal = useCartStore(store => store.setCartTotal);
const [couponField, setCouponField] = useState<string>("");
// const [coupon, setCoupon] = useState<string | undefined>();
const cartRows = cartTotal?.items.map(cartItemTotal => {
const envolvedDiscountsElement = (
<Box>
{cartItemTotal.envolvedDiscounts.map(discountId => (
<DiscountTooltip
key={discountId}
discounts={discounts}
discountId={discountId}
cartItemTotal={cartItemTotal}
/>
))}
</Box>
);
return {
id: cartItemTotal.tariff.id,
tariffName: cartItemTotal.tariff.name,
privilegeDesc: cartItemTotal.tariff.privilege.description,
envolvedDiscounts: envolvedDiscountsElement,
price: cartItemTotal.totalPrice,
};
});
const envolvedCartDiscountsElement = (
<Box sx={{
display: "inline-flex",
flexWrap: "wrap",
}}>
{cartTotal?.envolvedCartDiscounts.map((discountId, index, arr) => (
<span key={discountId}>
<DiscountTooltip
discounts={discounts}
discountId={discountId}
/>
{index < arr.length - 1 &&
<span>,&nbsp;</span>
}
</span>
))}
</Box>
);
function handleCalcCartClick() {
const cartTariffs = tariffs.filter(tariff => selectedTariffs.includes(tariff.id));
const cartItems = cartTariffs.map(tariff => createCartItem(tariff));
setCartTotal(calcCartData(testUser, cartItems, discounts, couponField));
}
return (
<Box
component="section"
sx={{
border: "1px solid white",
display: "flex",
flexDirection: "column",
alignItems: "center",
width: "100%"
}}
>
<Typography variant="caption">
корзина
</Typography>
<Paper
variant="bar"
sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}
>
<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
}
}}
/>
{/* <Button
sx={{ maxWidth: "140px" }}
onClick={() => setCoupon(couponField)}
>применить промокод</Button> */}
</Box>
<Button onClick={handleCalcCartClick}>рассчитать</Button>
</Paper>
<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>
{cartRows?.map(row => (
<TableRow
key={row.id}
sx={{
borderBottom: "2px solid",
borderColor: theme.palette.grayLight.main,
}}
>
<TableCell sx={{
color: theme.palette.secondary.main,
}}>{row.tariffName}</TableCell>
<TableCell sx={{
color: theme.palette.secondary.main,
}}>{row.privilegeDesc}</TableCell>
<TableCell sx={{
color: theme.palette.secondary.main,
}}>{row.envolvedDiscounts}</TableCell>
<TableCell sx={{
color: theme.palette.secondary.main,
}}>{row.price.toFixed(2)} </TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Typography id="transition-modal-title" variant="h6" sx={{
fontWeight: "normal",
textAlign: "center",
marginTop: "15px",
fontSize: "16px"
}}>
Скидки корзины: {envolvedCartDiscountsElement}
</Typography>
<Typography id="transition-modal-title" variant="h6" sx={{
fontWeight: "normal",
textAlign: "center",
marginTop: "10px"
}}>
ИТОГО: <span>{cartTotal?.totalPrice.toFixed(2)} </span>
</Typography>
</Box>
);
}
function DiscountTooltip({ discounts, discountId, cartItemTotal }: {
discounts: AnyDiscount[];
discountId: string;
cartItemTotal?: CartItemTotal;
}) {
const discount = findDiscountById(discounts, discountId);
const discountText = formatDiscountFactor(findDiscountFactorById(discounts, discountId, cartItemTotal?.tariff.privilege.privilegeId));
return (
<Tooltip title={
<>
<Typography>Скидка: {discount?.name}</Typography>
<Typography>{discount?.description}</Typography>
<Typography>-------</Typography>
<Typography>layer: {discount?.layer}</Typography>
<Typography>id: {discount?._id}</Typography>
</>
}>
<span>{discountText}</span>
</Tooltip>
);
}

@ -1,8 +1,8 @@
import { Cart } from "../../../../model/cart";
import { Tariffs } from "../../../../model/tariff";
import { User } from "../../../../model/user";
import { exampleCartValues, TestCase } from "../../../../stores/mocks/exampleCartValues";
import { calcCartData } from "./cartCalcs";
import { CartItem } from "@root/model/cart";
import { Tariff } from "@root/model/tariff";
import { User } from "@root/model/user";
import { exampleCartValues, TestCase } from "@stores/mocks/exampleCartValues";
import { calcCartData, createCartItem } from "./calc";
const MAX_PRICE_ERROR = 0.01;
@ -85,7 +85,7 @@ describe("cart tests", () => {
expect(cartTotal.totalPrice).toBeGreaterThan(testCase.expect.price - MAX_PRICE_ERROR);
expect(cartTotal.totalPrice).toBeLessThan(testCase.expect.price + MAX_PRICE_ERROR);
});
it("case 4", () => {
it("case 5", () => {
const testCase = prepareTestCase(exampleCartValues.testCases[4]);
// работает если не учитывать скидки id26, id27 (скидка на templategen от 1000/5000 р.)
@ -135,8 +135,6 @@ describe("cart tests", () => {
it("история про то, как получилось получить скидку за сервис", () => {
const testCase = prepareTestCase(exampleCartValues.testCases[7]);
// TODO Какая-то из скидок id30, id1 не учитывается в expected
const cartTotal = calcCartData(testCase.user, testCase.cartItems, discounts);
const allEnvolvedDiscounts: string[] = [...cartTotal.envolvedCartDiscounts];
cartTotal.items.forEach(cartItem => {
@ -163,7 +161,7 @@ describe("cart tests", () => {
it("юзер использовал промокод id33. он заменяет скидку на p6 собой. в один момент времени может быть активирован только 1 промокод, т.е. после активации следующего, предыдущий заменяется. но в промокоде может быть несколько скидок. промокоды имеют скидки только на привелеги", () => {
const testCase = prepareTestCase(exampleCartValues.testCases[9]);
const cartTotal = calcCartData(testCase.user, testCase.cartItems, discounts, "AB\\CD");
const cartTotal = calcCartData(testCase.user, testCase.cartItems, discounts, "ABCD");
const allEnvolvedDiscounts: string[] = [...cartTotal.envolvedCartDiscounts];
cartTotal.items.forEach(cartItem => {
allEnvolvedDiscounts.push(...cartItem.envolvedDiscounts);
@ -182,6 +180,19 @@ describe("cart tests", () => {
allEnvolvedDiscounts.push(...cartItem.envolvedDiscounts);
});
expect(allEnvolvedDiscounts.sort()).toEqual(testCase.expect.envolvedDiscounts.sort());
expect(cartTotal.totalPrice).toBeGreaterThan(testCase.expect.price - MAX_PRICE_ERROR);
expect(cartTotal.totalPrice).toBeLessThan(testCase.expect.price + MAX_PRICE_ERROR);
});
it("case 12", () => {
const testCase = prepareTestCase(exampleCartValues.testCases[11]);
const cartTotal = calcCartData(testCase.user, testCase.cartItems, discounts);
const allEnvolvedDiscounts: string[] = [...cartTotal.envolvedCartDiscounts];
cartTotal.items.forEach(cartItem => {
allEnvolvedDiscounts.push(...cartItem.envolvedDiscounts);
});
expect(allEnvolvedDiscounts.sort()).toEqual(testCase.expect.envolvedDiscounts.sort());
expect(cartTotal.totalPrice).toBeGreaterThan(testCase.expect.price - MAX_PRICE_ERROR);
expect(cartTotal.totalPrice).toBeLessThan(testCase.expect.price + MAX_PRICE_ERROR);
@ -195,15 +206,17 @@ function prepareTestCase(testCase: TestCase): ({
envolvedDiscounts: string[];
};
user: User;
cartItems: Cart.CartItem[];
cartItems: CartItem[];
}) {
const user = testCase.input.UserInformation;
const tariffs = testCase.input.Products.map((testProduct): Tariffs.Tariff => ({
const tariffs = testCase.input.Products.map((testProduct): Tariff => ({
id: "someId",
name: "someName",
amount: testProduct.Amount,
customPricePerUnit: testProduct.Price && testProduct.Price / testProduct.Amount, // приводим price из сниппета к pricePerUnit
privilege: findPrivilegeById(testProduct.ID)
}));
const cartItems: Cart.CartItem[] = tariffs.map(createCartItem);
const cartItems: CartItem[] = tariffs.map(createCartItem);
return { expect: testCase.expect, user, cartItems };
}
@ -213,11 +226,4 @@ function findPrivilegeById(id: string) {
if (!privilege) throw new Error(`Privilege not found with id ${id}`);
return privilege;
}
function createCartItem(tariff: Tariffs.Tariff): Cart.CartItem {
const pricePerUnit = tariff.customPricePerUnit ?? tariff.privilege.pricePerUnit;
const price = pricePerUnit * tariff.amount;
return { tariff, price };
}

@ -1,15 +1,15 @@
import { Cart } from "../../../../model/cart";
import { SERVICE_LIST, Tariffs } from "../../../../model/tariff";
import { User } from "../../../../model/user";
import { CartItem, AnyDiscount, CartTotal, CartItemTotal, PrivilegeDiscount, CartPurchasesAmountDiscount, PurchasesAmountDiscount, ServiceToPriceMap, ServiceDiscount, UserDiscount } from "@root/model/cart";
import { ServiceType, SERVICE_LIST, Tariff } from "@root/model/tariff";
import { User } from "@root/model/user";
export function calcCartData(
user: User,
cartItems: Cart.CartItem[],
discounts: Cart.AnyDiscount[],
cartItems: CartItem[],
discounts: AnyDiscount[],
coupon?: string,
): Cart.CartTotal {
const cartTotal: Cart.CartTotal = {
): CartTotal {
const cartTotal: CartTotal = {
items: [],
totalPrice: 0,
priceByService: {
@ -54,7 +54,7 @@ export function calcCartData(
// layer 1
for (const cartItem of cartItems) {
const cartItemTotal: Cart.CartItemTotal = {
const cartItemTotal: CartItemTotal = {
tariff: cartItem.tariff,
envolvedDiscounts: [],
totalPrice: cartItem.price,
@ -93,11 +93,11 @@ export function calcCartData(
}
// layer 2
SERVICE_LIST.forEach(service => {
SERVICE_LIST.map(service => service.serviceKey).forEach(service => {
const serviceDiscount = findMaxServiceDiscount(service, discounts, cartTotal.priceByService);
if (serviceDiscount) {
cartTotal.priceByService[service].defaultTariffs *= serviceDiscount.target.factor;
cartTotal.envolvedCartDiscounts.push(serviceDiscount._id);
if (cartTotal.priceByService[service].defaultTariffs > 0) cartTotal.envolvedCartDiscounts.push(serviceDiscount._id);
}
cartTotal.totalPrice += cartTotal.priceByService[service].defaultTariffs;
@ -121,8 +121,8 @@ export function calcCartData(
return cartTotal;
}
function findMaxApplicablePrivilegeDiscount(discounts: Cart.AnyDiscount[], tariff: Tariffs.Tariff): Cart.PrivilegeDiscount | null {
const applicableDiscounts = discounts.filter((discount): discount is Cart.PrivilegeDiscount => {
function findMaxApplicablePrivilegeDiscount(discounts: AnyDiscount[], tariff: Tariff): PrivilegeDiscount | null {
const applicableDiscounts = discounts.filter((discount): discount is PrivilegeDiscount => {
return (
discount.conditionType === "privilege" &&
tariff.privilege.privilegeId === discount.condition.privilege.id &&
@ -139,8 +139,8 @@ function findMaxApplicablePrivilegeDiscount(discounts: Cart.AnyDiscount[], tarif
return maxValueDiscount;
}
function findMaxCartPurchasesAmountDiscount(discounts: Cart.AnyDiscount[], cartTotal: Cart.CartTotal): Cart.CartPurchasesAmountDiscount | null {
const applicableDiscounts = discounts.filter((discount): discount is Cart.CartPurchasesAmountDiscount => {
function findMaxCartPurchasesAmountDiscount(discounts: AnyDiscount[], cartTotal: CartTotal): CartPurchasesAmountDiscount | null {
const applicableDiscounts = discounts.filter((discount): discount is CartPurchasesAmountDiscount => {
return discount.conditionType === "cartPurchasesAmount" && cartTotal.totalPrice >= discount.condition.cartPurchasesAmount;
});
@ -153,8 +153,8 @@ function findMaxCartPurchasesAmountDiscount(discounts: Cart.AnyDiscount[], cartT
return maxValueDiscount;
}
function findMaxTotalPurchasesAmountDiscount(discounts: Cart.AnyDiscount[], user: User): Cart.PurchasesAmountDiscount | null {
const applicableDiscounts = discounts.filter((discount): discount is Cart.PurchasesAmountDiscount => {
function findMaxTotalPurchasesAmountDiscount(discounts: AnyDiscount[], user: User): PurchasesAmountDiscount | null {
const applicableDiscounts = discounts.filter((discount): discount is PurchasesAmountDiscount => {
return discount.conditionType === "purchasesAmount" && user.PurchasesAmount >= discount.condition.purchasesAmount;
});
@ -168,11 +168,11 @@ function findMaxTotalPurchasesAmountDiscount(discounts: Cart.AnyDiscount[], user
}
function findMaxServiceDiscount(
service: Tariffs.ServiceType,
discounts: Cart.AnyDiscount[],
priceByService: Cart.ServiceToPriceMap,
): Cart.ServiceDiscount | null {
const discountsForTariffService = discounts.filter((discount): discount is Cart.ServiceDiscount => {
service: ServiceType,
discounts: AnyDiscount[],
priceByService: ServiceToPriceMap,
): ServiceDiscount | null {
const discountsForTariffService = discounts.filter((discount): discount is ServiceDiscount => {
return (
discount.conditionType === "service" &&
discount.condition.service.id === service &&
@ -189,8 +189,8 @@ function findMaxServiceDiscount(
return maxValueDiscount;
}
function findUserDiscount(discounts: Cart.AnyDiscount[], user: User, coupon: string,): Cart.UserDiscount | null {
const userDiscount = discounts.find((discount): discount is Cart.UserDiscount => {
function findUserDiscount(discounts: AnyDiscount[], user: User, coupon: string,): UserDiscount | null {
const userDiscount = discounts.find((discount): discount is UserDiscount => {
return (
discount.conditionType === "user" &&
discount.condition.user === user.ID &&
@ -199,4 +199,57 @@ function findUserDiscount(discounts: Cart.AnyDiscount[], user: User, coupon: str
});
return userDiscount ?? null;
}
}
export function createCartItem(tariff: Tariff): CartItem {
const pricePerUnit = tariff.customPricePerUnit ?? tariff.privilege.pricePerUnit;
const price = pricePerUnit * tariff.amount;
return { tariff, price, id: "someId" };
}
export function findDiscountById(discounts: AnyDiscount[], id: string) {
const discount = discounts.find(discount => discount._id === id);
if (!discount) throw new Error("Discount not found by id");
return discount;
}
export function findDiscountFactorById(discounts: AnyDiscount[], id: string, privilegeId?: string) {
const discount = discounts.find(discount => discount._id === id);
if (!discount) throw new Error("Discount not found by id");
switch (discount.conditionType) {
case "cartPurchasesAmount" || "purchasesAmount":
return discount.factor;
case "privilege": {
const product = discount.target.products.find(product => product.privilegeId === privilegeId);
if (!product) throw new Error("Discount target product not found");
return product.factor;
}
case "user": {
const product = discount.target.products.find(product => product.privilegeId === privilegeId);
if (!product) throw new Error("Discount target product not found");
return product.factor;
}
case "service":
return discount.target.factor;
case "userType":
return discount.target.factor;
}
throw new Error(`Unknown discount condition type: ${discount.conditionType}`);
}
export function formatDiscountFactor(factor: number): string {
return `${((1 - factor) * 100).toFixed(1)}%`;
}
export const PositiveInput = (event: any) => {
const numberInput = parseInt(event.target.value);
if (isNaN(numberInput) || numberInput < 0) {
event.target.value = '0';
}
};

@ -1,87 +0,0 @@
// export const setPromocode = (name: string) => {
// let codeNumber = -1;
//
// promocodeArray.forEach((item, i) => {
// if (name != "" && item.name == name) codeNumber = i;
// });
//
// setSelectedPromocode(codeNumber);
// }
export const PositiveInput = (event: any) => {
const numberInput = parseInt(event.target.value);
if (isNaN(numberInput) || numberInput < 0) {
event.target.value = '0'
}
}
// export const calcPriseTariff () => {
//
// // считаем цену в ТАРИФАХ
// price = item.price;
// percents = 0;
// discounts = "";
//
// // применяем скидки по промокоду
// if (selectedPromocode >= 0) {
// promocodeArray[selectedPromocode].privileges.forEach((privilege) => {
// console.log(item.service)
// console.log(privilege.good)
// if (item.service == privilege.good) {
// appliedDscnts.push(privilege.discount)
// price *= (1 - privilege.discount)
// }
// })
// } else {
// // применяем активные скидки
// if (fitDiscounts.length >= 0) {
// fitDiscounts.forEach((activeDiscount) => {
// const discount = discountsArray[activeDiscount]
// discount.privileges.forEach((p) => {
// const svcName = item.service.split(' ')[0]
// if (p.good == svcName) {
// const summary = cartSummary.get(svcName) || {
// mbs: 0,
// points: 0,
// days: 0
// }
// if (discount.toCapacity === 0 && discount.toTime === 0 && discount.basketMore === 0 && !(discount.incomeMore) ||
// discount.toCapacity > 0 && summary.points > discount.toCapacity && item.points > 0 && discount.toTime == 0
// || discount.toTime > 0 && summary.days > discount.toTime * 100 && item.time > 0 && discount.toCapacity == 0 ||
// discount.toTime > 0 && discount.toCapacity > 0 && summary.days > discount.toTime * 100 && summary.points > discount.toCapacity) {
//
// price *= (1 - p.discount)
// appliedDscnts.push(p.discount)
// }
//
//
// }
// })
// });
// }
//
// }
// percents = Number(percents.toFixed(2));
//
// priceBefore = price;
// price = price - (price * percents);
// prices += price;
// }
//
// // применяем активные скидки за объем корзины
// const discountsAfter = (priceBefore: number) => {
// const discounts: Array<number> = [];
//
//
// if (fitDiscounts.length >= 0 && !nonCommercial && selectedPromocode < 0) {
// fitDiscounts.forEach((activeDiscount) => {
// const d = discountsArray[activeDiscount]
// console.log(d)
// console.log(fieldAddedValue)
// if (d.basketMore > 0 && getPrice > d.basketMore && d.basketMore === fitDiscounts.reduce((a, e) => Math.max(a, discountsArray[e].basketMore), 0) ||
// d.incomeMore > 0 && parseInt(fieldAddedValue) > d.incomeMore) {
// prices *= (1 - d.privileges[0].discount)
// discounts.push(d.privileges[0].discount)
// }
// });
// }

@ -1,372 +0,0 @@
import * as React from "react";
import theme from "@theme";
import {Button, Paper, ListItemText, IconButton, FormControlLabel, Box, TextField, Typography, Container, Checkbox, List, ListItem, ListItemAvatar, Avatar} from "@mui/material";
import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
import DeleteIcon from "@mui/icons-material/Delete";
import Input from "@kitUI/input";
import useStore, {StoreState} from "../../stores/store";
import {useDemoData} from "@mui/x-data-grid-generator";
import {GridSelectionModel} from "@mui/x-data-grid";
import {ArrayProps} from "../../pages/dashboard/Content/Tariffs/types";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import TableCell from "@mui/material/TableCell";
import TableBody from "@mui/material/TableBody";
import Radio from "@mui/material/Radio";
import Skeleton from "@mui/material/Skeleton";
import Table from "@mui/material/Table";
export default (props:any):any => {
// const [added, setAdded] = React.useState<number>(0)
// const [notCommercial, setNotCommercial] = React.useState<boolean>(false)
//
// const setPromocode = (name: string) => {
// let codeNumber = -1;
//
// promocodeArray.forEach((item, i) => {
// if (name != "" && item.name == name) codeNumber = i;
// });
//
// setSelectedPromocode(codeNumber);
// }
//
// const PositiveInput = (event: any) => {
// const numberInput = parseInt(event.target.value);
// if (isNaN(numberInput) || numberInput < 0) {
// event.target.value = '0'
// }
// }
//
// // const getData = localStorage.getItem("tariffs");
// // const store = useStore( (state) => state );
// //
// // if( getData && !store.tariffsArray.length ) {
// // const rows:Array<ArrayProps> = JSON.parse(getData);
// // if( rows.length ) { store.tariffsArraySet( rows ); };
// // }
// let priceBefore = 0;
// let price = 0;
// let prices = 0;
// let percents = 0;
// let discounts = "";
// let discountsSum = "";
//
// const { discountsArray, discountsArraySet } = useStore<StoreState>((state) => state);
// const { discountsActiveArray, discountsActiveArraySet } = useStore<StoreState>((state) => state);
//
// const [nonCommercial, setNonCommercial] = React.useState(false);
//
// const { data } = useDemoData({
// dataSet: "Commodity",
// rowLength: 10,
// maxColumns: 5,
// });
//
//
// const { tariffsArray } = useStore<StoreState>((state) => state);
// const tariffsArrayConverted = tariffsArray.map( (item) => {
// if( item.type === "package" && item.tariffs ) {
// const result = item.tariffs.reduce( (acc, tariff) => {
// acc.service = acc.service? `${acc.service}, ${tariff.service}` : tariff.service;
// acc.disk = acc.disk+ tariff.disk;
// acc.time = acc.time+ tariff.time;
// acc.points = acc.points + tariff.points;
// acc.price = acc.price + tariff.price;
//
// return acc;
// }, { service: "", disk: "", time: "", points: "", price: 0 } );
//
// return { id: item.id, name: item.name, type: item.type, ...result }
// } else {
// return item;
// }
// } );
//
// const { tariffsSelectedRowsData, tariffsSelectedRowsDataSet } = useStore<StoreState>((state) => state);
// const onRowsSelectionHandler = ( ids:GridSelectionModel ) => {
// const result:Array<ArrayProps> = [];
// ids.forEach((id) => tariffsArray.forEach( (row) => {
// if(row.id === id) result.push(row);
// } ) );
//
// tariffsSelectedRowsDataSet( result );
// };
//
// const { cartRowsData, cartRowsDataSet } = useStore<StoreState>((state) => state);
// const handleToBasket = () => {
// cartRowsDataSet( tariffsSelectedRowsData );
// }
//
// const handleRemoveBasket = ( id:number ) => {
// const cartFiltered = cartRowsData.filter( (row) => row.id != id );
// cartRowsDataSet( cartFiltered );
// }
//
// const fieldPromocode = React.useRef<HTMLInputElement | null>(null);
// const checkPromocode = () => {
// if( fieldPromocode.current != null ) {
// setPromocode( fieldPromocode.current.value );
// }
// }
//
// let { promocodeArray, promocodeArraySet } = useStore<StoreState>((state) => state);
// const [selectedPromocode, setSelectedPromocode] = React.useState( -1 );
//
// //promocodeArray = [ ...rowz ];
//
// const setPromocode = ( name:string ) => {
// let codeNumber = -1;
//
// promocodeArray.forEach( (item, i) => {
// if( name != "" && item.name == name ) codeNumber = i;
// } );
//
// setSelectedPromocode( codeNumber );
// }
//
// const PositiveInput = (event:any) => {
// const numberInput = parseInt(event.target.value);
// if(isNaN(numberInput) || numberInput < 0) {event.target.value = '0'}
// }
//
// const fieldAdded = React.useRef<HTMLInputElement | null>(null);
// const [ fieldAddedValue, setFieldAddedValue ] = React.useState("");
//
// const changeAdded = (event:any) => {
// if( fieldAdded.current != null ) {
// if( fieldAdded.current.value != null ) {
// setFieldAddedValue( fieldAdded.current.value );
// }
//
// }
// PositiveInput(event)
// };
//
// const separator = (amount: number) => {
// console.log(amount)
//
// if( String(amount).length < 4 ) { return amount; }
//
// let result:Array<string> = [];
// const arrs = String(amount).split('.')
// const arr = arrs[0].split('').reverse();
//
// arr.forEach( (item, i:number) => {
// result.push( String( arr[ i ] ) );
// if( ((i+1) / 3) - Math.round((i+1) / 3) == 0 ) result.push(" ");
// });
//
// if( arrs.length > 1 ) { return result.reverse().join("") +"." +arrs[1]; }
// else { return result.reverse().join(""); }
//
// }
//
//
// const cartSummary = new Map()
// cartRowsData.forEach((row) => {
// const svcName = row.service.split(" ")[0]
// const prev = cartSummary.get(svcName)
// console.log(row)
// console.log(prev)
// if (!prev) {
// cartSummary.set(svcName, {
// mbs: row.disk,
// points: row.points,
// days: row.time,
// })
// } else {
// cartSummary.set(svcName, {
// mbs: row.disk+prev.mbs,
// points: row.points+prev.points,
// days: row.time+prev.days,
// })
// }
// })
// console.log(cartSummary)
//
// const fitDiscounts = discountsActiveArray.filter(e => {
// const d = discountsArray[e]
// const summary = cartSummary.get(d.privileges[0].good.split(' ')[0])
// return d.incomeMore*100 < parseInt(fieldAddedValue) && d.incomeMore > 0 ||
// d.toTime < (summary ? summary.days : 0) && d.toTime > 0 && d.toCapacity === 0 ||
// d.toCapacity > 0 && d.toCapacity < (summary ? summary.points : 0) && d.toTime === 0 ||
// d.toCapacity > 0 && d.toTime > 0 && d.toCapacity < (summary ? summary.points : 0) && d.toTime < (summary ? summary.days : 0) ||
// !d.toCapacity && !d.toTime && !d.incomeMore && !d.basketMore ||
// d.basketMore
// }).filter((e,i,a)=>{
// const d = discountsArray[e]
// if (d.incomeMore) {
// return d.incomeMore === a.reduce((a, e) => Math.max(a, discountsArray[e].incomeMore || 0), 0 )
// }
// if (d.toTime && d.toCapacity) {
// return d.toTime === a.reduce((a, e) => Math.max(a, (discountsArray[e].toTime && discountsArray[e].toCapacity) ? discountsArray[e].toTime:0 ), 0 ) && d.toCapacity === a.reduce((a, e) => Math.max(a, (discountsArray[e].toCapacity && discountsArray[e].toTime) ? discountsArray[e].toCapacity : 0 ), 0 )
// }
// if (d.toTime && !d.toCapacity) {
// return d.toTime === a.reduce((a, e) => Math.max(a, discountsArray[e].toTime && !discountsArray[e].toCapacity ? discountsArray[e].toTime : 0), 0 )
// }
// if (!d.toTime && d.toCapacity) {
// return d.toCapacity === a.reduce((a, e) => Math.max(a, discountsArray[e].toCapacity && !discountsArray[e].toTime ? discountsArray[e].toCapacity : 0), 0 )
// }
// return true
// })
// console.log(fitDiscounts)
//
// const discountsAfter = ( getPrice:number ) => {
// const discounts:Array<number> = [];
// priceBefore = getPrice;
//
// prices = getPrice;
// console.log(getPrice)
//
// // применяем активные скидки за объем корзины
//
// if( fitDiscounts.length >= 0 && !nonCommercial && selectedPromocode < 0 ) {
// fitDiscounts.forEach( (activeDiscount) => {
// const d = discountsArray[activeDiscount]
// console.log(d)
// console.log(fieldAddedValue)
// if (d.basketMore > 0 && getPrice > d.basketMore && d.basketMore === fitDiscounts.reduce((a,e) => Math.max(a, discountsArray[e].basketMore), 0) ||
// d.incomeMore > 0 && parseInt(fieldAddedValue) > d.incomeMore) {
// prices *= (1-d.privileges[0].discount)
// discounts.push(d.privileges[0].discount)
// }
// });
// }
//
// if( nonCommercial ) {
// prices *= 0.2
// return `80%`;
// }
//
// return discounts.map(e => `${(e*100).toFixed(2)}%`).join(' × ') + ` = ${(100 - discounts.reduce((a : number,cv : number) => a*(1-cv), 100)) .toFixed(2)}%`;
// }
return (
<Box
component="section"
sx={{
border: "1px solid white",
display: "flex",
flexDirection: "column",
alignItems: "center",
width: "100%"
}}
>
<Typography variant="caption">
корзина
</Typography>
<Paper
variant="bar"
sx={{display:"flex", alignItems:"center", justifyContent: "space-between"}}
>
<FormControlLabel
label="НКО"
control={
<Checkbox
sx={{
color: theme.palette.secondary.main,
"&.Mui-checked": {
color: theme.palette.secondary.main,
},
}}
/>
}
sx={{
color: theme.palette.secondary.main,
}}
/>
<Input
label="внесено"
type="number"
size="small"
// onChange={(e:React.ChangeEvent<HTMLInputElement>) => setAdded(Number(e.target.value))}
/>
<Box
sx={{
border: "1px solid white",
padding: "3px",
display:"flex",
flexDirection:"column"
}}
>
<Input
label="промокод"
size="small"
/>
<Button sx={{maxWidth:"140px"}}>применить промокод</Button>
</Box>
<Button>рассчитать</Button>
</Paper>
{/*<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>*/}
{/* </TableRow>*/}
{/* </TableHead>*/}
{/* <TableBody>*/}
{/* </TableBody>*/}
{/*</Table>*/}
<Typography id="transition-modal-title" variant="h6" sx={{
fontWeight: "normal",
textAlign: "center",
marginTop: "15px",
fontSize: "16px"
}}>
Скидки:
</Typography>
<Typography id="transition-modal-title" variant="h6" sx={{
fontWeight: "normal",
textAlign: "center",
marginTop: "10px"
}}>
ИТОГО:
</Typography>
</Box>
)
}

@ -1,235 +0,0 @@
import { Avatar, Box, Checkbox, FormControlLabel, IconButton, List, ListItem, ListItemAvatar, ListItemText, TextField, Typography, useTheme } from "@mui/material";
import DeleteIcon from "@mui/icons-material/Delete";
import { useMemo, useState } from "react";
import { calcFitDiscounts, calcTotalAndRowData, separator } from "./utils";
import type { CartSummary, Promocode } from "../../../../model/cart";
import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
import { useDiscountStore } from "../../../../stores/discounts";
import { useCartStore } from "../../../../stores/cart";
interface Props {
promocode?: Promocode;
}
export default function Cart({ promocode }: Props) {
const theme = useTheme();
const discountsArray = useDiscountStore(state => state.discountsArray);
const discountsActiveArray = useDiscountStore(state => state.discountsActiveArray);
const cartRowsData = useCartStore(state => state.cartRowsData);
const cartRowsDataSet = useCartStore(state => state.setCartRowsData);
const [isNonCommercial, setIsNonCommercial] = useState(false);
const [addedValueField, setAddedValueField] = useState("");
const cartSummary = useMemo(() => {
const cartSum: { [key: string]: CartSummary; } = {};
cartRowsData.forEach((row) => {
const prev = cartSum[row.service];
cartSum[row.service] = {
mbs: row.disk + (prev?.mbs || 0),
points: row.points + (prev?.points || 0),
days: row.time + (prev?.days || 0),
};
});
return cartSum;
}, [cartRowsData]);
const fitDiscounts = useMemo(() =>
calcFitDiscounts(discountsArray, discountsActiveArray, cartSummary, addedValueField),
[addedValueField, cartSummary, discountsActiveArray, discountsArray]
);
const { totalPrice, calculatedCartRowData } = useMemo(() => calcTotalAndRowData(
cartRowsData,
isNonCommercial,
discountsArray,
discountsActiveArray,
fitDiscounts,
addedValueField,
cartSummary,
promocode,
), [addedValueField, cartRowsData, cartSummary, discountsActiveArray, discountsArray, fitDiscounts, isNonCommercial, promocode]);
const { resultPrice, discountText } = useMemo(() => {
const discounts: Array<number> = [];
let resultPrice = totalPrice;
if (isNonCommercial) {
resultPrice *= 0.2;
return { resultPrice, discountText: `80%` };
}
// применяем активные скидки за объем корзины
if (fitDiscounts.length >= 0 && !isNonCommercial && !promocode) {
fitDiscounts.forEach(activeDiscount => {
const discount = discountsArray[activeDiscount];
if ((discount.basketMore > 0 && totalPrice > discount.basketMore && discount.basketMore === fitDiscounts.reduce((a, e) => Math.max(a, discountsArray[e].basketMore), 0)) ||
(discount.incomeMore > 0 && parseInt(addedValueField) > discount.incomeMore)) {
resultPrice *= (1 - discount.privileges[0].discount);
discounts.push(discount.privileges[0].discount);
}
});
}
const discountText = discounts.map(e => `${(e * 100).toFixed(2)}%`).join(' × ') + ` = ${(100 - discounts.reduce((a: number, cv: number) => a * (1 - cv), 100)).toFixed(2)}%`;
return {
resultPrice,
discountText,
};
}, [addedValueField, discountsArray, fitDiscounts, isNonCommercial, promocode, totalPrice]);
const handleRemoveBasket = (id: number) => cartRowsDataSet(cartRowsData.filter((row) => row.id !== id));
return (
<Box sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
paddingBottom: "45px"
}}>
<Typography id="transition-modal-title" variant="caption">
Корзина
</Typography>
<Box sx={{
display: "flex",
marginTop: "15px",
marginBottom: "15px",
maxWidth: "350px",
width: '100%',
justifyContent: "space-between"
}}>
<FormControlLabel
label="НКО"
control={<Checkbox
sx={{
color: theme.palette.secondary.main,
"&.Mui-checked": {
color: theme.palette.secondary.main,
},
}}
onClick={() => setIsNonCommercial(prev => !prev)}
/>}
/>
<TextField
id="standard-basic"
label={"Внесено"}
variant="filled"
size="small"
color="secondary"
type="number"
sx={{
width: "200px"
}}
InputProps={{
style: {
backgroundColor: theme.palette.content.main,
color: theme.palette.secondary.main,
}
}}
InputLabelProps={{
style: {
color: theme.palette.secondary.main
}
}}
value={addedValueField}
onChange={e => setAddedValueField(Number(e.target.value) >= 0 ? e.target.value : "")}
/>
</Box>
<List sx={{
border: "1px solid",
borderColor: theme.palette.secondary.main,
maxWidth: '745px',
width: '100%',
}}>
<ListItem>
<ListItemText
primary="Название"
sx={{
textAlign: "center",
// minWidth: "250px",
maxWidth: "250px",
padding: '0 10px'
}}
/>
<ListItemText
primary="Цена"
sx={{
textAlign: "center",
// minWidth: "200px",
padding: '0 10px',
maxWidth: "200px"
}}
/>
<ListItemText
primary="Скидки"
sx={{
textAlign: "center",
// minWidth: "200px",
padding: '0 10px',
maxWidth: "400px"
}}
/>
<IconButton edge="end" aria-label="delete">
<DeleteIcon sx={{
color: theme.palette.grayDisabled.main,
display: "none"
}} />
</IconButton>
</ListItem>
{calculatedCartRowData.map((cartRow) => (
<ListItem key={cartRow.id}>
<ListItemAvatar>
<Avatar sx={{ backgroundColor: theme.palette.secondary.main }}>
<ShoppingCartIcon sx={{
color: theme.palette.content.main,
}} />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={cartRow.name}
sx={{ maxWidth: "250px" }}
/>
<ListItemText
primary={`${separator(cartRow.price)}`}
sx={{ textAlign: "center", maxWidth: "200px" }}
/>
<ListItemText
primary={`${(cartRow.appliedDiscounts.map(e => (e * 100).toFixed(2)).join(' × '))} = ${(100 - cartRow.appliedDiscounts.reduce((a: number, cv: number) => a * (1 - cv), 100)).toFixed(2)}%`}
sx={{ textAlign: "center", maxWidth: "400px" }}
/>
<IconButton edge="end" aria-label="delete" onClick={() => handleRemoveBasket(cartRow.id)}>
<DeleteIcon sx={{
color: theme.palette.secondary.main,
}} />
</IconButton>
</ListItem>
))}
<Typography id="transition-modal-title" variant="h6" sx={{
fontWeight: "normal",
textAlign: "center",
marginTop: "15px",
fontSize: "16px"
}}>
Скидки: &ensp; {discountText}
</Typography>
<Typography id="transition-modal-title" variant="h6" sx={{
fontWeight: "normal",
textAlign: "center",
marginTop: "10px"
}}>
ИТОГО: &ensp; {resultPrice}
</Typography>
</List>
</Box>
);
}