Merge branch 'discounts-page' into 'dev'

fix discount creating page

See merge request frontend/admin!19
This commit is contained in:
Mikhail 2023-05-24 18:20:23 +00:00
commit 0470485b64
12 changed files with 659 additions and 575 deletions

@ -19,7 +19,7 @@ import Error404 from "@pages/Error404";
import Users from "@pages/dashboard/Content/Users";
import Entities from "@pages/dashboard/Content/Entities";
import Tariffs from "@pages/dashboard/Content/Tariffs";
import DiscountManagement from "@pages/dashboard/Content/DiscountManagement";
import DiscountManagement from "@root/pages/dashboard/Content/DiscountManagement/DiscountManagement";
import PromocodeManagement from "@pages/dashboard/Content/PromocodeManagement";
import { SettingRoles } from "@pages/Setting/SettingRoles";
import Support from "@pages/dashboard/Content/Support/Support";

@ -328,9 +328,6 @@ function DiscountTooltip({ discount, cartItemTotal }: {
<>
<Typography>Скидка: {discount?.name}</Typography>
<Typography>{discount?.description}</Typography>
<Typography>-------</Typography>
<Typography>layer: {discount?.layer}</Typography>
<Typography>id: {discount?._id}</Typography>
</>
}>
<span>{discountText}</span>

@ -1,13 +1,14 @@
import { TextField, useTheme } from "@mui/material";
import { SxProps, TextField, Theme, useTheme } from "@mui/material";
import { HTMLInputTypeAttribute, ChangeEvent } from "react";
export function CustomTextField({ id, label, value, type, setValue }: {
export function CustomTextField({ id, label, value, type, sx, onChange: setValue }: {
id: string;
label: string;
value: number | string | null;
type?: HTMLInputTypeAttribute;
setValue: (e: ChangeEvent<HTMLInputElement>) => void;
sx?: SxProps<Theme>;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
}) {
const theme = useTheme();
@ -19,6 +20,7 @@ export function CustomTextField({ id, label, value, type, setValue }: {
variant="filled"
color="secondary"
type={type}
sx={sx}
InputProps={{
style: {
backgroundColor: theme.palette.content.main,

@ -6,7 +6,7 @@ interface DiscountBase {
name: string;
description: string;
/** Этап применения скидки */
layer: number;
layer?: number;
disabled?: boolean;
}

@ -9,7 +9,7 @@ export const SERVICE_LIST = [
},
{
serviceKey: "dwarfener",
displayName: "Сокращатель ссылок",
displayName: "Аналитика сокращателя",
},
] as const;

@ -1,562 +0,0 @@
import { Box, Typography, TextField, Checkbox, Button } from "@mui/material";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { DesktopDatePicker } from "@mui/x-date-pickers/DesktopDatePicker";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableRow from "@mui/material/TableRow";
import TableContainer from "@mui/material/TableContainer";
import Paper from "@mui/material/Paper";
import { DataGrid, GridColDef, GridRowsProp, GridToolbar } from "@mui/x-data-grid";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import theme from "../../../theme";
import { styled } from "@mui/material/styles";
import { activateDiscounts, deactivateDiscounts, setSelectedDiscountIds, useDiscountStore } from "../../../stores/discounts";
import { useState } from "react";
import { DiscountConditionType } from "@root/model/cart";
import { ServiceType } from "@root/model/tariff";
import { findDiscountFactor, formatDiscountFactor } from "@root/kitUI/Cart/calc";
const BoxButton = styled('div')(({ theme }) => ({
[theme.breakpoints.down(400)]: {
justifyContent: 'center'
},
}));
const columns: GridColDef[] = [
{
field: "id",
headerName: "ID",
width: 30,
sortable: false,
},
{
field: "name",
headerName: "Название скидки",
width: 200,
sortable: false,
},
{
field: "description",
headerName: "Описание",
width: 120,
sortable: false,
},
{
field: "conditionType",
headerName: "Тип условия",
width: 120,
sortable: false,
},
{
field: "factor",
headerName: "Процент скидки",
width: 120,
sortable: false,
},
{
field: "active",
headerName: "Активна",
width: 120,
sortable: false,
},
];
const DiscountManagement: React.FC = () => {
const discounts = useDiscountStore(state => state.discounts);
const selectedDiscountIds = useDiscountStore(state => state.selectedDiscountIds);
const [isInfinite, setIsInfinite] = useState<boolean>(false);
const [serviceType, setServiceType] = useState<ServiceType>("templategen");
const [startDate, setStartDate] = useState<Date>(new Date());
const [endDate, setEndDate] = useState<Date>(new Date());
const [discountTypeField, setDiscountTypeField] = useState<string>("");
const [discountNameField, setDiscountNameField] = useState<string>("");
const [discountDescriptionField, setDiscountDescriptionField] = useState<string>("");
const [discountConditionType, setDiscountConditionType] = useState<DiscountConditionType | null>(null);
const [discountFactor, setDiscountFactor] = useState<number>(1);
const [purchasesAmountThreshold, setPurchasesAmountThreshold] = useState<number>(0);
const [cartPurchasesAmountThreshold, setCartPurchasesAmountThreshold] = useState<number>(0);
const [discountMinValue, setDiscountMinValue] = useState<number>(0);
const handleServiceTypeChange = (event: SelectChangeEvent) => {
setServiceType(event.target.value as ServiceType);
};
function createDiscount() {
// TODO
}
// const discountsArrayConverted = discounts.map((item) => {
// const basketMorePerc = Math.round(Number(item.basketMore) * 100) + "%";
// const toTimePerc = Math.round(Number(item.toTime) * 100) + "%";
// const toCapacityPerc = Math.round(Number(item.toCapacity) * 100) + "%";
// const dateFrom = item.from ? new Date(Number(item.from)) : "";
// const dateDueTo = item.from ? new Date(Number(item.dueTo)) : "";
// const strFrom = dateFrom
// ? `${dateFrom.getDate()}.${dateFrom.getMonth()}.${dateFrom.getFullYear()}`
// : "-";
// const strDueTo = dateDueTo
// ? `${dateDueTo.getDate()}.${dateDueTo.getMonth()}.${dateDueTo.getFullYear()}`
// : "-";
// if (item.privileges.length) {
// const result = item.privileges.reduce((acc, privilege) => {
// acc = acc
// ? `${acc}, ${privilege.good} - ${privilege.discount}%`
// : `${privilege.good} - ${Math.round(privilege.discount * 100)}%`;
// return acc;
// }, "");
// return {
// ...item, privileges: result, from: strFrom, dueTo: strDueTo,
// basketMore: basketMorePerc, toTime: toTimePerc, toCapacity: toCapacityPerc
// };
// } else {
// return {
// ...item, from: strFrom, dueTo: strDueTo,
// basketMore: basketMorePerc, toTime: toTimePerc, toCapacity: toCapacityPerc
// };
// }
// });
const discountsGridData: GridRowsProp = discounts.map(discount => {
return {
id: discount._id,
name: discount.name,
description: discount.description,
conditionType: discount.conditionType,
factor: formatDiscountFactor(findDiscountFactor(discount)),
active: discount.disabled ? "🚫" : "✅",
};
});
return (
<>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Typography
variant="subtitle1"
sx={{
width: "90%",
height: "60px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
color: theme.palette.secondary.main
}}>
СКИДКИ
</Typography>
<Box
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "left",
alignItems: "left",
marginTop: "15px",
}}
>
<TextField
id="standard-basic"
label={"Название"}
variant="filled"
color="secondary"
sx={{
height: "30px",
}}
InputProps={{
style: {
backgroundColor: theme.palette.content.main,
color: theme.palette.secondary.main,
}
}}
InputLabelProps={{
style: {
color: theme.palette.secondary.main
}
}}
value={discountNameField}
onChange={e => setDiscountNameField(e.target.value)}
/>
<Typography
variant="h4"
sx={{
width: "90%",
height: "40px",
fontWeight: "normal",
color: theme.palette.grayDisabled.main,
marginTop: "75px",
paddingLeft: '10px',
}}>
Условия:
</Typography>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={serviceType}
label="Age"
onChange={handleServiceTypeChange}
sx={{
color: theme.palette.secondary.main,
border: "1px solid",
borderColor: theme.palette.secondary.main,
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: theme.palette.secondary.main
},
".MuiSvgIcon-root ": {
fill: theme.palette.secondary.main,
}
}}
>
<MenuItem value={"templategen"}>Шаблонизатор</MenuItem>
<MenuItem value={"squiz"}>Опросник</MenuItem>
<MenuItem value={"dwarfener"}>Аналитика сокращателя</MenuItem>
</Select>
<TextField
id="standard-basic"
label={"Процент скидки"}
variant="filled"
color="secondary"
sx={{
marginTop: "15px"
}}
InputProps={{
style: {
backgroundColor: theme.palette.content.main,
color: theme.palette.secondary.main,
}
}}
InputLabelProps={{
style: {
color: theme.palette.secondary.main
}
}}
value={discountFactor}
onChange={e => setDiscountFactor(Number(e.target.value) || 1)}
/>
<TextField
id="standard-basic"
label={"Внесено больше"}
variant="filled"
color="secondary"
type="number"
sx={{
marginTop: "15px"
}}
InputProps={{
style: {
backgroundColor: theme.palette.content.main,
color: theme.palette.secondary.main,
}
}}
InputLabelProps={{
style: {
color: theme.palette.secondary.main
}
}}
value={purchasesAmountThreshold}
onChange={e => setPurchasesAmountThreshold(Number(e.target.value) || 0)}
/>
<TextField
id="standard-basic"
label={"Объем в корзине"}
variant="filled"
color="secondary"
type="number"
sx={{
marginTop: "15px"
}}
InputProps={{
style: {
backgroundColor: theme.palette.content.main,
color: theme.palette.secondary.main,
}
}}
InputLabelProps={{
style: {
color: theme.palette.secondary.main
}
}}
value={cartPurchasesAmountThreshold}
onChange={e => setCartPurchasesAmountThreshold(Number(e.target.value) || 0)}
/>
<TextField
id="standard-basic"
label={"Минимальное значение"}
variant="filled"
color="secondary"
type="number"
sx={{
marginTop: "15px"
}}
InputProps={{
style: {
backgroundColor: theme.palette.content.main,
color: theme.palette.secondary.main,
}
}}
InputLabelProps={{
style: {
color: theme.palette.secondary.main
}
}}
value={discountMinValue}
onChange={e => setDiscountMinValue(Number(e.target.value) || 0)}
/>
<TableContainer component={Paper} sx={{
width: "100%",
marginTop: "35px",
backgroundColor: theme.palette.content.main
}}>
<Table aria-label="simple table">
<TableBody>
<TableRow sx={{ border: "1px solid white" }} >
<TableCell component="th" scope="row" sx={{ color: theme.palette.secondary.main }}>
Работает, если заплатите 100500 денег
</TableCell>
</TableRow>
<TableRow sx={{ border: "1px solid white" }} >
<TableCell component="th" scope="row" sx={{ color: theme.palette.secondary.main }}>
Вы должны будете продать душу дьяволу
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
<Typography
variant="h4"
sx={{
width: "90%",
height: "40px",
fontWeight: "normal",
color: theme.palette.grayDisabled.main,
marginTop: "55px"
}}>
Дата действия:
</Typography>
<Box
sx={{
width: "100%",
display: "flex",
flexWrap: 'wrap'
}}
>
<Typography sx={{
width: "35px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "left",
}}>С</Typography>
<DesktopDatePicker
inputFormat="DD/MM/YYYY"
value={startDate}
onChange={(e) => { if (e) { setStartDate(e); } }}
renderInput={(params) => <TextField {...params} />}
InputProps={{
sx: {
height: "40px",
color: theme.palette.secondary.main,
border: "1px solid",
borderColor: theme.palette.secondary.main,
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
}
}}
/>
<Typography sx={{
width: "65px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}>по</Typography>
<DesktopDatePicker
inputFormat="DD/MM/YYYY"
value={endDate}
onChange={(e) => { if (e) { setEndDate(e); } }}
renderInput={(params) => <TextField {...params} />}
InputProps={{
sx: {
height: "40px",
color: theme.palette.secondary.main,
border: "1px solid",
borderColor: theme.palette.secondary.main,
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
}
}}
/>
</Box>
<Box sx={{
display: "flex",
width: "90%",
marginTop: theme.spacing(2),
}}>
<Box sx={{
width: "20px",
height: "42px",
display: "flex",
flexDirection: "column",
justifyContent: "left",
alignItems: "left",
marginRight: theme.spacing(1)
}}>
<Checkbox
sx={{
color: theme.palette.secondary.main,
"&.Mui-checked": {
color: theme.palette.secondary.main,
},
}}
checked={isInfinite}
onClick={() => setIsInfinite(p => !p)}
/>
</Box>
<Box sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}}>
Бессрочно
</Box>
</Box>
<Box sx={{
width: "90%",
marginTop: "55px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}}>
<Button
variant="contained"
sx={{
backgroundColor: theme.palette.menu.main,
height: "52px",
fontWeight: "normal",
fontSize: "17px",
"&:hover": {
backgroundColor: theme.palette.grayMedium.main
}
}}
onClick={createDiscount}
>Cоздать</Button>
</Box>
</Box>
<Box style={{ width: "80%", marginTop: "55px" }}>
<Box style={{ height: 600 }}>
<DataGrid
checkboxSelection={true}
rows={discountsGridData}
columns={columns}
selectionModel={selectedDiscountIds}
onSelectionModelChange={setSelectedDiscountIds}
sx={{
color: theme.palette.secondary.main,
"& .MuiDataGrid-iconSeparator": {
display: "none"
},
"& .css-levciy-MuiTablePagination-displayedRows": {
color: theme.palette.secondary.main
},
"& .MuiSvgIcon-root": {
color: theme.palette.secondary.main
},
"& .MuiTablePagination-selectLabel": {
color: theme.palette.secondary.main
},
"& .MuiInputBase-root": {
color: theme.palette.secondary.main
},
"& .MuiButton-text": {
color: theme.palette.secondary.main
},
}}
components={{ Toolbar: GridToolbar }}
/>
</Box>
</Box>
<Box sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
width: '100%',
marginTop: "45px"
}}>
<BoxButton sx={{
maxWidth: "420px",
width: '100%',
display: "flex",
justifyContent: "space-between",
flexWrap: 'wrap',
}}>
<Button
variant="contained"
onClick={deactivateDiscounts}
sx={{
backgroundColor: theme.palette.menu.main,
width: "200px",
height: "48px",
fontWeight: "normal",
fontSize: "17px",
marginBottom: '10px',
"&:hover": {
backgroundColor: theme.palette.grayMedium.main
}
}}>
Деактивировать
</Button>
<Button
variant="contained"
onClick={activateDiscounts}
sx={{
backgroundColor: theme.palette.menu.main,
width: "200px",
height: "48px",
fontWeight: "normal",
fontSize: "17px",
"&:hover": {
backgroundColor: theme.palette.grayMedium.main
}
}}>
Активировать
</Button>
</BoxButton>
</Box>
</LocalizationProvider>
</>
);
};
export default DiscountManagement;

@ -0,0 +1,335 @@
import { Box, Typography, Button, useTheme, FormControl, FormLabel, RadioGroup, FormControlLabel, Radio, InputLabel } from "@mui/material";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { useState } from "react";
import { SERVICE_LIST, ServiceType } from "@root/model/tariff";
import { CustomTextField } from "@root/kitUI/CustomTextField";
import { usePrivilegeStore } from "@root/stores/privileges";
import { AnyDiscount } from "@root/model/cart";
import { nanoid } from "nanoid";
import { addDiscounts } from "@root/stores/discounts";
import { enqueueSnackbar } from "notistack";
const discountTypes = {
"Лояльность": "purchasesAmount",
"Корзина": "cartPurchasesAmount",
"Сервис": "service",
"Товар": "privilege",
} as const;
type DiscountType = keyof typeof discountTypes;
export default function CreateDiscount() {
const theme = useTheme();
const privileges = usePrivilegeStore(state => state.privileges);
const [serviceType, setServiceType] = useState<ServiceType>("templategen");
const [discountType, setDiscountType] = useState<DiscountType>("Лояльность");
const [discountNameField, setDiscountNameField] = useState<string>("");
const [discountDescription, setDiscountDescription] = useState<string>("");
const [privilegeIdField, setPrivilegeIdField] = useState<string | "">("");
const [discountFactorField, setDiscountFactorField] = useState<string>("0");
const [purchasesAmountField, setPurchasesAmountField] = useState<string>("0");
const [cartPurchasesAmountField, setCartPurchasesAmountField] = useState<string>("0");
const [discountMinValueField, setDiscountMinValueField] = useState<string>("0");
const handleDiscountTypeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setDiscountType(event.target.value as DiscountType);
};
const handleServiceTypeChange = (event: SelectChangeEvent) => {
setServiceType(event.target.value as ServiceType);
};
function handleCreateDiscount() {
const purchasesAmount = parseFloat(purchasesAmountField.replace(",", "."));
const discountFactor = 1 - parseFloat(discountFactorField.replace(",", ".")) / 100;
const cartPurchasesAmount = parseFloat(cartPurchasesAmountField.replace(",", "."));
const discountMinValue = parseFloat(discountMinValueField.replace(",", "."));
if (!isFinite(purchasesAmount)) return enqueueSnackbar("Поле purchasesAmount не число");
if (!isFinite(discountFactor)) return enqueueSnackbar("Поле discountFactor не число");
if (!isFinite(cartPurchasesAmount)) return enqueueSnackbar("Поле cartPurchasesAmount не число");
if (!isFinite(discountMinValue)) return enqueueSnackbar("Поле discountMinValue не число");
let discount: AnyDiscount;
switch (discountType) {
case "Лояльность": {
discount = {
_id: nanoid(6),
name: discountNameField,
description: discountDescription,
disabled: false,
conditionType: "purchasesAmount",
condition: {
purchasesAmount: purchasesAmount,
},
factor: discountFactor,
};
break;
}
case "Корзина": {
discount = {
_id: nanoid(6),
name: discountNameField,
description: discountDescription,
disabled: false,
conditionType: "cartPurchasesAmount",
condition: {
cartPurchasesAmount: cartPurchasesAmount,
},
factor: discountFactor,
};
break;
}
case "Сервис": {
discount = {
_id: nanoid(6),
name: discountNameField,
description: discountDescription,
disabled: false,
conditionType: "service",
condition: {
service: {
id: serviceType,
value: discountMinValue,
},
},
target: {
service: serviceType,
factor: discountFactor,
},
};
break;
}
case "Товар": {
discount = {
_id: nanoid(6),
name: discountNameField,
description: discountDescription,
disabled: false,
conditionType: "privilege",
condition: {
privilege: {
id: privilegeIdField,
value: discountMinValue,
},
},
target: {
products: [{
privilegeId: privilegeIdField,
factor: discountFactor,
}],
},
};
break;
}
}
addDiscounts([discount]);
}
return (
<Box sx={{
display: "flex",
flexDirection: "column",
justifyContent: "left",
alignItems: "left",
marginTop: "15px",
width: "100%",
padding: "16px",
maxWidth: "600px",
gap: "1em",
}}>
<CustomTextField
id="discount-name"
label="Название"
value={discountNameField}
onChange={e => setDiscountNameField(e.target.value)}
/>
<CustomTextField
id="discount-desc"
label="Описание"
value={discountDescription}
onChange={e => setDiscountDescription(e.target.value)}
/>
<Typography
variant="h4"
sx={{
width: "90%",
fontWeight: "normal",
color: theme.palette.grayDisabled.main,
paddingLeft: "10px",
}}>
Условия:
</Typography>
<CustomTextField
id="discount-factor"
label="Процент скидки"
value={discountFactorField}
type="number"
onChange={e => setDiscountFactorField(e.target.value)}
/>
<FormControl>
<FormLabel
id="discount-type"
sx={{
color: "white",
"&.Mui-focused": {
color: "white",
},
}}
>Тип скидки</FormLabel>
<RadioGroup
row
aria-labelledby="discount-type"
name="discount-type"
value={discountType}
onChange={handleDiscountTypeChange}
>
{Object.keys(discountTypes).map(type =>
<FormControlLabel key={type} value={type} control={<Radio color="secondary" />} label={type} />
)}
</RadioGroup>
</FormControl>
{discountType === "Лояльность" &&
<CustomTextField
id="discount-purchases"
label="Внесено больше"
type="number"
sx={{
marginTop: "15px"
}}
value={purchasesAmountField}
onChange={e => setPurchasesAmountField(e.target.value)}
/>
}
{discountType === "Корзина" &&
<CustomTextField
id="discount-cart-purchases"
label="Объем в корзине"
type="number"
sx={{
marginTop: "15px"
}}
value={cartPurchasesAmountField}
onChange={e => setCartPurchasesAmountField(e.target.value)}
/>
}
{discountType === "Сервис" &&
<>
<Select
labelId="discount-service-label"
id="discount-service"
value={serviceType}
onChange={handleServiceTypeChange}
sx={{
color: theme.palette.secondary.main,
borderColor: theme.palette.secondary.main,
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: theme.palette.secondary.main
},
".MuiSvgIcon-root ": {
fill: theme.palette.secondary.main,
}
}}
>
{SERVICE_LIST.map(service =>
<MenuItem key={service.serviceKey} value={service.serviceKey}>{service.displayName}</MenuItem>
)}
</Select>
<CustomTextField
id="discount-min-value"
label="Минимальное значение"
type="number"
sx={{
marginTop: "15px"
}}
value={discountMinValueField}
onChange={e => setDiscountMinValueField(e.target.value)}
/>
</>
}
{discountType === "Товар" &&
<>
<FormControl
fullWidth
sx={{
color: theme.palette.secondary.main,
"& .MuiInputLabel-outlined": {
color: theme.palette.secondary.main,
},
"& .MuiInputLabel-outlined.MuiInputLabel-shrink": {
color: theme.palette.secondary.main,
}
}}
>
<InputLabel
id="privilege-select-label"
sx={{
color: theme.palette.secondary.main,
fontSize: "16px",
lineHeight: "19px",
}}
>Привелегия</InputLabel>
<Select
labelId="privilege-select-label"
id="privilege-select"
value={privilegeIdField}
label="Привелегия"
onChange={e => setPrivilegeIdField(e.target.value)}
sx={{
color: theme.palette.secondary.main,
borderColor: theme.palette.secondary.main,
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: theme.palette.secondary.main,
},
".MuiSvgIcon-root ": {
fill: theme.palette.secondary.main,
}
}}
inputProps={{ sx: { pt: "12px" } }}
>
{privileges.map((privilege, index) => (
<MenuItem key={index} value={privilege.privilegeId}>
{privilege.description}
</MenuItem>
))}
</Select>
</FormControl>
<CustomTextField
id="discount-min-value"
label="Минимальное значение"
type="number"
sx={{
marginTop: "15px"
}}
value={discountMinValueField}
onChange={e => setDiscountMinValueField(e.target.value)}
/>
</>
}
<Box sx={{
width: "90%",
marginTop: "55px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}}>
<Button
variant="contained"
sx={{
backgroundColor: theme.palette.menu.main,
height: "52px",
fontWeight: "normal",
fontSize: "17px",
"&:hover": {
backgroundColor: theme.palette.grayMedium.main
}
}}
onClick={handleCreateDiscount}
>Cоздать</Button>
</Box>
</Box >
);
}

@ -0,0 +1,112 @@
import { Box, Checkbox, TextField, Typography, useTheme } from "@mui/material";
import { DesktopDatePicker } from "@mui/x-date-pickers/DesktopDatePicker";
import { useState } from "react";
export default function DatePickers() {
const theme = useTheme();
const [isInfinite, setIsInfinite] = useState<boolean>(false);
const [startDate, setStartDate] = useState<Date>(new Date());
const [endDate, setEndDate] = useState<Date>(new Date());
return (
<>
<Typography
variant="h4"
sx={{
width: "90%",
height: "40px",
fontWeight: "normal",
color: theme.palette.grayDisabled.main,
marginTop: "55px"
}}>
Дата действия:
</Typography>
<Box sx={{
width: "100%",
display: "flex",
flexWrap: 'wrap'
}}>
<Typography sx={{
width: "35px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "left",
}}>С</Typography>
<DesktopDatePicker
inputFormat="DD/MM/YYYY"
value={startDate}
onChange={(e) => { if (e) { setStartDate(e); } }}
renderInput={(params) => <TextField {...params} />}
InputProps={{
sx: {
height: "40px",
color: theme.palette.secondary.main,
border: "1px solid",
borderColor: theme.palette.secondary.main,
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
}
}}
/>
<Typography sx={{
width: "65px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}>по</Typography>
<DesktopDatePicker
inputFormat="DD/MM/YYYY"
value={endDate}
onChange={(e) => { if (e) { setEndDate(e); } }}
renderInput={(params) => <TextField {...params} />}
InputProps={{
sx: {
height: "40px",
color: theme.palette.secondary.main,
border: "1px solid",
borderColor: theme.palette.secondary.main,
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
}
}}
/>
</Box>
<Box sx={{
display: "flex",
width: "90%",
marginTop: theme.spacing(2),
}}>
<Box sx={{
width: "20px",
height: "42px",
display: "flex",
flexDirection: "column",
justifyContent: "left",
alignItems: "left",
marginRight: theme.spacing(1)
}}>
<Checkbox
sx={{
color: theme.palette.secondary.main,
"&.Mui-checked": {
color: theme.palette.secondary.main,
},
}}
checked={isInfinite}
onClick={() => setIsInfinite(p => !p)}
/>
</Box>
<Box sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}}>
Бессрочно
</Box>
</Box>
</>
);
};

@ -0,0 +1,165 @@
import { Box, Button, styled, useTheme } from "@mui/material";
import { DataGrid, GridColDef, GridRowsProp, GridToolbar } from "@mui/x-data-grid";
import { findDiscountFactor, formatDiscountFactor } from "@root/kitUI/Cart/calc";
import { activateDiscounts, deactivateDiscounts, setSelectedDiscountIds, useDiscountStore } from "@root/stores/discounts";
const BoxButton = styled('div')(({ theme }) => ({
[theme.breakpoints.down(400)]: {
justifyContent: 'center'
},
}));
const columns: GridColDef[] = [
{
field: "id",
headerName: "ID",
width: 70,
sortable: false,
},
{
field: "name",
headerName: "Название скидки",
width: 150,
sortable: false,
},
{
field: "description",
headerName: "Описание",
width: 120,
sortable: false,
},
{
field: "conditionType",
headerName: "Тип условия",
width: 120,
sortable: false,
},
{
field: "factor",
headerName: "Процент скидки",
width: 120,
sortable: false,
},
{
field: "value",
headerName: "Значение",
width: 120,
sortable: false,
},
{
field: "active",
headerName: "Активна",
width: 120,
sortable: false,
},
];
export default function DiscountDataGrid() {
const theme = useTheme();
const discounts = useDiscountStore(state => state.discounts);
const selectedDiscountIds = useDiscountStore(state => state.selectedDiscountIds);
const discountsGridData: GridRowsProp = discounts.map(discount => {
const value =
(discount.condition as any).purchasesAmount ??
(discount.condition as any).cartPurchasesAmount ??
(discount.condition as any).service?.value ??
(discount.condition as any).privilege?.value ??
"-";
return {
id: discount._id,
name: discount.name,
description: discount.description,
conditionType: discount.conditionType,
factor: formatDiscountFactor(findDiscountFactor(discount)),
active: discount.disabled ? "🚫" : "✅",
value,
};
});
return (
<Box sx={{ width: "100%", marginTop: "55px", p: "16px", maxWidth: "1000px" }}>
<Box sx={{ height: 600 }}>
<DataGrid
checkboxSelection={true}
rows={discountsGridData}
columns={columns}
selectionModel={selectedDiscountIds}
onSelectionModelChange={setSelectedDiscountIds}
sx={{
color: theme.palette.secondary.main,
"& .MuiDataGrid-iconSeparator": {
display: "none"
},
"& .css-levciy-MuiTablePagination-displayedRows": {
color: theme.palette.secondary.main
},
"& .MuiSvgIcon-root": {
color: theme.palette.secondary.main
},
"& .MuiTablePagination-selectLabel": {
color: theme.palette.secondary.main
},
"& .MuiInputBase-root": {
color: theme.palette.secondary.main
},
"& .MuiButton-text": {
color: theme.palette.secondary.main
},
}}
components={{ Toolbar: GridToolbar }}
/>
</Box>
<Box sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
width: '100%',
marginTop: "45px"
}}>
<BoxButton sx={{
maxWidth: "420px",
width: '100%',
display: "flex",
justifyContent: "space-between",
flexWrap: 'wrap',
}}>
<Button
variant="contained"
onClick={deactivateDiscounts}
sx={{
backgroundColor: theme.palette.menu.main,
width: "200px",
height: "48px",
fontWeight: "normal",
fontSize: "17px",
marginBottom: '10px',
"&:hover": {
backgroundColor: theme.palette.grayMedium.main
}
}}>
Деактивировать
</Button>
<Button
variant="contained"
onClick={activateDiscounts}
sx={{
backgroundColor: theme.palette.menu.main,
width: "200px",
height: "48px",
fontWeight: "normal",
fontSize: "17px",
"&:hover": {
backgroundColor: theme.palette.grayMedium.main
}
}}>
Активировать
</Button>
</BoxButton>
</Box>
</Box>
);
}

@ -0,0 +1,35 @@
import { Typography, useTheme } from "@mui/material";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import DiscountDataGrid from "./DiscountDataGrid";
import CreateDiscount from "./CreateDiscount";
const DiscountManagement: React.FC = () => {
const theme = useTheme();
return (
<>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Typography
variant="subtitle1"
sx={{
width: "90%",
height: "60px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
color: theme.palette.secondary.main
}}>
СКИДКИ
</Typography>
<CreateDiscount />
<DiscountDataGrid />
</LocalizationProvider>
</>
);
};
export default DiscountManagement;

@ -106,20 +106,20 @@ export default function CreateTariff() {
id="tariff-name"
label="Название тарифа"
value={nameField}
setValue={e => setNameField(e.target.value)}
onChange={e => setNameField(e.target.value)}
/>
<CustomTextField
id="tariff-amount"
label="Кол-во единиц привилегии"
value={amountField}
setValue={e => setAmountField(e.target.value)}
onChange={e => setAmountField(e.target.value)}
type="number"
/>
<CustomTextField
id="tariff-custom-price"
label="Кастомная цена (не обязательно)"
value={customPriceField}
setValue={e => setCustomPriceField(e.target.value)}
onChange={e => setCustomPriceField(e.target.value)}
type="number"
/>
<Button

@ -30,7 +30,7 @@ export default function ChangePriceModal() {
id="privilege-custom-price"
label="Цена"
value={modalPriceField}
setValue={e => changeModalPriceField(e.target.value)}
onChange={e => changeModalPriceField(e.target.value)}
type="number"
/>
<Button