Удаление и добавление тарифов в таблицу: Список тарифов

This commit is contained in:
ArtChaos189 2023-06-06 14:27:49 +03:00
parent d40b241e5b
commit 19ccbdaf4e
6 changed files with 412 additions and 407 deletions

@ -47,61 +47,13 @@ root.render(
<SnackbarProvider>
<BrowserRouter>
<Routes>
<Route
path="/"
element={
<PublicRoute>
<Signin />
</PublicRoute>
}
/>
<Route
path="/signin"
element={
<PublicRoute>
<Signin />
</PublicRoute>
}
/>
<Route
path="/signup"
element={
<PublicRoute>
<Signup />
</PublicRoute>
}
/>
<Route
path="/restore"
element={
<PublicRoute>
<Restore />
</PublicRoute>
}
/>
<Route
path="/dispatch"
element={
<PublicRoute>
<Sections />
</PublicRoute>
}
/>
<Route
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
>
<Route
path="/settingRoles"
element={
<PrivateRoute>
<SettingRoles />
</PrivateRoute>
}
/>
<Route path="/" element={<Signin />} />
<Route path="/signin" element={<Signin />} />
<Route path="/signup" element={<Signup />} />
<Route path="/restore" element={<Restore />} />
<Route path="/dispatch" element={<Sections />} />
<Route element={<Dashboard />}>
<Route path="/settingRoles" element={<SettingRoles />} />
{componentsArray.map((element: any) => (
<Route key={element} path={element[0]} element={element[1]} />
))}

@ -1,5 +1,19 @@
import theme from "@theme";
import { Button, Paper, Box, Typography, TableHead, TableRow, TableCell, TableBody, Table, Tooltip, Alert, Checkbox, FormControlLabel } from "@mui/material";
import {
Button,
Paper,
Box,
Typography,
TableHead,
TableRow,
TableCell,
TableBody,
Table,
Tooltip,
Alert,
Checkbox,
FormControlLabel,
} from "@mui/material";
import Input from "@kitUI/input";
import { useCartStore } from "@root/stores/cart";
import { useState } from "react";
@ -11,326 +25,341 @@ import { useTariffStore } from "@root/stores/tariffs";
import { AnyDiscount, CartItemTotal } from "@root/model/cart";
import { findPrivilegeById } from "@root/stores/privileges";
interface Props {
selectedTariffs: GridSelectionModel;
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 [loyaltyField, setLoyaltyField] = useState<string>("");
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [isNonCommercial, setIsNonCommercial] = useState<boolean>(false);
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 [loyaltyField, setLoyaltyField] = useState<string>("");
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [isNonCommercial, setIsNonCommercial] = useState<boolean>(false);
const cartRows = cartTotal?.items.map(cartItemTotal => {
const privilege = findPrivilegeById(cartItemTotal.tariff.privilegeId);
const cartRows = cartTotal?.items.map((cartItemTotal) => {
const privilege = findPrivilegeById(cartItemTotal.tariff.privilegeId);
const service = privilege?.serviceKey;
const serviceDiscount = service ? cartTotal.discountsByService[service] : null;
const service = privilege?.serviceKey;
const serviceDiscount = service ? cartTotal.discountsByService[service] : null;
const envolvedDiscountsElement = (
<Box>
{cartItemTotal.envolvedDiscounts.map((discount, index, arr) => (
<span key={discount._id}>
<DiscountTooltip
discount={discount}
cartItemTotal={cartItemTotal}
/>
{index < arr.length - (serviceDiscount ? 0 : 1) &&
<span>,&nbsp;</span>
}
</span>
))}
{serviceDiscount &&
<span>
<DiscountTooltip
discount={serviceDiscount}
cartItemTotal={cartItemTotal}
/>
</span>
}
</Box>
);
const envolvedDiscountsElement = (
<Box>
{cartItemTotal.envolvedDiscounts.map((discount, index, arr) => (
<span key={discount._id}>
<DiscountTooltip discount={discount} cartItemTotal={cartItemTotal} />
{index < arr.length - (serviceDiscount ? 0 : 1) && <span>,&nbsp;</span>}
</span>
))}
{serviceDiscount && (
<span>
<DiscountTooltip discount={serviceDiscount} cartItemTotal={cartItemTotal} />
</span>
)}
</Box>
);
const totalIncludingServiceDiscount = cartItemTotal.totalPrice * (serviceDiscount?.target.factor || 1);
const totalIncludingServiceDiscount = cartItemTotal.totalPrice * (serviceDiscount?.target.factor || 1);
return {
id: cartItemTotal.tariff.id,
tariffName: cartItemTotal.tariff.name,
privilegeDesc: privilege?.description ?? "Привилегия не найдена",
envolvedDiscounts: envolvedDiscountsElement,
price: totalIncludingServiceDiscount,
};
return {
id: cartItemTotal.tariff.id,
tariffName: cartItemTotal.tariff.name,
privilegeDesc: privilege?.description ?? "Привилегия не найдена",
envolvedDiscounts: envolvedDiscountsElement,
price: totalIncludingServiceDiscount,
};
});
const cartDiscounts = cartTotal?.envolvedCartDiscounts;
const cartDiscountsResultFactor =
cartDiscounts &&
cartDiscounts?.length > 1 &&
cartDiscounts.reduce((acc, discount) => acc * findDiscountFactor(discount), 1);
const envolvedCartDiscountsElement = cartDiscounts && (
<Box
sx={{
display: "inline-flex",
flexWrap: "wrap",
}}
>
{cartDiscounts?.map((discount, index, arr) => (
<span key={discount._id}>
<DiscountTooltip discount={discount} />
{index < arr.length - 1 && <span>,&nbsp;</span>}
</span>
))}
&nbsp;
{cartDiscountsResultFactor && `= ${formatDiscountFactor(cartDiscountsResultFactor)}`}
</Box>
);
function handleCalcCartClick() {
const cartTariffs = tariffs.filter((tariff) => selectedTariffs.includes(tariff.id));
const cartItems = cartTariffs.map((tariff) => createCartItem(tariff));
let loyaltyValue = parseInt(loyaltyField);
if (!isFinite(loyaltyValue)) loyaltyValue = 0;
const activeDiscounts = discounts.filter((discount) => !discount.disabled);
const cartData = calcCartData({
user: testUser,
purchasesAmount: loyaltyValue,
cartItems,
discounts: activeDiscounts,
isNonCommercial,
coupon: couponField,
});
const cartDiscounts = cartTotal?.envolvedCartDiscounts;
const cartDiscountsResultFactor = cartDiscounts && cartDiscounts?.length > 1 && cartDiscounts.reduce((acc, discount) => acc * findDiscountFactor(discount), 1);
const envolvedCartDiscountsElement = cartDiscounts && (
<Box sx={{
display: "inline-flex",
flexWrap: "wrap",
}}>
{cartDiscounts?.map((discount, index, arr) => (
<span key={discount._id}>
<DiscountTooltip
discount={discount}
/>
{index < arr.length - 1 &&
<span>,&nbsp;</span>
}
</span>
))}
&nbsp;
{cartDiscountsResultFactor && `= ${formatDiscountFactor(cartDiscountsResultFactor)}`}
</Box>
);
function handleCalcCartClick() {
const cartTariffs = tariffs.filter(tariff => selectedTariffs.includes(tariff.id));
const cartItems = cartTariffs.map(tariff => createCartItem(tariff));
let loyaltyValue = parseInt(loyaltyField);
if (!isFinite(loyaltyValue)) loyaltyValue = 0;
const activeDiscounts = discounts.filter(discount => !discount.disabled);
const cartData = calcCartData({
user: testUser,
purchasesAmount: loyaltyValue,
cartItems,
discounts: activeDiscounts,
isNonCommercial,
coupon: couponField,
});
if (cartData instanceof Error) {
setErrorMessage(cartData.message);
return setCartTotal(null);
}
setErrorMessage(null);
setCartTotal(cartData);
if (cartData instanceof Error) {
setErrorMessage(cartData.message);
return setCartTotal(null);
}
return (
setErrorMessage(null);
setCartTotal(cartData);
}
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",
}}
>
<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
component="section"
sx={{
border: "1px solid white",
display: "flex",
flexDirection: "column",
alignItems: "center",
width: "100%",
pb: "20px",
borderRadius: "4px",
}}
sx={{
border: "1px solid white",
padding: "3px",
display: "flex",
flexDirection: "column",
}}
>
<Typography variant="caption">
корзина
</Typography>
<Paper
variant="bar"
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: "20px",
}}
>
<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: "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>
{cartTotal?.items && cartTotal.items.length > 0 &&
<>
<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>
</>
}
{errorMessage !== null &&
<Alert variant="filled" severity="error" sx={{ mt: "20px" }}>
{errorMessage}
</Alert>
}
<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: "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>
{cartTotal?.items && cartTotal.items.length > 0 && (
<>
<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>
</>
)}
{errorMessage !== null && (
<Alert variant="filled" severity="error" sx={{ mt: "20px" }}>
{errorMessage}
</Alert>
)}
</Box>
);
}
function DiscountTooltip({ discount, cartItemTotal }: {
discount: AnyDiscount;
cartItemTotal?: CartItemTotal;
}) {
const discountText = formatDiscountFactor(findDiscountFactor(discount));
function DiscountTooltip({ discount, cartItemTotal }: { discount: AnyDiscount; cartItemTotal?: CartItemTotal }) {
const discountText = formatDiscountFactor(findDiscountFactor(discount));
return (
<Tooltip title={
<>
<Typography>Скидка: {discount?.name}</Typography>
<Typography>{discount?.description}</Typography>
</>
}>
<span>{discountText}</span>
</Tooltip>
);
}
return (
<Tooltip
title={
<>
<Typography>Скидка: {discount?.name}</Typography>
<Typography>{discount?.description}</Typography>
</>
}
>
<span>{discountText}</span>
</Tooltip>
);
}

