change create coupon

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