diff --git a/src/api/discounts.ts b/src/api/discounts.ts index 803bc44..82ecf01 100644 --- a/src/api/discounts.ts +++ b/src/api/discounts.ts @@ -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, diff --git a/src/kitUI/CustomTextField.tsx b/src/kitUI/CustomTextField.tsx index 1d632fc..e373820 100644 --- a/src/kitUI/CustomTextField.tsx +++ b/src/kitUI/CustomTextField.tsx @@ -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; onChange: (e: ChangeEvent) => 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} diff --git a/src/pages/Authorization/signin.tsx b/src/pages/Authorization/signin.tsx index 8c0fa26..107ff22 100644 --- a/src/pages/Authorization/signin.tsx +++ b/src/pages/Authorization/signin.tsx @@ -222,4 +222,4 @@ const SigninForm = () => { ); }; -export default SigninForm; +export default SigninForm; \ No newline at end of file diff --git a/src/pages/Error404/index.tsx b/src/pages/Error404/index.tsx index bb28b21..3709b15 100644 --- a/src/pages/Error404/index.tsx +++ b/src/pages/Error404/index.tsx @@ -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 + + + + + + diff --git a/src/pages/dashboard/Content/DiscountManagement/CreateDiscount.tsx b/src/pages/dashboard/Content/DiscountManagement/CreateDiscount.tsx index 4c78723..525e7e4 100644 --- a/src/pages/dashboard/Content/DiscountManagement/CreateDiscount.tsx +++ b/src/pages/dashboard/Content/DiscountManagement/CreateDiscount.tsx @@ -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("templategen"); - const [discountType, setDiscountType] = - useState("purchasesAmount"); - const [discountNameField, setDiscountNameField] = useState(""); - const [discountDescriptionField, setDiscountDescriptionField] = - useState(""); - const [privilegeIdField, setPrivilegeIdField] = useState(""); - const [discountFactorField, setDiscountFactorField] = useState("0"); - const [purchasesAmountField, setPurchasesAmountField] = useState("0"); - const [cartPurchasesAmountField, setCartPurchasesAmountField] = - useState("0"); - const [discountMinValueField, setDiscountMinValueField] = - useState("0"); usePrivileges({ onNewPrivileges: resetPrivilegeArray }); - const handleDiscountTypeChange = ( - event: React.ChangeEvent + const initialValues: Values = { + discountNameField: "", + discountDescriptionField: "", + discountFactorField: "", + serviceType: "", + discountType: "purchasesAmount", + purchasesAmountField: "", + cartPurchasesAmountField: "", + discountMinValueField: "", + privilegeIdField: "", + } + + const handleCreateDiscount = async( + values: Values, + formikHelpers: FormikHelpers ) => { - 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 ( - - setDiscountNameField(e.target.value)} - /> - setDiscountDescriptionField(e.target.value)} - /> - - Условия: - - setDiscountFactorField(e.target.value)} - /> - - { + 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 ( + - Тип скидки - - - {Object.keys(discountTypes).map((type) => ( - } - label={discountTypes[type as DiscountType]} - /> - ))} - - - {discountType === "purchasesAmount" && ( - setPurchasesAmountField(e.target.value)} - /> - )} - {discountType === "cartPurchasesAmount" && ( - setCartPurchasesAmountField(e.target.value)} - /> - )} - {discountType === "service" && ( - <> - - setDiscountMinValueField(e.target.value)} - /> - - )} - {discountType === "privilege" && ( - <> - - - Привилегия - - - - setDiscountMinValueField(e.target.value)} - /> - - )} - - - - - ); + {(props) => ( +
+ + + {props.errors.discountNameField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + } + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main + } + }} + /> + + {props.errors.discountDescriptionField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + } + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main + } + }} + /> + + Условия: + + + {props.errors.discountFactorField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + } + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main + } + }} + /> + + + Тип скидки + + + ) => { + props.setFieldValue("discountType", event.target.value as DiscountType); + }} + onBlur={props.handleBlur} + > + {Object.keys(discountTypes).map((type) => ( + } + label={discountTypes[type as DiscountType]} + /> + ))} + + + {props.values.discountType === "purchasesAmount" && ( + { + props.setFieldValue("purchasesAmountField", e.target.value.replace(/[^\d]/g, '')) + }} + value={props.values.purchasesAmountField} + onBlur={props.handleBlur} + sx={{ + marginTop: "15px", + }} + helperText={ + + {props.errors.purchasesAmountField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + } + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main + } + }} + /> + )} + {props.values.discountType === "cartPurchasesAmount" && ( + { + props.setFieldValue("cartPurchasesAmountField", e.target.value.replace(/[^\d]/g, '')) + }} + value={props.values.cartPurchasesAmountField} + onBlur={props.handleBlur} + sx={{ + marginTop: "15px", + }} + helperText={ + + {props.errors.cartPurchasesAmountField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + } + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main + } + }} + /> + )} + {props.values.discountType === "service" && ( + <> + + { + props.setFieldValue("discountMinValueField", e.target.value.replace(/[^\d]/g, '')) + }} + value={props.values.discountMinValueField} + error={props.touched.discountMinValueField && !!props.errors.discountMinValueField} + sx={{ + marginTop: "15px", + }} + helperText={ + + {props.errors.discountMinValueField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + } + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main + } + }} + /> + + )} + {props.values.discountType === "privilege" && ( + <> + + + Привилегия + + + + { + props.setFieldValue("discountMinValueField", e.target.value.replace(/[^\d]/g, '')) + }} + value={props.values.discountMinValueField} + error={props.touched.discountMinValueField && !!props.errors.discountMinValueField} + sx={{ + marginTop: "15px", + }} + helperText={ + + {props.errors.discountMinValueField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + } + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main + } + }} + /> + + )} + + + + +
+ )} + + + ); } diff --git a/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx b/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx index 2250207..5f16b66 100644 --- a/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx +++ b/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx @@ -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(""); - const [amountField, setAmountField] = useState(""); - const [customPriceField, setCustomPriceField] = useState(""); - const [privilegeIdField, setPrivilegeIdField] = useState(""); + 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 + ) => { + 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 ( - - - Создание тарифа - - - - Привилегия - - - - {privilege && ( - - - Имя: {privilege.name} - - - Сервис: {privilege.serviceKey} - - - Единица: {privilege.type} - - - Стандартная цена за единицу: {currencyFormatter.format(privilege.price / 100)} - - - )} - setNameField(e.target.value)} - /> - setAmountField(e.target.value)} - type="number" - /> - setCustomPriceField(e.target.value)} - type="number" - /> - - + {(props) => ( +
+ + + Создание тарифа + + + + Привилегия + + + + {props.values.privilege && ( + + + Имя: {props.values.privilege.name} + + + Сервис: {props.values.privilege.serviceKey} + + + Единица: {props.values.privilege.type} + + + Стандартная цена за единицу: {currencyFormatter.format(props.values.privilege.price / 100)} + + + )} + + {props.errors.nameField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + } + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main + } + }} + /> + { + 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={ + + {props.errors.amountField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + } + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main + } + }} + /> + { + 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 + } + }} + /> + + +
+ )} + + ); } diff --git a/src/pages/dashboard/Menu/index.tsx b/src/pages/dashboard/Menu/index.tsx index e9c55db..aec86e4 100644 --- a/src/pages/dashboard/Menu/index.tsx +++ b/src/pages/dashboard/Menu/index.tsx @@ -106,7 +106,6 @@ const links: { path: string; element: JSX.Element; title: string; className: str { path: "/discounts", element: , title: "Скидки", className: "menu" }, { path: "/promocode", element: , title: "Промокод", className: "menu" }, { path: "/settingRoles", element: , title: "Настройки", className: "menu" }, - { path: "/jjj", element: , title: "Камера", className: "menu" }, { path: "/support", element: , title: "Служба поддержки", className: "menu" }, ];