@ -35,9 +35,9 @@ export default function CreateTariff() {
};
addTariffs([newTariff]);
}
console.log(mergedPrivileges);
console.log(newTariff);
}
return (
<Container

@ -1,23 +1,13 @@
import * as React from "react";
import { GridColDef, GridSelectionModel, GridToolbar } from "@mui/x-data-grid";
import DataGrid from "@kitUI/datagrid";
import { useTariffStore } from "@root/stores/tariffs";
import { deleteTariffs, useTariffStore } from "@root/stores/tariffs";
import { SERVICE_LIST } from "@root/model/tariff";
import { findPrivilegeById } from "@root/stores/privileges";
import { IconButton } from "@mui/material";
const columns: GridColDef[] = [
{ field: "id", headerName: "ID", width: 100 },
{ field: "name", headerName: "Название тарифа", width: 150 },
{ field: "serviceName", headerName: "Сервис", width: 150 }, //инфо из гитлаба.
{ field: "privilege", headerName: "Привелегия", width: 150 },
{ field: "amount", headerName: "Количество", width: 110 },
{ field: "type", headerName: "Единица", width: 100 },
{ field: "pricePerUnit", headerName: "Цена за ед.", width: 100 },
{ field: "isCustomPrice", headerName: "Кастомная цена", width: 130 },
{ field: "total", headerName: "Сумма", width: 130 },
];
import BackspaceIcon from "@mui/icons-material/Backspace";
interface Props {
handleSelectionChange: (selectionModel: GridSelectionModel) => void;
@ -26,6 +16,35 @@ interface Props {
export default function TariffsDG({ handleSelectionChange }: Props) {
const tariffs = useTariffStore((state) => state.tariffs);
const columns: GridColDef[] = [
{ field: "id", headerName: "ID", width: 100 },
{ field: "name", headerName: "Название тарифа", width: 150 },
{ field: "serviceName", headerName: "Сервис", width: 150 }, //инфо из гитлаба.
{ field: "privilege", headerName: "Привелегия", width: 150 },
{ field: "amount", headerName: "Количество", width: 110 },
{ field: "type", headerName: "Единица", width: 100 },
{ field: "pricePerUnit", headerName: "Цена за ед.", width: 100 },
{ field: "isCustomPrice", headerName: "Кастомная цена", width: 130 },
{ field: "total", headerName: "Сумма", width: 60 },
{
field: "delete",
headerName: "Удаление",
width: 60,
renderCell: ({ row }) => {
return (
<IconButton
onClick={() => {
console.log(row.id);
deleteTariffs(row.id);
}}
>
<BackspaceIcon />
</IconButton>
);
},
},
];
const gridData = tariffs.map((tariff) => ({
id: tariff.id,
name: tariff.name,
@ -36,7 +55,7 @@ export default function TariffsDG({ handleSelectionChange }: Props) {
findPrivilegeById(tariff.privilegeId)?.description ?? "Привилегия не найдена"
}`,
amount: tariff.amount,
type: findPrivilegeById(tariff.privilegeId)?.type === "count" ? "день" : "шт.",
type: console.log(tariff.id),
pricePerUnit: tariff.customPricePerUnit ?? findPrivilegeById(tariff.privilegeId)?.price,
isCustomPrice: tariff.customPricePerUnit === undefined ? "Нет" : "Да",
total: tariff.amount * (tariff.customPricePerUnit ?? findPrivilegeById(tariff.privilegeId)?.price ?? 0),
@ -47,6 +66,7 @@ export default function TariffsDG({ handleSelectionChange }: Props) {
checkboxSelection={true}
rows={gridData}
columns={columns}
getRowId={(row) => row.id}
components={{ Toolbar: GridToolbar }}
onSelectionModelChange={handleSelectionChange}
/>

@ -2,26 +2,25 @@ import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
import { CartTotal } from "@root/model/cart";
interface CartStore {
cartTotal: CartTotal | null;
setCartTotal: (newCartTotal: CartTotal | null) => void;
cartTotal: CartTotal | null;
setCartTotal: (newCartTotal: CartTotal | null) => void;
}
export const useCartStore = create<CartStore>()(
devtools(
// persist(
(set, get) => ({
cartTotal: null,
setCartTotal: newCartTotal => set({ cartTotal: newCartTotal })
}),
// {
// name: "cart",
// getStorage: () => localStorage,
// }
// ),
{
name: "Cart store"
}
)
);
devtools(
// persist(
(set, get) => ({
cartTotal: null,
setCartTotal: (newCartTotal) => set({ cartTotal: newCartTotal }),
}),
// {
// name: "cart",
// getStorage: () => localStorage,
// }
// ),
{
name: "Cart store",
}
)
);

@ -1,22 +1,27 @@
import { Tariff } from "@root/model/tariff";
import { create } from "zustand";
import { devtools } from "zustand/middleware";
import { persist } from "zustand/middleware";
import { exampleTariffs } from "./mocks/tariffs";
interface TariffStore {
tariffs: Tariff[];
tariffs: Tariff[];
}
export const useTariffStore = create<TariffStore>()(
devtools(
(set, get) => ({
tariffs: exampleTariffs,
}),
{
name: "Tariff store"
}
)
persist(
(set, get) => ({
tariffs: exampleTariffs,
}),
{
name: "Tariff store",
}
)
);
export const addTariffs = (newTariffs: Tariff[]) => useTariffStore.setState(state => ({ tariffs: [...state.tariffs, ...newTariffs] }));
export const addTariffs = (newTariffs: Tariff[]) =>
useTariffStore.setState((state) => ({ tariffs: [...state.tariffs, ...newTariffs] }));
export const deleteTariffs = (tariffId: string) =>
useTariffStore.setState((state) => ({
tariffs: state.tariffs.filter((tariff) => tariff.id !== tariffId),
}));