Merge branch 'dev' into 'staging'
Dev See merge request frontend/admin!64
This commit is contained in:
commit
5a31cb5fde
@ -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 },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user