Skip to content
Extraits de code Groupes Projets
Valider 788ab6eb rédigé par salaheddine zidani's avatar salaheddine zidani
Parcourir les fichiers

save

parent 55fc7ab3
Branches
1 requête de fusion!433MYD-792/Add promo code usage limit per client
......@@ -40,7 +40,8 @@ NEXT_PUBLIC_DEFAULT_IMAGE_URL=https://mydressin-rec.s3.eu-west-3.amazonaws.com/I
#GATEWAY API URL
NEXT_PUBLIC_MYDRESSIN_GATEWAY_API_URL=https://api-gateway.mydressin-server.com/
NEXT_PUBLIC_MYDRESSIN_GATEWAY_API_URL=http://localhost:8080
#https://api-gateway.mydressin-server.com/
NEXT_PUBLIC_MYDRESSIN_CLIENT_URL=https://app.mydressin-server.com/*
......
import { ICategory } from "@/shared/types/category";
import { IProductListItem, ProductType } from "@/shared/types/product";
import { ProductType } from "@/shared/types/product";
export type PromoCodeTableFilterValue = string | string[] | Date | null;
......@@ -19,6 +19,7 @@ export type PromoCodeItem = {
description: string;
usage: number;
usageLimit: number;
usageLimitPerClient: number;
minAmount: number;
startDate: Date;
expirationDate: Date;
......
......@@ -50,6 +50,11 @@ export async function checkPromoCodeExists(code: string) {
return response.data;
}
export async function checkPromoCodeByCodeAndUsageCountPerClientGreaterThanExists(code: string, newUsageLimitPerClient: number) {
const response = await axiosInstance.get(endpoints.Addons.PromoCode.checkPromoCodeByCodeAndUsageCountPerClientGreaterThanExists(code, newUsageLimitPerClient));
return response.data;
}
export async function addPromoCode(data: PromoCodeItem) {
const response = await axiosInstance.post(
endpoints.Addons.PromoCode.add,
......
......@@ -483,6 +483,7 @@ export const endpoints = {
getAll: "/api/addons/promoCode",
getById: (id: string) => `/api/addons/promoCode/${id}`,
checkPromoCodeExists: (code: string) => `/api/addons/promoCode/check-promoCode-exists/${code}`,
checkPromoCodeByCodeAndUsageCountPerClientGreaterThanExists: (code: string, newUsageLimitPerClient: number) => `/api/addons/promoCode/check-promoCode-exists/${code}/${newUsageLimitPerClient}`,
add: "/api/addons/promoCode/add-promoCode",
update: (id: string) => `/api/addons/promoCode/update-promoCode/${id}`,
delete: (id: string) => `/api/addons/promoCode/delete-promoCode/${id}`,
......
......@@ -296,14 +296,20 @@ function applyFilter({
inputData = stabilizedThis.map((el) => el[0]);
if (name) {
inputData = inputData.filter(
(user) =>
user.firstName?.toLowerCase().includes(name.toLowerCase()) ||
user.lastName?.toLowerCase().includes(name.toLowerCase()) ||
user.email?.toLowerCase().includes(name.toLowerCase()) ||
user.pseudo?.toLowerCase().includes(name.toLowerCase()) ||
user.uid?.toString().includes(name)
);
const cleanNameSearch = name.toLowerCase();
if (cleanNameSearch.includes('[') && cleanNameSearch.includes(']')) {
inputData = inputData.filter((user) => {
return `[${user.uid}]` === cleanNameSearch;
});
} else {
inputData = inputData.filter(
(user) =>
user.firstName?.toLowerCase().includes(cleanNameSearch) ||
user.lastName?.toLowerCase().includes(cleanNameSearch) ||
user.email?.toLowerCase().includes(cleanNameSearch) ||
user.pseudo?.toLowerCase().includes(cleanNameSearch)
);
}
}
if (status !== "all") {
......
......@@ -10,7 +10,7 @@ import { useRouter } from '@/hooks';
import { Autocomplete, Chip } from '@mui/material';
import Box from '@mui/material/Box';
import { useGetCategories } from '@/shared/api/categorie';
import { getPromoCodeById, addPromoCode, updatePromoCode, checkPromoCodeExists } from '@/shared/api/promoCode';
import { getPromoCodeById, addPromoCode, updatePromoCode, checkPromoCodeExists, checkPromoCodeByCodeAndUsageCountPerClientGreaterThanExists } from '@/shared/api/promoCode';
import { ProductPromoCode, PromoCodeType } from '@/contexts/types/promoCode';
import LoadingButton from '@mui/lab/LoadingButton';
import { useResponsive } from '@/hooks/use-responsive';
......@@ -52,7 +52,11 @@ export default function AddEditDuplicatePromoCodeView({ id, isEdit, isDuplicate
description: Yup.string().notRequired(),
usageLimit: Yup.number()
.moreThan(0, 'Limite d\'utilisation doit être supérieur à 0')
.moreThan(Yup.ref('usageLimitPerClient'), "Limite d'utilisation doit être supérieure à la limite d'utilisation par client")
.required('Limite d\'utilisation est requise'),
usageLimitPerClient: Yup.number()
.moreThan(0, 'Limite d\'utilisation par client doit être supérieur à 0')
.required('Limite d\'utilisation par client est requise'),
minAmount: Yup.number().optional(),
promoCodeType: Yup.string().required('Type de promo est requis'),
startDate: Yup.date()
......@@ -111,6 +115,7 @@ export default function AddEditDuplicatePromoCodeView({ id, isEdit, isDuplicate
valueCode: 0.00,
description: '',
usageLimit: 0,
usageLimitPerClient: 0,
minAmount: 0,
promoCodeType: PromoCodeType.DISCOUNT_PER_CART,
startDate: new Date(),
......@@ -122,7 +127,7 @@ export default function AddEditDuplicatePromoCodeView({ id, isEdit, isDuplicate
variationsExcludedIds: [],
categoriesIncludedIds: [],
categoriesExcludedIds: [],
referral:false,
referral: false,
}), []);
const methods = useForm({
......@@ -149,6 +154,7 @@ export default function AddEditDuplicatePromoCodeView({ id, isEdit, isDuplicate
setValue("promoCodeType", promoCodeData?.promoCodeType || PromoCodeType.DISCOUNT_PER_CART);
setValue("valueCode", promoCodeData?.valueCode || 0);
setValue("usageLimit", promoCodeData?.usageLimit || 0);
setValue("usageLimitPerClient", promoCodeData?.usageLimitPerClient || 0);
setValue("minAmount", promoCodeData?.minAmount || 0);
setValue("startDate", promoCodeData?.startDate ? new Date(promoCodeData.startDate) : new Date());
setValue("expirationDate", promoCodeData?.expirationDate ? new Date(promoCodeData.expirationDate) : new Date());
......@@ -291,6 +297,14 @@ export default function AddEditDuplicatePromoCodeView({ id, isEdit, isDuplicate
return;
}
if (isEdit) {
const checkUsagePerClient = await checkPromoCodeByCodeAndUsageCountPerClientGreaterThanExists(data.code, data.usageLimitPerClient);
if (checkUsagePerClient) {
enqueueSnackbar("L'utilisation de se code promo à déjà dépassé cette limite par client", { variant: "error" });
return;
}
}
data.startDate = new Date(new Date(data.startDate).getTime() - new Date(data.startDate).getTimezoneOffset() * 60000).toISOString();
data.expirationDate = new Date(new Date(data.expirationDate).getTime() - new Date(data.expirationDate).getTimezoneOffset() * 60000).toISOString();
if (isEdit && !isDuplicate) {
......@@ -402,7 +416,7 @@ export default function AddEditDuplicatePromoCodeView({ id, isEdit, isDuplicate
{ name: "Codes promo", href: paths.dashboard.admin.promoCode.root },
{ name: isEdit ? "Modifier un code promo" : isDuplicate ? "Dupliquer un code promo" : "Ajouter un code promo" },
]}
rollbackLink={paths.dashboard.admin.promoCode.root}
rollbackLink={paths.dashboard.admin.promoCode.root}
sx={{
mb: { xs: 3, md: 5 },
}}
......@@ -430,6 +444,11 @@ export default function AddEditDuplicatePromoCodeView({ id, isEdit, isDuplicate
fullWidth
error={!!errors.code}
helperText={errors.code?.message}
value={field.value || ""}
onChange={(e) => {
const lowercaseValue = e.target.value.toLowerCase();
field.onChange(lowercaseValue);
}}
/>
)}
/>
......@@ -463,6 +482,27 @@ export default function AddEditDuplicatePromoCodeView({ id, isEdit, isDuplicate
{renderValueCodeField()}
</Grid>
<Grid item xs={12} md={3}>
<Controller
name="usageLimitPerClient"
control={control}
render={({ field }) => (
<TextField
{...field}
id="outlined-usage-limit-per-client"
label="Limite d'utilisation par client"
type="number"
placeholder="0"
value={values.usageLimitPerClient}
variant="outlined"
name="usageLimitPerClient"
fullWidth
error={!!errors.usageLimitPerClient}
helperText={errors.usageLimitPerClient?.message}
/>
)}
/>
</Grid>
<Grid item xs={12} md={2}>
<Controller
name="usageLimit"
control={control}
......@@ -483,7 +523,7 @@ export default function AddEditDuplicatePromoCodeView({ id, isEdit, isDuplicate
)}
/>
</Grid>
<Grid item xs={12} md={3}>
<Grid item xs={12} md={2}>
<Controller
name="minAmount"
control={control}
......@@ -527,7 +567,7 @@ export default function AddEditDuplicatePromoCodeView({ id, isEdit, isDuplicate
)}
/>
</Grid>
<Grid item xs={12} md={5}>
<Grid item xs={12} md={4}>
<Controller
name="expirationDate"
control={control}
......
import React from 'react';
import { List, ListItem, ListItemText, Avatar, Card, CardHeader, CardContent } from '@mui/material';
import { List, ListItem, ListItemText, Avatar, Card, CardHeader, CardContent, Typography } from '@mui/material';
import { DEFAULT_IMAGE_URL } from '@/config-global';
import { ProductChoiceItem } from '../add-edit-duplicate/AddEditDuplicatePromoCodeView';
......@@ -20,7 +20,7 @@ const PromoCodeCardProductsList: React.FC<PromoCodeCardProductsListProps> = ({ t
<CardHeader title={title} />
<CardContent sx={{ maxHeight: 500, overflowY: 'auto' }}>
<List>
{products.map((product) => (
{products && products.map((product) => (
<ListItem
key={product.id}
sx={{
......
......@@ -157,6 +157,18 @@ export default function PromoCodeDetailsView({ promoCodeId }: Props) {
<Divider orientation={mdUp ? 'vertical' : 'horizontal'} flexItem style={{ borderStyle: 'dashed', margin: "16px 0" }} />
<Grid item lg={2} md={2} sm={6} container justifyContent={mdUp ? 'center' : 'start'} alignItems='center'>
<p>
<Typography
variant="h6"
sx={{ color: "text.disabled", flexGrow: 1 }}
>
Usage limite par client
</Typography>
<span>{promoCode?.usageLimitPerClient}</span>
</p>
</Grid>
<Grid item lg={2} md={2} sm={2} container justifyContent={mdUp ? 'center' : 'start'} alignItems='center'>
<p>
<Typography
variant="h6"
......@@ -170,7 +182,7 @@ export default function PromoCodeDetailsView({ promoCodeId }: Props) {
<Divider orientation="horizontal" flexItem style={{ borderStyle: 'dashed', margin: "16px 0" }} />
<Grid item lg={2} md={5} sm={6} container justifyContent={mdUp ? 'center' : 'start'} alignItems='center'>
<Grid item lg={2} md={5} sm={5} container justifyContent={mdUp ? 'center' : 'start'} alignItems='center'>
<p>
<Typography
variant="h6"
......@@ -184,7 +196,7 @@ export default function PromoCodeDetailsView({ promoCodeId }: Props) {
<Divider orientation={mdUp ? 'vertical' : 'horizontal'} flexItem style={{ borderStyle: 'dashed', margin: "16px 0" }} />
<Grid item lg={2} md={5} sm={6} container justifyContent={mdUp ? 'center' : 'start'} alignItems='center'>
<Grid item lg={2} md={5} sm={5} container justifyContent={mdUp ? 'center' : 'start'} alignItems='center'>
<p>
<Typography
variant="h6"
......@@ -219,7 +231,7 @@ export default function PromoCodeDetailsView({ promoCodeId }: Props) {
</p>
</Card>
)}
<Grid container spacing={2} sx={{ mt: 2 }} >
<Grid item xs={6} md={3}>
<PromoCodeCardProductsList title="Produits inclus" products={promoCode?.productsIncluded || []} />
......
......@@ -39,6 +39,7 @@ const TABLE_HEAD = [
{ id: "minAmount", label: "Montant minimum" },
{ id: "usage", label: "Usage" },
{ id: "usageLimit", label: "Usage limit" },
{ id: "usageLimitPerClient", label: "Usage limit par client" },
{ id: "expirationDate", label: "Date d'expiration" },
{ id: "promoCodeType", label: "Type" },
{ id: "" },
......
......@@ -15,7 +15,7 @@ import { ConfirmDialog } from "@/shared/components/custom-dialog";
import CustomPopover, { usePopover } from "@/shared/components/custom-popover";
import { useRouter } from "@/hooks";
import { paths } from '@/routes/paths';
import { getPromoCodeTypeColor, PromoCodeItem, promoCodeTypeLabels } from "@/contexts/types/promoCode";
import { getPromoCodeTypeColor, PromoCodeItem, PromoCodeType, promoCodeTypeLabels } from "@/contexts/types/promoCode";
type Props = {
row: PromoCodeItem;
......@@ -30,7 +30,7 @@ export default function PromoCodeTableRow({
onSelectRow,
onDeleteRow,
}: Props) {
const { id, code, valueCode, minAmount, usage, usageLimit, expirationDate, promoCodeType } = row;
const { id, code, valueCode, minAmount, usage, usageLimit, usageLimitPerClient, expirationDate, promoCodeType } = row;
const confirm = useBoolean();
const popover = usePopover();
......@@ -48,25 +48,31 @@ export default function PromoCodeTableRow({
router.push(paths.dashboard.admin.promoCode.duplicate(promoCodeId));
});
const valueSymbol = PromoCodeType.DISCOUNT_PER_CART === promoCodeType || PromoCodeType.DISCOUNT_PER_PRODUCT === promoCodeType ? ""
: PromoCodeType.PERCENTAGE_PER_CART === promoCodeType
? " %"
: "";
return (
<>
<TableRow hover selected={selected}>
<TableRow hover selected={selected} onClick={() => handleShowPromoCodeDetails(id!)}>
<TableCell padding="checkbox" onClick={(event) => event.stopPropagation()}>
<Checkbox checked={selected} onClick={onSelectRow} />
</TableCell>
<TableCell>{code}</TableCell>
<TableCell align="center">{code}</TableCell>
<TableCell>{valueCode}</TableCell>
<TableCell align="center">{valueCode + valueSymbol}</TableCell>
<TableCell>{fCurrency(minAmount)}</TableCell>
<TableCell align="center">{fCurrency(minAmount)}</TableCell>
<TableCell align="center">{usage}</TableCell>
<TableCell align="center">{usageLimit}</TableCell>
<TableCell>
<TableCell align="center">{usageLimitPerClient}</TableCell>
<TableCell align="center">
<ListItemText
primary={fDate(expirationDate)}
secondary={fTime(expirationDate)}
......@@ -79,7 +85,7 @@ export default function PromoCodeTableRow({
/>
</TableCell>
<TableCell>
<TableCell align="center">
<Label
variant="soft"
color={getPromoCodeTypeColor(promoCodeType)}
......@@ -111,10 +117,10 @@ export default function PromoCodeTableRow({
onClick={() => handleShowPromoCodeDetails(id!)}
>
<Iconify icon="solar:eye-bold" />
Voir
Visualiser
</MenuItem>
<MenuItem
<MenuItem
onClick={() => handleEditPromoCode(id!)}
>
<Iconify icon="solar:pen-bold" />
......@@ -124,7 +130,7 @@ export default function PromoCodeTableRow({
<MenuItem
onClick={() => handleDuplicatePromoCode(id!)}
>
<Iconify icon="solar:download-bold" />
<Iconify icon="solar:copy-bold" />
Dupliquer
</MenuItem>
<Divider sx={{ borderStyle: "dashed" }} />
......
0% ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter