commit
9b69097e49
@ -82,7 +82,7 @@ export function createDiscountObject({
|
||||
case "privilege":
|
||||
discount.Layer = 1;
|
||||
discount.Condition.Product = privilegeId;
|
||||
discount.Condition.Term = discountMinValue;
|
||||
discount.Condition.Term = discountMinValue / 100;
|
||||
discount.Target.Products = [
|
||||
{
|
||||
Factor: discountFactor,
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import { SxProps, TextField, Theme, useTheme } from "@mui/material";
|
||||
import { HTMLInputTypeAttribute, ChangeEvent } from "react";
|
||||
import {InputBaseProps} from "@mui/material/InputBase";
|
||||
|
||||
|
||||
export function CustomTextField({ id, label, value, type, sx, onChange: setValue }: {
|
||||
export function CustomTextField({ id, label, value, name, onBlur,error, type, sx, onChange: setValue }: {
|
||||
id: string;
|
||||
label: string;
|
||||
value: number | string | null;
|
||||
name?: string;
|
||||
onBlur?: InputBaseProps['onBlur'];
|
||||
error?: boolean;
|
||||
type?: HTMLInputTypeAttribute;
|
||||
sx?: SxProps<Theme>;
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||
@ -18,6 +22,9 @@ export function CustomTextField({ id, label, value, type, sx, onChange: setValue
|
||||
id={id}
|
||||
label={label}
|
||||
variant="filled"
|
||||
name={name}
|
||||
onBlur={onBlur}
|
||||
error={error}
|
||||
color="secondary"
|
||||
type={type}
|
||||
sx={sx}
|
||||
|
||||
@ -222,4 +222,4 @@ const SigninForm = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SigninForm;
|
||||
export default SigninForm;
|
||||
@ -1,8 +1,9 @@
|
||||
import * as React from "react";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import {Box, Button, Typography} from "@mui/material";
|
||||
import { ThemeProvider } from "@mui/material";
|
||||
import theme from "../../theme";
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import {Link} from "react-router-dom";
|
||||
|
||||
|
||||
const Error404: React.FC = () => {
|
||||
@ -46,6 +47,13 @@ const Error404: React.FC = () => {
|
||||
4
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Link
|
||||
to="/users">
|
||||
<Button>На главную</Button>
|
||||
</Link>
|
||||
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</ThemeProvider>
|
||||
|
||||
@ -1,20 +1,18 @@
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Button,
|
||||
useTheme,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
RadioGroup,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
InputLabel,
|
||||
Box,
|
||||
Typography,
|
||||
Button,
|
||||
useTheme,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
RadioGroup,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
InputLabel, TextField,
|
||||
} 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 {
|
||||
resetPrivilegeArray,
|
||||
usePrivilegeStore,
|
||||
@ -24,76 +22,67 @@ import { enqueueSnackbar } from "notistack";
|
||||
import { DiscountType, discountTypes } from "@root/model/discount";
|
||||
import { createDiscount } from "@root/api/discounts";
|
||||
import usePrivileges from "@root/utils/hooks/usePrivileges";
|
||||
import { Formik, Field, Form, FormikHelpers } from "formik";
|
||||
|
||||
interface Values {
|
||||
discountNameField: string,
|
||||
discountDescriptionField: string,
|
||||
discountFactorField: string,
|
||||
serviceType: string,
|
||||
discountType: DiscountType,
|
||||
purchasesAmountField: string,
|
||||
cartPurchasesAmountField: string,
|
||||
discountMinValueField: string,
|
||||
privilegeIdField: string,
|
||||
}
|
||||
|
||||
export default function CreateDiscount() {
|
||||
const theme = useTheme();
|
||||
const privileges = usePrivilegeStore((state) => state.privileges);
|
||||
const [serviceType, setServiceType] = useState<ServiceType>("templategen");
|
||||
const [discountType, setDiscountType] =
|
||||
useState<DiscountType>("purchasesAmount");
|
||||
const [discountNameField, setDiscountNameField] = useState<string>("");
|
||||
const [discountDescriptionField, setDiscountDescriptionField] =
|
||||
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");
|
||||
|
||||
usePrivileges({ onNewPrivileges: resetPrivilegeArray });
|
||||
|
||||
const handleDiscountTypeChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
const initialValues: Values = {
|
||||
discountNameField: "",
|
||||
discountDescriptionField: "",
|
||||
discountFactorField: "",
|
||||
serviceType: "",
|
||||
discountType: "purchasesAmount",
|
||||
purchasesAmountField: "",
|
||||
cartPurchasesAmountField: "",
|
||||
discountMinValueField: "",
|
||||
privilegeIdField: "",
|
||||
}
|
||||
|
||||
const handleCreateDiscount = async(
|
||||
values: Values,
|
||||
formikHelpers: FormikHelpers<Values>
|
||||
) => {
|
||||
setDiscountType(event.target.value as DiscountType);
|
||||
};
|
||||
|
||||
const handleServiceTypeChange = (event: SelectChangeEvent) => {
|
||||
setServiceType(event.target.value as ServiceType);
|
||||
};
|
||||
|
||||
async function handleCreateDiscount() {
|
||||
const purchasesAmount = parseFloat(purchasesAmountField.replace(",", "."));
|
||||
console.log("работаю")
|
||||
const purchasesAmount = Number(parseFloat(values.purchasesAmountField.replace(",", "."))) * 100;
|
||||
|
||||
const discountFactor =
|
||||
(100 - parseFloat(discountFactorField.replace(",", "."))) / 100;
|
||||
const cartPurchasesAmount = parseFloat(
|
||||
cartPurchasesAmountField.replace(",", ".")
|
||||
(100 - parseFloat(values.discountFactorField.replace(",", "."))) / 100;
|
||||
const cartPurchasesAmount = Number(parseFloat(
|
||||
values.cartPurchasesAmountField.replace(",", ".")) * 100
|
||||
);
|
||||
const discountMinValue = parseFloat(
|
||||
discountMinValueField.replace(",", ".")
|
||||
const discountMinValue = Number(parseFloat(
|
||||
values.discountMinValueField.replace(",", ".")) * 100
|
||||
);
|
||||
|
||||
if (!isFinite(purchasesAmount))
|
||||
return enqueueSnackbar("Поле purchasesAmount не число");
|
||||
if (!isFinite(discountFactor))
|
||||
return enqueueSnackbar("Поле discountFactor не число");
|
||||
if (!isFinite(cartPurchasesAmount))
|
||||
return enqueueSnackbar("Поле cartPurchasesAmount не число");
|
||||
if (!isFinite(discountMinValue))
|
||||
return enqueueSnackbar("Поле discountMinValue не число");
|
||||
if (discountType === "privilege" && !privilegeIdField)
|
||||
return enqueueSnackbar("Привилегия не выбрана");
|
||||
if (!discountNameField) return enqueueSnackbar('Поле "Имя" пустое');
|
||||
if (!discountDescriptionField)
|
||||
return enqueueSnackbar('Поле "Описание" пустое');
|
||||
if (discountFactor < 0)
|
||||
return enqueueSnackbar("Процент скидки не может быть больше 100");
|
||||
|
||||
const [createdDiscountResponse, createdDiscountError] =
|
||||
await createDiscount({
|
||||
cartPurchasesAmount,
|
||||
discountFactor,
|
||||
discountMinValue,
|
||||
purchasesAmount,
|
||||
discountDescription: discountDescriptionField,
|
||||
discountName: discountNameField,
|
||||
discountDescription: values.discountDescriptionField,
|
||||
discountName: values.discountNameField,
|
||||
startDate: new Date().toISOString(),
|
||||
endDate: new Date(Date.now() + 1000 * 3600 * 24 * 30).toISOString(),
|
||||
serviceType,
|
||||
discountType,
|
||||
privilegeId: privilegeIdField,
|
||||
serviceType: values.serviceType,
|
||||
discountType: values.discountType,
|
||||
privilegeId: values.privilegeIdField,
|
||||
});
|
||||
|
||||
if (createdDiscountError) {
|
||||
@ -107,226 +96,423 @@ export default function CreateDiscount() {
|
||||
}
|
||||
}
|
||||
|
||||
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={discountDescriptionField}
|
||||
onChange={(e) => setDiscountDescriptionField(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",
|
||||
},
|
||||
}}
|
||||
const validateFulledFields = (values: Values) => {
|
||||
const errors = {} as any;
|
||||
if (values.discountNameField.length === 0) {
|
||||
errors.discountNameField = 'Поле "Имя" пустое'
|
||||
}
|
||||
if (values.discountDescriptionField.length === 0) {
|
||||
errors.discountDescriptionField = 'Поле "Описание" пустое'
|
||||
}
|
||||
if (((100 - parseFloat(values.discountFactorField.replace(",", "."))) / 100) < 0) {
|
||||
errors.discountFactorField = "Процент скидки не может быть больше 100"
|
||||
}
|
||||
if (!isFinite(((100 - parseFloat(values.discountFactorField.replace(",", "."))) / 100))) {
|
||||
errors.discountFactorField = 'Поле "Процент скидки" не число'
|
||||
}
|
||||
if (values.discountType === "privilege" && !values.privilegeIdField) {
|
||||
errors.privilegeIdField = "Привилегия не выбрана"
|
||||
}
|
||||
if (values.discountType === "service" && !values.serviceType) {
|
||||
errors.serviceType = "Сервис не выбран"
|
||||
}
|
||||
if (values.discountType === "purchasesAmount" && !isFinite(parseFloat(values.purchasesAmountField.replace(",", ".")))) {
|
||||
errors.purchasesAmountField = 'Поле "Внесено больше" не число'
|
||||
}
|
||||
if (values.discountType === "cartPurchasesAmount" && !isFinite(parseFloat(values.cartPurchasesAmountField.replace(",", ".")))) {
|
||||
errors.cartPurchasesAmountField = 'Поле "Объём в корзине" не число'
|
||||
}
|
||||
if (values.discountType === ("service" || "privilege") && !isFinite(parseFloat(values.discountMinValueField.replace(",", ".")))) {
|
||||
errors.discountMinValueField = 'Поле "Минимальное значение" не число'
|
||||
}
|
||||
console.log(errors)
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleCreateDiscount}
|
||||
validate={validateFulledFields}
|
||||
>
|
||||
Тип скидки
|
||||
</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={discountTypes[type as DiscountType]}
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
{discountType === "purchasesAmount" && (
|
||||
<CustomTextField
|
||||
id="discount-purchases"
|
||||
label="Внесено больше"
|
||||
type="number"
|
||||
sx={{
|
||||
marginTop: "15px",
|
||||
}}
|
||||
value={purchasesAmountField}
|
||||
onChange={(e) => setPurchasesAmountField(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
{discountType === "cartPurchasesAmount" && (
|
||||
<CustomTextField
|
||||
id="discount-cart-purchases"
|
||||
label="Объем в корзине"
|
||||
type="number"
|
||||
sx={{
|
||||
marginTop: "15px",
|
||||
}}
|
||||
value={cartPurchasesAmountField}
|
||||
onChange={(e) => setCartPurchasesAmountField(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
{discountType === "service" && (
|
||||
<>
|
||||
<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 === "privilege" && (
|
||||
<>
|
||||
<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>
|
||||
);
|
||||
{(props) => (
|
||||
<Form style={{width: "100%", display: "flex", justifyContent: "center"}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "left",
|
||||
alignItems: "left",
|
||||
marginTop: "15px",
|
||||
width: "100%",
|
||||
padding: "16px",
|
||||
maxWidth: "600px",
|
||||
gap: "1em",
|
||||
}}
|
||||
>
|
||||
<Field
|
||||
as={TextField}
|
||||
id="discount-name"
|
||||
label="Название"
|
||||
variant="filled"
|
||||
name="discountNameField"
|
||||
error={props.touched.discountNameField && !!props.errors.discountNameField}
|
||||
helperText={
|
||||
<Typography sx={{fontSize: "12px", width: "200px"}}>
|
||||
{props.errors.discountNameField}
|
||||
</Typography>
|
||||
}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Field
|
||||
as={TextField}
|
||||
id="discount-desc"
|
||||
label="Описание"
|
||||
variant="filled"
|
||||
name="discountDescriptionField"
|
||||
type="text"
|
||||
error={props.touched.discountDescriptionField && !!props.errors.discountDescriptionField}
|
||||
helperText={
|
||||
<Typography sx={{fontSize: "12px", width: "200px"}}>
|
||||
{props.errors.discountDescriptionField}
|
||||
</Typography>
|
||||
}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
width: "90%",
|
||||
fontWeight: "normal",
|
||||
color: theme.palette.grayDisabled.main,
|
||||
paddingLeft: "10px",
|
||||
}}
|
||||
>
|
||||
Условия:
|
||||
</Typography>
|
||||
<Field
|
||||
as={TextField}
|
||||
id="discount-factor"
|
||||
label="Процент скидки"
|
||||
variant="filled"
|
||||
name="discountFactorField"
|
||||
error={props.touched.discountFactorField && !!props.errors.discountFactorField}
|
||||
value={props.values.discountFactorField}
|
||||
helperText={
|
||||
<Typography sx={{fontSize: "12px", width: "200px"}}>
|
||||
{props.errors.discountFactorField}
|
||||
</Typography>
|
||||
}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<FormControl>
|
||||
<FormLabel
|
||||
id="discount-type"
|
||||
sx={{
|
||||
color: "white",
|
||||
"&.Mui-focused": {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Тип скидки
|
||||
</FormLabel>
|
||||
<RadioGroup
|
||||
row
|
||||
aria-labelledby="discount-type"
|
||||
name="discountType"
|
||||
value={props.values.discountType}
|
||||
onChange={(
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
props.setFieldValue("discountType", event.target.value as DiscountType);
|
||||
}}
|
||||
onBlur={props.handleBlur}
|
||||
>
|
||||
{Object.keys(discountTypes).map((type) => (
|
||||
<FormControlLabel
|
||||
key={type}
|
||||
value={type}
|
||||
control={<Radio color="secondary"/>}
|
||||
label={discountTypes[type as DiscountType]}
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
{props.values.discountType === "purchasesAmount" && (
|
||||
<TextField
|
||||
id="discount-purchases"
|
||||
name="purchasesAmountField"
|
||||
variant="filled"
|
||||
error={props.touched.purchasesAmountField && !!props.errors.purchasesAmountField}
|
||||
label="Внесено больше"
|
||||
onChange={(e) => {
|
||||
props.setFieldValue("purchasesAmountField", e.target.value.replace(/[^\d]/g, ''))
|
||||
}}
|
||||
value={props.values.purchasesAmountField}
|
||||
onBlur={props.handleBlur}
|
||||
sx={{
|
||||
marginTop: "15px",
|
||||
}}
|
||||
helperText={
|
||||
<Typography sx={{fontSize: "12px", width: "200px"}}>
|
||||
{props.errors.purchasesAmountField}
|
||||
</Typography>
|
||||
}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{props.values.discountType === "cartPurchasesAmount" && (
|
||||
<TextField
|
||||
id="discount-cart-purchases"
|
||||
label="Объем в корзине"
|
||||
name="cartPurchasesAmountField"
|
||||
variant="filled"
|
||||
error={props.touched.cartPurchasesAmountField && !!props.errors.cartPurchasesAmountField}
|
||||
onChange={(e) => {
|
||||
props.setFieldValue("cartPurchasesAmountField", e.target.value.replace(/[^\d]/g, ''))
|
||||
}}
|
||||
value={props.values.cartPurchasesAmountField}
|
||||
onBlur={props.handleBlur}
|
||||
sx={{
|
||||
marginTop: "15px",
|
||||
}}
|
||||
helperText={
|
||||
<Typography sx={{fontSize: "12px", width: "200px"}}>
|
||||
{props.errors.cartPurchasesAmountField}
|
||||
</Typography>
|
||||
}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{props.values.discountType === "service" && (
|
||||
<>
|
||||
<Select
|
||||
labelId="discount-service-label"
|
||||
id="discount-service"
|
||||
name="serviceType"
|
||||
onBlur={props.handleBlur}
|
||||
onChange={(e) => {
|
||||
props.setFieldValue("serviceType", e.target.value as ServiceType);
|
||||
}}
|
||||
error={props.touched.serviceType && !!props.errors.serviceType}
|
||||
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>
|
||||
<TextField
|
||||
id="discount-min-value"
|
||||
name="discountMinValueField"
|
||||
label="Минимальное значение"
|
||||
onBlur={props.handleBlur}
|
||||
variant="filled"
|
||||
onChange={(e) => {
|
||||
props.setFieldValue("discountMinValueField", e.target.value.replace(/[^\d]/g, ''))
|
||||
}}
|
||||
value={props.values.discountMinValueField}
|
||||
error={props.touched.discountMinValueField && !!props.errors.discountMinValueField}
|
||||
sx={{
|
||||
marginTop: "15px",
|
||||
}}
|
||||
helperText={
|
||||
<Typography sx={{fontSize: "12px", width: "200px"}}>
|
||||
{props.errors.discountMinValueField}
|
||||
</Typography>
|
||||
}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{props.values.discountType === "privilege" && (
|
||||
<>
|
||||
<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"
|
||||
name="privilegeIdField"
|
||||
onBlur={props.handleBlur}
|
||||
onChange={(e) => {
|
||||
props.setFieldValue("privilegeIdField", e.target.value);
|
||||
}}
|
||||
error={props.touched.privilegeIdField && !!props.errors.privilegeIdField}
|
||||
label="Привилегия"
|
||||
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>
|
||||
<TextField
|
||||
id="discount-min-value"
|
||||
name="discountMinValueField"
|
||||
label="Минимальное значение"
|
||||
onBlur={props.handleBlur}
|
||||
variant="filled"
|
||||
onChange={(e) => {
|
||||
props.setFieldValue("discountMinValueField", e.target.value.replace(/[^\d]/g, ''))
|
||||
}}
|
||||
value={props.values.discountMinValueField}
|
||||
error={props.touched.discountMinValueField && !!props.errors.discountMinValueField}
|
||||
sx={{
|
||||
marginTop: "15px",
|
||||
}}
|
||||
helperText={
|
||||
<Typography sx={{fontSize: "12px", width: "200px"}}>
|
||||
{props.errors.discountMinValueField}
|
||||
</Typography>
|
||||
}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
width: "90%",
|
||||
marginTop: "55px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
type='submit'
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
height: "52px",
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Создать
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Typography,
|
||||
Container,
|
||||
@ -8,11 +7,9 @@ import {
|
||||
FormControl,
|
||||
InputLabel,
|
||||
useTheme,
|
||||
Box,
|
||||
Box, TextField,
|
||||
} from "@mui/material";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { CustomTextField } from "@root/kitUI/CustomTextField";
|
||||
import { requestTariffs } from "@root/services/tariffs.service";
|
||||
import { createTariff } from "@root/api/tariffs";
|
||||
|
||||
@ -20,56 +17,67 @@ import {
|
||||
findPrivilegeById,
|
||||
usePrivilegeStore,
|
||||
} from "@root/stores/privilegesStore";
|
||||
import { PrivilegeWithAmount } from "@frontend/kitui";
|
||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
||||
import { Formik, Field, Form, FormikHelpers } from "formik";
|
||||
|
||||
interface Values {
|
||||
nameField: string,
|
||||
amountField: string,
|
||||
customPriceField: string,
|
||||
privilegeIdField: string,
|
||||
privilege: PrivilegeWithAmount | null
|
||||
}
|
||||
|
||||
export default function CreateTariff() {
|
||||
const theme = useTheme();
|
||||
|
||||
const privileges = usePrivilegeStore((store) => store.privileges);
|
||||
|
||||
const [nameField, setNameField] = useState<string>("");
|
||||
const [amountField, setAmountField] = useState<string>("");
|
||||
const [customPriceField, setCustomPriceField] = useState<string>("");
|
||||
const [privilegeIdField, setPrivilegeIdField] = useState<string>("");
|
||||
const checkFulledFields = (values: Values) => {
|
||||
const errors = {} as any;
|
||||
|
||||
const privilege = findPrivilegeById(privilegeIdField);
|
||||
if (values.nameField.length === 0) {
|
||||
errors.nameField = "Пустое название тарифа"
|
||||
}
|
||||
if (values.amountField.length === 0) {
|
||||
errors.amountField = "Пустое кол-во едениц привилегии"
|
||||
}
|
||||
if (values.privilegeIdField.length === 0) {
|
||||
errors.privilegeIdField = "Не выбрана привилегия"
|
||||
}
|
||||
console.log(errors)
|
||||
return errors;
|
||||
|
||||
const checkFulledFields = () => {
|
||||
if (nameField.length === 0) {
|
||||
enqueueSnackbar("Пустое название тарифа");
|
||||
return false;
|
||||
}
|
||||
if (amountField.length === 0) {
|
||||
enqueueSnackbar("Пустое кол-во едениц привилегии");
|
||||
return false;
|
||||
}
|
||||
if (privilegeIdField.length === 0) {
|
||||
enqueueSnackbar("Не выбрана привилегия");
|
||||
return false;
|
||||
}
|
||||
if (!privilege) {
|
||||
enqueueSnackbar("Привилегия с таким id не найдена");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const createTariffBackend = async () => {
|
||||
if (checkFulledFields() && privilege !== null) {
|
||||
const initialValues: Values = {
|
||||
nameField: "",
|
||||
amountField: "",
|
||||
customPriceField: "",
|
||||
privilegeIdField: "",
|
||||
privilege: null
|
||||
};
|
||||
|
||||
const createTariffBackend = async (
|
||||
values: Values,
|
||||
formikHelpers: FormikHelpers<Values>
|
||||
) => {
|
||||
if (values.privilege !== null) {
|
||||
const [_, createdTariffError] = await createTariff({
|
||||
name: nameField,
|
||||
price: Number(customPriceField) * 100,
|
||||
name: values.nameField,
|
||||
price: Number(values.customPriceField) * 100,
|
||||
isCustom: false,
|
||||
privileges: [
|
||||
{
|
||||
name: privilege.name,
|
||||
privilegeId: privilege.privilegeId ?? "",
|
||||
serviceKey: privilege.serviceKey,
|
||||
description: privilege.description,
|
||||
type: privilege.type,
|
||||
value: privilege.value ?? "",
|
||||
price: privilege.price,
|
||||
amount: Number(amountField),
|
||||
name: values.privilege.name,
|
||||
privilegeId: values.privilege.privilegeId ?? "",
|
||||
serviceKey: values.privilege.serviceKey,
|
||||
description: values.privilege.description,
|
||||
type: values.privilege.type,
|
||||
value: values.privilege.value ?? "",
|
||||
price: values.privilege.price,
|
||||
amount: Number(values.amountField),
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -80,9 +88,10 @@ export default function CreateTariff() {
|
||||
|
||||
enqueueSnackbar("Тариф создан");
|
||||
|
||||
requestTariffs();
|
||||
await requestTariffs();
|
||||
}
|
||||
};
|
||||
|
||||
// const createTariffFrontend = () => {
|
||||
// if (checkFulledFields() && privilege !== null) {
|
||||
// updateTariffStore({
|
||||
@ -97,122 +106,194 @@ export default function CreateTariff() {
|
||||
// }
|
||||
|
||||
return (
|
||||
<Container
|
||||
sx={{
|
||||
p: "20px",
|
||||
border: "1px solid rgba(224, 224, 224, 1)",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "12px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" sx={{ textAlign: "center", mb: "16px" }}>
|
||||
Создание тарифа
|
||||
</Typography>
|
||||
<FormControl
|
||||
fullWidth
|
||||
sx={{
|
||||
height: "52px",
|
||||
color: theme.palette.secondary.main,
|
||||
"& .MuiInputLabel-outlined": {
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
"& .MuiInputLabel-outlined.MuiInputLabel-shrink": {
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
}}
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validate={checkFulledFields}
|
||||
onSubmit={createTariffBackend}
|
||||
>
|
||||
<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,
|
||||
border: "1px solid",
|
||||
},
|
||||
".MuiSvgIcon-root ": {
|
||||
fill: theme.palette.secondary.main,
|
||||
},
|
||||
}}
|
||||
inputProps={{ sx: { pt: "12px" } }}
|
||||
>
|
||||
{privileges.map((privilege) => (
|
||||
<MenuItem
|
||||
data-cy={`select-option-${privilege.description}`}
|
||||
key={privilege.description}
|
||||
value={privilege._id}
|
||||
>
|
||||
{privilege.description}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{privilege && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography>
|
||||
Имя: <span>{privilege.name}</span>
|
||||
</Typography>
|
||||
<Typography>
|
||||
Сервис: <span>{privilege.serviceKey}</span>
|
||||
</Typography>
|
||||
<Typography>
|
||||
Единица: <span>{privilege.type}</span>
|
||||
</Typography>
|
||||
<Typography>
|
||||
Стандартная цена за единицу: <span>{currencyFormatter.format(privilege.price / 100)}</span>
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<CustomTextField
|
||||
id="tariff-name"
|
||||
label="Название тарифа"
|
||||
value={nameField}
|
||||
onChange={(e) => setNameField(e.target.value)}
|
||||
/>
|
||||
<CustomTextField
|
||||
id="tariff-amount"
|
||||
label="Кол-во единиц привилегии"
|
||||
value={amountField}
|
||||
onChange={(e) => setAmountField(e.target.value)}
|
||||
type="number"
|
||||
/>
|
||||
<CustomTextField
|
||||
id="tariff-custom-price"
|
||||
label="Кастомная цена (не обязательно)"
|
||||
value={customPriceField}
|
||||
onChange={(e) => setCustomPriceField(e.target.value)}
|
||||
type="number"
|
||||
/>
|
||||
<Button
|
||||
className="btn_createTariffBackend"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
createTariffBackend();
|
||||
}}
|
||||
>
|
||||
Создать
|
||||
</Button>
|
||||
</Container>
|
||||
{(props) => (
|
||||
<Form style={{width: "100%"}} >
|
||||
<Container
|
||||
sx={{
|
||||
p: "20px",
|
||||
border: "1px solid rgba(224, 224, 224, 1)",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "12px",
|
||||
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" sx={{ textAlign: "center", mb: "16px" }}>
|
||||
Создание тарифа
|
||||
</Typography>
|
||||
<FormControl
|
||||
fullWidth
|
||||
sx={{
|
||||
height: "52px",
|
||||
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={props.values.privilegeIdField}
|
||||
label="Привилегия"
|
||||
error={props.touched.privilegeIdField && !!props.errors.privilegeIdField}
|
||||
onChange={(e) => {
|
||||
props.setFieldValue("privilegeIdField", e.target.value)
|
||||
props.setFieldValue("privilege", findPrivilegeById(e.target.value))
|
||||
if (props.values.privilege === null)
|
||||
return enqueueSnackbar("Привилегия не найдена");
|
||||
}}
|
||||
onBlur={props.handleBlur}
|
||||
sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
},
|
||||
".MuiSvgIcon-root ": {
|
||||
fill: theme.palette.secondary.main,
|
||||
},
|
||||
}}
|
||||
inputProps={{ sx: { pt: "12px" } }}
|
||||
>
|
||||
{privileges.map((privilege) => (
|
||||
<MenuItem
|
||||
data-cy={`select-option-${privilege.description}`}
|
||||
key={privilege.description}
|
||||
value={privilege._id}
|
||||
>
|
||||
{privilege.description}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{props.values.privilege && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography>
|
||||
Имя: <span>{props.values.privilege.name}</span>
|
||||
</Typography>
|
||||
<Typography>
|
||||
Сервис: <span>{props.values.privilege.serviceKey}</span>
|
||||
</Typography>
|
||||
<Typography>
|
||||
Единица: <span>{props.values.privilege.type}</span>
|
||||
</Typography>
|
||||
<Typography>
|
||||
Стандартная цена за единицу: <span>{currencyFormatter.format(props.values.privilege.price / 100)}</span>
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<Field
|
||||
as={TextField}
|
||||
id="tariff-name"
|
||||
name="nameField"
|
||||
variant="filled"
|
||||
label="Название тарифа"
|
||||
type="text"
|
||||
error={props.touched.nameField && !!props.errors.nameField}
|
||||
helperText={
|
||||
<Typography sx={{ fontSize: "12px", width: "200px" }}>
|
||||
{props.errors.nameField}
|
||||
</Typography>
|
||||
}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
id="amountField"
|
||||
name="amountField"
|
||||
variant="filled"
|
||||
onChange={(e) => {
|
||||
props.setFieldValue("amountField", e.target.value.replace(/[^\d]/g,''))
|
||||
}}
|
||||
value={props.values.amountField}
|
||||
onBlur={props.handleBlur}
|
||||
label="Кол-во единиц привилегии"
|
||||
error={props.touched.amountField && !!props.errors.amountField}
|
||||
helperText={
|
||||
<Typography sx={{ fontSize: "12px", width: "200px" }}>
|
||||
{props.errors.amountField}
|
||||
</Typography>
|
||||
}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
id="tariff-custom-price"
|
||||
name="customPriceField"
|
||||
variant="filled"
|
||||
onChange={(e) => {
|
||||
props.setFieldValue("customPriceField", e.target.value.replace(/[^\d]/g,''))
|
||||
}}
|
||||
value={props.values.customPriceField}
|
||||
onBlur={props.handleBlur}
|
||||
label="Кастомная цена (не обязательно)"
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
className="btn_createTariffBackend"
|
||||
type="submit"
|
||||
disabled={props.isSubmitting}
|
||||
>
|
||||
Создать
|
||||
</Button>
|
||||
</Container>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@ -106,7 +106,6 @@ const links: { path: string; element: JSX.Element; title: string; className: str
|
||||
{ path: "/discounts", element: <AddPhotoAlternateOutlinedIcon />, title: "Скидки", className: "menu" },
|
||||
{ path: "/promocode", element: <NaturePeopleOutlinedIcon />, title: "Промокод", className: "menu" },
|
||||
{ path: "/settingRoles", element: <SettingsIcon />, title: "Настройки", className: "menu" },
|
||||
{ path: "/jjj", element: <CameraIcon />, title: "Камера", className: "menu" },
|
||||
{ path: "/support", element: <HeadsetMicOutlinedIcon />, title: "Служба поддержки", className: "menu" },
|
||||
];
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user