Merge branch 'dev' into 'staging'

Dev

See merge request frontend/admin!64
This commit is contained in:
Nastya 2024-04-10 15:01:05 +00:00
commit 5a31cb5fde
2 changed files with 358 additions and 329 deletions

@ -8,6 +8,7 @@ export type CreatePromocodeBody = {
privilege: { privilege: {
privilegeID: string; privilegeID: string;
amount: number; amount: number;
serviceKey: string;
}; };
discount: { discount: {
layer: number; layer: number;

@ -1,12 +1,12 @@
import { import {
Button, Button,
FormControlLabel, FormControlLabel,
MenuItem, MenuItem,
Radio, Radio,
RadioGroup, RadioGroup,
Select, Select,
TextField, TextField,
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import { DesktopDatePicker } from "@mui/x-date-pickers/DesktopDatePicker"; import { DesktopDatePicker } from "@mui/x-date-pickers/DesktopDatePicker";
import { Field, Form, Formik } from "formik"; import { Field, Form, Formik } from "formik";
@ -25,345 +25,373 @@ import type { ChangeEvent } from "react";
type BonusType = "discount" | "privilege"; type BonusType = "discount" | "privilege";
type FormValues = { type FormValues = {
codeword: string; codeword: string;
description: string; description: string;
greetings: string; greetings: string;
dueTo: number; dueTo: number;
activationCount: number; activationCount: number;
privilegeId: string; privilegeId: string;
amount: number; amount: number;
layer: 1 | 2; layer: 1 | 2;
factor: number; factor: number;
target: string; target: string;
threshold: number; threshold: number;
serviceKey: string;
};
type SelectChangeProps = {
target: {
name: string;
value: string;
};
}; };
const initialValues: FormValues = { const initialValues: FormValues = {
codeword: "", codeword: "",
description: "", description: "",
greetings: "", greetings: "",
dueTo: 0, dueTo: 0,
activationCount: 0, activationCount: 0,
privilegeId: "", privilegeId: "",
amount: 0, amount: 0,
layer: 1, layer: 1,
factor: 0, factor: 0,
target: "", target: "",
threshold: 0, threshold: 0,
serviceKey: "",
}; };
type Props = { type Props = {
createPromocode: (body: CreatePromocodeBody) => Promise<void>; createPromocode: (body: CreatePromocodeBody) => Promise<void>;
}; };
export const CreatePromocodeForm = ({ createPromocode }: Props) => { export const CreatePromocodeForm = ({ createPromocode }: Props) => {
const [bonusType, setBonusType] = useState<BonusType>("discount"); const [bonusType, setBonusType] = useState<BonusType>("discount");
const { privileges } = usePrivilegeStore(); const { privileges } = usePrivilegeStore();
useEffect(() => { useEffect(() => {
requestPrivileges(); requestPrivileges();
}, []); }, []);
const submitForm = (values: FormValues) => { const submitForm = (values: FormValues) => {
const body = { const body = {
...values, ...values,
threshold: values.layer === 1 ? values.threshold : values.threshold * 100, threshold: values.layer === 1 ? values.threshold : values.threshold * 100,
};
const factorFromDiscountValue = 1 - body.factor / 100;
return createPromocode({
codeword: body.codeword,
description: body.description,
greetings: body.greetings,
dueTo: body.dueTo,
activationCount: body.activationCount,
bonus: {
privilege: { privilegeID: body.privilegeId, amount: body.amount },
discount: {
layer: body.layer,
factor: factorFromDiscountValue,
target: body.target,
threshold: body.threshold,
},
},
});
}; };
return ( const factorFromDiscountValue = 1 - body.factor / 100;
<Formik initialValues={initialValues} onSubmit={submitForm}>
{({ values, handleChange, handleBlur, setFieldValue }) => ( return createPromocode({
<Form codeword: body.codeword,
style={{ description: body.description,
width: "100%", greetings: body.greetings,
maxWidth: "600px", dueTo: body.dueTo,
padding: "0 10px", activationCount: body.activationCount,
}} bonus: {
> privilege: {
<CustomTextField privilegeID: body.privilegeId,
name="codeword" amount: body.amount,
label="Кодовое слово" serviceKey: body.serviceKey,
required },
onChange={handleChange} discount: {
/> layer: body.layer,
<CustomTextField factor: factorFromDiscountValue,
name="description" target: body.target,
label="Описание" threshold: body.threshold,
required },
onChange={handleChange} },
/> });
<CustomTextField };
name="greetings"
label="Приветственное сообщение" return (
required <Formik initialValues={initialValues} onSubmit={submitForm}>
onChange={handleChange} {({ values, handleChange, handleBlur, setFieldValue }) => (
/> <Form
<Typography style={{
variant="h4" width: "100%",
sx={{ maxWidth: "600px",
height: "40px", padding: "0 10px",
fontWeight: "normal", }}
marginTop: "15px", >
color: theme.palette.secondary.main, <CustomTextField
}} name="codeword"
> label="Кодовое слово"
Время существования промокода required
</Typography> onChange={handleChange}
<Field />
name="dueTo" <CustomTextField
as={DesktopDatePicker} name="description"
inputFormat="DD/MM/YYYY" label="Описание"
value={values.dueTo ? new Date(Number(values.dueTo) * 1000) : null} required
onChange={(event: any) => { onChange={handleChange}
setFieldValue("dueTo", event.$d.getTime() / 1000 || null); />
}} <CustomTextField
renderInput={(params: TextFieldProps) => <TextField {...params} />} name="greetings"
InputProps={{ label="Приветственное сообщение"
sx: { required
height: "40px", onChange={handleChange}
color: theme.palette.secondary.main, />
border: "1px solid", <Typography
borderColor: theme.palette.secondary.main, variant="h4"
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }, sx={{
}, height: "40px",
}} fontWeight: "normal",
/> marginTop: "15px",
<CustomTextField color: theme.palette.secondary.main,
name="activationCount" }}
label="Количество активаций промокода" >
onChange={({ target }) => Время существования промокода
setFieldValue( </Typography>
"activationCount", <Field
Number(target.value.replace(/\D/g, "")) name="dueTo"
) as={DesktopDatePicker}
} inputFormat="DD/MM/YYYY"
/> value={values.dueTo ? new Date(Number(values.dueTo) * 1000) : null}
<RadioGroup onChange={(event: any) => {
row setFieldValue("dueTo", event.$d.getTime() / 1000 || null);
name="bonusType" }}
value={bonusType} renderInput={(params: TextFieldProps) => <TextField {...params} />}
sx={{ marginTop: "15px" }} InputProps={{
onChange={({ target }: React.ChangeEvent<HTMLInputElement>) => { sx: {
setBonusType(target.value as BonusType); height: "40px",
}} color: theme.palette.secondary.main,
onBlur={handleBlur} border: "1px solid",
> borderColor: theme.palette.secondary.main,
<FormControlLabel "& .MuiSvgIcon-root": { color: theme.palette.secondary.main },
value="discount" },
control={<Radio color="secondary" />} }}
label="Скидка" />
/> <CustomTextField
<FormControlLabel name="activationCount"
value="privilege" label="Количество активаций промокода"
control={<Radio color="secondary" />} onChange={({ target }) =>
label="Привилегия" setFieldValue(
/> "activationCount",
</RadioGroup> Number(target.value.replace(/\D/g, ""))
{bonusType === "discount" && ( )
<> }
<RadioGroup />
row <RadioGroup
name="layer" row
value={values.layer} name="bonusType"
sx={{ marginTop: "15px" }} value={bonusType}
onChange={({ target }: React.ChangeEvent<HTMLInputElement>) => { sx={{ marginTop: "15px" }}
setFieldValue("target", ""); onChange={({ target }: React.ChangeEvent<HTMLInputElement>) => {
setFieldValue("layer", Number(target.value)); setBonusType(target.value as BonusType);
}} }}
onBlur={handleBlur} onBlur={handleBlur}
> >
<FormControlLabel <FormControlLabel
value="1" value="discount"
control={<Radio color="secondary" />} control={<Radio color="secondary" />}
label="Привилегия" label="Скидка"
/> />
<FormControlLabel <FormControlLabel
value="2" value="privilege"
control={<Radio color="secondary" />} control={<Radio color="secondary" />}
label="Сервис" label="Привилегия"
/> />
</RadioGroup> </RadioGroup>
<CustomTextField {bonusType === "discount" && (
name="factor" <>
label="Процент скидки" <RadioGroup
required row
onChange={({ target }) => { name="layer"
setFieldValue( value={values.layer}
"factor", sx={{ marginTop: "15px" }}
Number(target.value.replace(/\D/g, "")) onChange={({ target }: React.ChangeEvent<HTMLInputElement>) => {
); setFieldValue("target", "");
}} setFieldValue("layer", Number(target.value));
/> }}
<Typography onBlur={handleBlur}
variant="h4" >
sx={{ <FormControlLabel
height: "40px", value="1"
fontWeight: "normal", control={<Radio color="secondary" />}
marginTop: "15px", label="Привилегия"
padding: "0 12px", />
color: theme.palette.secondary.main, <FormControlLabel
}} value="2"
> control={<Radio color="secondary" />}
{values.layer === 1 ? "Выбор привилегии" : "Выбор сервиса"} label="Сервис"
</Typography> />
<Field </RadioGroup>
name="target" <CustomTextField
as={Select} name="factor"
label={values.layer === 1 ? "Привилегия" : "Сервис"} label="Процент скидки"
sx={{ required
width: "100%", onChange={({ target }) => {
border: "2px solid", setFieldValue(
color: theme.palette.secondary.main, "factor",
borderColor: theme.palette.secondary.main, Number(target.value.replace(/\D/g, ""))
"&.Mui-focused .MuiOutlinedInput-notchedOutline": { );
border: "none", }}
}, />
".MuiSvgIcon-root ": { fill: theme.palette.secondary.main }, <Typography
}} variant="h4"
children={ sx={{
values.layer === 1 height: "40px",
? privileges.map(({ name, privilegeId }) => ( fontWeight: "normal",
<MenuItem key={privilegeId} value={privilegeId}> marginTop: "15px",
{name} padding: "0 12px",
</MenuItem> color: theme.palette.secondary.main,
)) }}
: SERVICE_LIST.map(({ displayName, serviceKey }) => ( >
<MenuItem key={serviceKey} value={serviceKey}> {values.layer === 1 ? "Выбор привилегии" : "Выбор сервиса"}
{displayName} </Typography>
</MenuItem> <Field
)) name="target"
} as={Select}
/> label={values.layer === 1 ? "Привилегия" : "Сервис"}
<CustomTextField onChange={({ target }: SelectChangeProps) => {
name="threshold" if (values.layer === 1) {
label="При каком значении применяется скидка" const currentPrivilege = privileges.find(
onChange={({ target }) => (item) => item.privilegeId === target.value
setFieldValue( );
"threshold",
Number(target.value.replace(/\D/g, "")) setFieldValue("serviceKey", currentPrivilege?.serviceKey);
) setFieldValue("privilegeId", currentPrivilege?.privilegeId);
}
/> return;
</> }
)}
{bonusType === "privilege" && ( setFieldValue("privilegeId", target.value);
<> }}
<Typography sx={{
variant="h4" width: "100%",
sx={{ border: "2px solid",
height: "40px", color: theme.palette.secondary.main,
fontWeight: "normal", borderColor: theme.palette.secondary.main,
marginTop: "15px", "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
padding: "0 12px", border: "none",
color: theme.palette.secondary.main, },
}} ".MuiSvgIcon-root ": { fill: theme.palette.secondary.main },
> }}
Выбор привилегии value={values.privilegeId}
</Typography> children={
<Field values.layer === 1
name="privilegeId" ? privileges.map(({ name, privilegeId }) => (
as={Select} <MenuItem key={privilegeId} value={privilegeId}>
label="Привилегия" {name}
sx={{ </MenuItem>
width: "100%", ))
border: "2px solid", : SERVICE_LIST.map(({ displayName, serviceKey }) => (
color: theme.palette.secondary.main, <MenuItem key={serviceKey} value={serviceKey}>
borderColor: theme.palette.secondary.main, {displayName}
"&.Mui-focused .MuiOutlinedInput-notchedOutline": { </MenuItem>
border: "none", ))
}, }
".MuiSvgIcon-root ": { fill: theme.palette.secondary.main }, />
}} <CustomTextField
children={privileges.map(({ name, privilegeId }) => ( name="threshold"
<MenuItem key={privilegeId} value={privilegeId}> label="При каком значении применяется скидка"
{name} onChange={({ target }) =>
</MenuItem> setFieldValue(
))} "threshold",
/> Number(target.value.replace(/\D/g, ""))
<CustomTextField )
name="amount" }
label="Количество" />
required </>
onChange={({ target }) => )}
setFieldValue( {bonusType === "privilege" && (
"amount", <>
Number(target.value.replace(/\D/g, "")) <Typography
) variant="h4"
} sx={{
/> height: "40px",
</> fontWeight: "normal",
)} marginTop: "15px",
<Button padding: "0 12px",
variant="contained" color: theme.palette.secondary.main,
sx={{ }}
display: "block", >
padding: "10px", Выбор привилегии
margin: "15px auto 0", </Typography>
fontWeight: "normal", <Field
fontSize: "18px", name="privilegeId"
backgroundColor: theme.palette.menu.main, as={Select}
"&:hover": { backgroundColor: theme.palette.grayMedium.main }, label="Привилегия"
}} sx={{
type="submit" width: "100%",
> border: "2px solid",
Cоздать color: theme.palette.secondary.main,
</Button> borderColor: theme.palette.secondary.main,
</Form> "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
)} border: "none",
</Formik> },
); ".MuiSvgIcon-root ": { fill: theme.palette.secondary.main },
}}
children={privileges.map(({ name, privilegeId }) => (
<MenuItem key={privilegeId} value={privilegeId}>
{name}
</MenuItem>
))}
/>
<CustomTextField
name="amount"
label="Количество"
required
onChange={({ target }) =>
setFieldValue(
"amount",
Number(target.value.replace(/\D/g, ""))
)
}
/>
</>
)}
<Button
variant="contained"
sx={{
display: "block",
padding: "10px",
margin: "15px auto 0",
fontWeight: "normal",
fontSize: "18px",
backgroundColor: theme.palette.menu.main,
"&:hover": { backgroundColor: theme.palette.grayMedium.main },
}}
type="submit"
>
Cоздать
</Button>
</Form>
)}
</Formik>
);
}; };
type CustomTextFieldProps = { type CustomTextFieldProps = {
name: string; name: string;
label: string; label: string;
required?: boolean; required?: boolean;
onChange: (event: ChangeEvent<HTMLInputElement>) => void; onChange: (event: ChangeEvent<HTMLInputElement>) => void;
}; };
const CustomTextField = ({ const CustomTextField = ({
name, name,
label, label,
required = false, required = false,
onChange, onChange,
}: CustomTextFieldProps) => ( }: CustomTextFieldProps) => (
<Field <Field
name={name} name={name}
label={label} label={label}
required={required} required={required}
variant="filled" variant="filled"
color="secondary" color="secondary"
as={TextField} as={TextField}
onChange={onChange} onChange={onChange}
sx={{ width: "100%", marginTop: "15px" }} sx={{ width: "100%", marginTop: "15px" }}
InputProps={{ InputProps={{
style: { style: {
backgroundColor: theme.palette.content.main, backgroundColor: theme.palette.content.main,
color: theme.palette.secondary.main, color: theme.palette.secondary.main,
}, },
}} }}
InputLabelProps={{ InputLabelProps={{
style: { color: theme.palette.secondary.main }, style: { color: theme.palette.secondary.main },
}} }}
/> />
); );