change create coupon
This commit is contained in:
@@ -23,7 +23,6 @@ const CreateCoupon: FC = () => {
|
|||||||
const { data: categoriesData } = useGetCategories()
|
const { data: categoriesData } = useGetCategories()
|
||||||
const { data: foodsData } = useGetFoods()
|
const { data: foodsData } = useGetFoods()
|
||||||
|
|
||||||
const [discountType, setDiscountType] = useState<'DIRECT' | 'CODE'>('CODE')
|
|
||||||
const [couponType, setCouponType] = useState<'PERCENTAGE' | 'FIXED'>('PERCENTAGE')
|
const [couponType, setCouponType] = useState<'PERCENTAGE' | 'FIXED'>('PERCENTAGE')
|
||||||
|
|
||||||
const categories = useMemo(() => categoriesData?.data || [], [categoriesData?.data])
|
const categories = useMemo(() => categoriesData?.data || [], [categoriesData?.data])
|
||||||
@@ -34,7 +33,6 @@ const CreateCoupon: FC = () => {
|
|||||||
code: '',
|
code: '',
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
type: 'PERCENTAGE',
|
|
||||||
value: 0,
|
value: 0,
|
||||||
maxDiscount: null,
|
maxDiscount: null,
|
||||||
minOrderAmount: 0,
|
minOrderAmount: 0,
|
||||||
@@ -43,14 +41,16 @@ const CreateCoupon: FC = () => {
|
|||||||
startDate: null,
|
startDate: null,
|
||||||
endDate: null,
|
endDate: null,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
categoryIds: [],
|
foodCategories: [],
|
||||||
foodIds: [],
|
foods: [],
|
||||||
|
userPhone: '',
|
||||||
|
type: 'PERCENTAGE',
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object().shape({
|
validationSchema: Yup.object().shape({
|
||||||
|
type: Yup.string().required('نوع کوپن الزامی است'),
|
||||||
code: Yup.string().required('کد کوپن الزامی است'),
|
code: Yup.string().required('کد کوپن الزامی است'),
|
||||||
name: Yup.string().required('نام کوپن الزامی است'),
|
name: Yup.string().required('نام کوپن الزامی است'),
|
||||||
description: Yup.string(),
|
description: Yup.string(),
|
||||||
type: Yup.string().required('نوع کوپن الزامی است'),
|
|
||||||
value: Yup.number().min(0, 'مقدار باید بیشتر از صفر باشد').required('مقدار کوپن الزامی است'),
|
value: Yup.number().min(0, 'مقدار باید بیشتر از صفر باشد').required('مقدار کوپن الزامی است'),
|
||||||
maxDiscount: Yup.number().nullable().min(0, 'حداکثر تخفیف باید بیشتر از صفر باشد'),
|
maxDiscount: Yup.number().nullable().min(0, 'حداکثر تخفیف باید بیشتر از صفر باشد'),
|
||||||
minOrderAmount: Yup.number().min(0, 'حداقل مبلغ سفارش باید بیشتر از صفر باشد').required('حداقل مبلغ سفارش الزامی است'),
|
minOrderAmount: Yup.number().min(0, 'حداقل مبلغ سفارش باید بیشتر از صفر باشد').required('حداقل مبلغ سفارش الزامی است'),
|
||||||
@@ -58,9 +58,14 @@ const CreateCoupon: FC = () => {
|
|||||||
maxUsesPerUser: Yup.number().nullable().min(0, 'حداکثر استفاده برای هر کاربر باید بیشتر از صفر باشد'),
|
maxUsesPerUser: Yup.number().nullable().min(0, 'حداکثر استفاده برای هر کاربر باید بیشتر از صفر باشد'),
|
||||||
startDate: Yup.string().nullable(),
|
startDate: Yup.string().nullable(),
|
||||||
endDate: Yup.string().nullable(),
|
endDate: Yup.string().nullable(),
|
||||||
|
userPhone: Yup.string().nullable(),
|
||||||
}),
|
}),
|
||||||
onSubmit: (values) => {
|
onSubmit: (values) => {
|
||||||
createCoupon(values, {
|
const submitValues = {
|
||||||
|
...values,
|
||||||
|
userPhone: values.userPhone?.trim() || undefined,
|
||||||
|
}
|
||||||
|
createCoupon(submitValues, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success('کوپن با موفقیت ایجاد شد')
|
toast.success('کوپن با موفقیت ایجاد شد')
|
||||||
navigate(Pages.coupons.list)
|
navigate(Pages.coupons.list)
|
||||||
@@ -98,8 +103,6 @@ const CreateCoupon: FC = () => {
|
|||||||
|
|
||||||
<CouponBasicInfo
|
<CouponBasicInfo
|
||||||
formik={formik}
|
formik={formik}
|
||||||
discountType={discountType}
|
|
||||||
onDiscountTypeChange={setDiscountType}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CouponDiscountSettings
|
<CouponDiscountSettings
|
||||||
|
|||||||
@@ -25,26 +25,26 @@ const CategorySelector: FC<CategorySelectorProps> = ({ formik, categories }) =>
|
|||||||
}, [categories, search])
|
}, [categories, search])
|
||||||
|
|
||||||
const handleToggle = (categoryId: string, checked: boolean) => {
|
const handleToggle = (categoryId: string, checked: boolean) => {
|
||||||
const currentIds = formik.values.categoryIds || []
|
const currentIds = formik.values.foodCategories || []
|
||||||
if (checked) {
|
if (checked) {
|
||||||
formik.setFieldValue('categoryIds', [...currentIds, categoryId])
|
formik.setFieldValue('foodCategories', [...currentIds, categoryId])
|
||||||
} else {
|
} else {
|
||||||
formik.setFieldValue('categoryIds', currentIds.filter((id) => id !== categoryId))
|
formik.setFieldValue('foodCategories', currentIds.filter((id) => id !== categoryId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSelectAll = (checked: boolean) => {
|
const handleSelectAll = (checked: boolean) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
formik.setFieldValue('categoryIds', filteredCategories.map((cat) => cat.id))
|
formik.setFieldValue('foodCategories', filteredCategories.map((cat) => cat.id))
|
||||||
} else {
|
} else {
|
||||||
formik.setFieldValue('categoryIds', [])
|
formik.setFieldValue('foodCategories', [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAllSelected = useMemo(() => {
|
const isAllSelected = useMemo(() => {
|
||||||
return filteredCategories.length > 0 &&
|
return filteredCategories.length > 0 &&
|
||||||
filteredCategories.every((cat) => formik.values.categoryIds?.includes(cat.id))
|
filteredCategories.every((cat) => formik.values.foodCategories?.includes(cat.id))
|
||||||
}, [filteredCategories, formik.values.categoryIds])
|
}, [filteredCategories, formik.values.foodCategories])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -68,7 +68,7 @@ const CategorySelector: FC<CategorySelectorProps> = ({ formik, categories }) =>
|
|||||||
{filteredCategories.map((category) => (
|
{filteredCategories.map((category) => (
|
||||||
<div key={category.id} className='flex items-center gap-2'>
|
<div key={category.id} className='flex items-center gap-2'>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={formik.values.categoryIds?.includes(category.id) || false}
|
checked={formik.values.foodCategories?.includes(category.id) || false}
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
handleToggle(category.id, checked as boolean)
|
handleToggle(category.id, checked as boolean)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { Refresh } from 'iconsax-react'
|
|||||||
import type { FormikProps } from 'formik'
|
import type { FormikProps } from 'formik'
|
||||||
import Input from '@/components/Input'
|
import Input from '@/components/Input'
|
||||||
import Textarea from '@/components/Textarea'
|
import Textarea from '@/components/Textarea'
|
||||||
import RadioGroup from '@/components/RadioGroup'
|
|
||||||
import Button from '@/components/Button'
|
import Button from '@/components/Button'
|
||||||
import type { CreateCouponType } from '../types/Types'
|
import type { CreateCouponType } from '../types/Types'
|
||||||
import { useGenerateCouponCode } from '../hooks/useCouponData'
|
import { useGenerateCouponCode } from '../hooks/useCouponData'
|
||||||
@@ -12,14 +11,10 @@ import { toast } from 'react-toastify'
|
|||||||
|
|
||||||
interface CouponBasicInfoProps {
|
interface CouponBasicInfoProps {
|
||||||
formik: FormikProps<CreateCouponType>
|
formik: FormikProps<CreateCouponType>
|
||||||
discountType: 'DIRECT' | 'CODE'
|
|
||||||
onDiscountTypeChange: (value: 'DIRECT' | 'CODE') => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const CouponBasicInfo: FC<CouponBasicInfoProps> = ({
|
const CouponBasicInfo: FC<CouponBasicInfoProps> = ({
|
||||||
formik,
|
formik,
|
||||||
discountType,
|
|
||||||
onDiscountTypeChange,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const { mutate: generateCouponCode, isPending: isGenerating } = useGenerateCouponCode()
|
const { mutate: generateCouponCode, isPending: isGenerating } = useGenerateCouponCode()
|
||||||
@@ -38,18 +33,6 @@ const CouponBasicInfo: FC<CouponBasicInfoProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='mb-6'>
|
|
||||||
<div className='text-sm mb-3'>نوع تخفیف</div>
|
|
||||||
<RadioGroup
|
|
||||||
items={[
|
|
||||||
{ label: 'تخفیف مستقیم', value: 'DIRECT' },
|
|
||||||
{ label: 'کد تخفیف', value: 'CODE' },
|
|
||||||
]}
|
|
||||||
selected={discountType}
|
|
||||||
onChange={(value) => onDiscountTypeChange(value as 'DIRECT' | 'CODE')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mb-6'>
|
<div className='mb-6'>
|
||||||
<div className='flex items-end gap-2'>
|
<div className='flex items-end gap-2'>
|
||||||
<div className='flex-1'>
|
<div className='flex-1'>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const CouponUsageSettings: FC<CouponUsageSettingsProps> = ({ formik }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='grid grid-cols-2 gap-4'>
|
<div className='grid grid-cols-2 gap-4 mb-6'>
|
||||||
<DatePickerComponent
|
<DatePickerComponent
|
||||||
label='تاریخ شروع'
|
label='تاریخ شروع'
|
||||||
placeholder='تاریخ شروع را انتخاب کنید'
|
placeholder='تاریخ شروع را انتخاب کنید'
|
||||||
@@ -60,6 +60,21 @@ const CouponUsageSettings: FC<CouponUsageSettingsProps> = ({ formik }) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='mb-6'>
|
||||||
|
<Input
|
||||||
|
label='شماره تلفن کاربر (اختیاری)'
|
||||||
|
placeholder='09123456789'
|
||||||
|
name='userPhone'
|
||||||
|
type='tel'
|
||||||
|
value={formik.values.userPhone || ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value.trim()
|
||||||
|
formik.setFieldValue('userPhone', value || undefined)
|
||||||
|
}}
|
||||||
|
error_text={formik.touched.userPhone && formik.errors.userPhone ? formik.errors.userPhone : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,26 +25,26 @@ const FoodSelector: FC<FoodSelectorProps> = ({ formik, foods }) => {
|
|||||||
}, [foods, search])
|
}, [foods, search])
|
||||||
|
|
||||||
const handleToggle = (foodId: string, checked: boolean) => {
|
const handleToggle = (foodId: string, checked: boolean) => {
|
||||||
const currentIds = formik.values.foodIds || []
|
const currentIds = formik.values.foods || []
|
||||||
if (checked) {
|
if (checked) {
|
||||||
formik.setFieldValue('foodIds', [...currentIds, foodId])
|
formik.setFieldValue('foods', [...currentIds, foodId])
|
||||||
} else {
|
} else {
|
||||||
formik.setFieldValue('foodIds', currentIds.filter((id) => id !== foodId))
|
formik.setFieldValue('foods', currentIds.filter((id) => id !== foodId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSelectAll = (checked: boolean) => {
|
const handleSelectAll = (checked: boolean) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
formik.setFieldValue('foodIds', filteredFoods.map((food) => food.id))
|
formik.setFieldValue('foods', filteredFoods.map((food) => food.id))
|
||||||
} else {
|
} else {
|
||||||
formik.setFieldValue('foodIds', [])
|
formik.setFieldValue('foods', [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAllSelected = useMemo(() => {
|
const isAllSelected = useMemo(() => {
|
||||||
return filteredFoods.length > 0 &&
|
return filteredFoods.length > 0 &&
|
||||||
filteredFoods.every((food) => formik.values.foodIds?.includes(food.id))
|
filteredFoods.every((food) => formik.values.foods?.includes(food.id))
|
||||||
}, [filteredFoods, formik.values.foodIds])
|
}, [filteredFoods, formik.values.foods])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -68,7 +68,7 @@ const FoodSelector: FC<FoodSelectorProps> = ({ formik, foods }) => {
|
|||||||
{filteredFoods.map((food) => (
|
{filteredFoods.map((food) => (
|
||||||
<div key={food.id} className='flex items-center gap-2'>
|
<div key={food.id} className='flex items-center gap-2'>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={formik.values.foodIds?.includes(food.id) || false}
|
checked={formik.values.foods?.includes(food.id) || false}
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
handleToggle(food.id, checked as boolean)
|
handleToggle(food.id, checked as boolean)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ export type CreateCouponType = {
|
|||||||
code: string;
|
code: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
type: CouponType;
|
|
||||||
value: number;
|
value: number;
|
||||||
maxDiscount: number | null;
|
maxDiscount: number | null;
|
||||||
minOrderAmount: number;
|
minOrderAmount: number;
|
||||||
@@ -39,6 +38,8 @@ export type CreateCouponType = {
|
|||||||
startDate: string | null;
|
startDate: string | null;
|
||||||
endDate: string | null;
|
endDate: string | null;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
categoryIds?: string[];
|
foodCategories?: string[];
|
||||||
foodIds?: string[];
|
foods?: string[];
|
||||||
|
userPhone?: string;
|
||||||
|
type: CouponType;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ const SideBar: FC = () => {
|
|||||||
icon={<TicketDiscount variant={isActive('discounts') ? 'Bold' : 'Outline'} color={isActive('discounts') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
|
icon={<TicketDiscount variant={isActive('discounts') ? 'Bold' : 'Outline'} color={isActive('discounts') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
|
||||||
title={t('sidebar.discount')}
|
title={t('sidebar.discount')}
|
||||||
isActive={isActive('discounts')}
|
isActive={isActive('discounts')}
|
||||||
link={Pages.discount.list}
|
link={Pages.coupons.list}
|
||||||
name='discounts'
|
name='discounts'
|
||||||
activeName='discounts'
|
activeName='discounts'
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user