diff --git a/src/components/PaginationUi.tsx b/src/components/PaginationUi.tsx index 71b7655..106e3f0 100644 --- a/src/components/PaginationUi.tsx +++ b/src/components/PaginationUi.tsx @@ -59,7 +59,7 @@ const PaginationUi: FC = ({ pager, onPageChange }) => { } return ( -
+
{getPageNumbers().map((page, index) => ( diff --git a/src/components/ProductMultiSelect.tsx b/src/components/ProductMultiSelect.tsx index 6f0820a..0c22f06 100644 --- a/src/components/ProductMultiSelect.tsx +++ b/src/components/ProductMultiSelect.tsx @@ -35,7 +35,7 @@ const ProductMultiSelect: React.FC = ({ }, [searchTerm]) // Search products (includes initial load and search) - const { data: searchResults, isLoading } = useSearchProducts(debouncedSearchTerm, dropdownOpen) + const { data: searchResults, isLoading } = useSearchProducts(debouncedSearchTerm, true) // Combine selected products with search results const allProducts = useMemo(() => { diff --git a/src/pages/Coupon/Update.tsx b/src/pages/Coupon/Update.tsx index 1a462ae..ef8b8bf 100644 --- a/src/pages/Coupon/Update.tsx +++ b/src/pages/Coupon/Update.tsx @@ -1,8 +1,303 @@ -import { type FC } from 'react' +import { useFormik } from 'formik' +import { type FC, useEffect, useRef } from 'react' +import { useParams } from 'react-router-dom' +import { type CreateCouponType } from './types/Types' +import * as Yup from 'yup' +import { useGetCouponDetail, useUpdateCoupon } from './hooks/useCouponData' +import { useNavigate } from 'react-router-dom' +import { useGetCategories, type CategoryTreeNode } from '../category' +import Input from '../../components/Input' +import DatePicker from '../../components/DatePicker' +import Switch from '../../components/Switch' +import Button from '../../components/Button' +import Textarea from '../../components/Textarea' +import MultiSelectSearchable from '../../components/MultiSelectSearchable' +import ProductMultiSelect from '../../components/ProductMultiSelect' +import { TickCircle } from 'iconsax-react' +import { toast } from 'react-toastify' +import { extractErrorMessage } from '@/helpers/utils' +import { Pages } from '../../config/Pages' const UpdateCoupon: FC = () => { + const navigate = useNavigate() + const { id } = useParams<{ id: string }>() + const { data } = useGetCouponDetail(id!) + const { data: categoriesData } = useGetCategories() + const updateCouponMutation = useUpdateCoupon() + const hasInitialized = useRef(false) + + const formik = useFormik({ + initialValues: { + couponId: id, + discountPercentage: 0, + discountAmount: 0, + usageLimit: 1, + userUsageLimit: 1, + minPurchaseAmount: 0, + category: [], + includedProducts: [], + excludedProducts: [], + description: '', + includeSpecialSale: false, + expirationDate: '', + }, + validationSchema: Yup.object({ + usageLimit: Yup.number().min(1, 'محدودیت استفاده باید حداقل ۱ باشد').required('محدودیت استفاده الزامی است'), + userUsageLimit: Yup.number().min(1, 'محدودیت استفاده کاربر باید حداقل ۱ باشد').required('محدودیت استفاده کاربر الزامی است'), + description: Yup.string().required('توضیحات کوپن الزامی است'), + expirationDate: Yup.string().required('تاریخ انقضا الزامی است'), + discountPercentage: Yup.number().optional(), + discountAmount: Yup.number().optional(), + minPurchaseAmount: Yup.number().min(0, 'حداقل مبلغ خرید نمی‌تواند منفی باشد').optional(), + includedProducts: Yup.array().optional(), + excludedProducts: Yup.array().optional(), + }).test('at-least-one-discount', 'درصد تخفیف یا مبلغ تخفیف الزامی است', function (value) { + const { discountPercentage, discountAmount } = value; + + // چک کردن اینکه حداقل یکی از دو فیلد پر شده باشد + if ((!discountPercentage || discountPercentage === 0) && (!discountAmount || discountAmount === 0)) { + return this.createError({ + path: 'discountPercentage', + message: 'درصد تخفیف یا مبلغ تخفیف الزامی است', + }); + } + + // چک کردن اینکه هر دو فیلد همزمان پر نشده باشند + if ((discountPercentage && discountPercentage > 0) && (discountAmount && discountAmount > 0)) { + return this.createError({ + path: 'discountPercentage', + message: 'نباید هر دو مقدار مبلغ تخفیف و درصد را وارد کنید', + }); + } + + if (discountPercentage && (discountPercentage < 1 || discountPercentage > 100)) { + return this.createError({ + path: 'discountPercentage', + message: 'درصد تخفیف باید بین ۱ تا ۱۰۰ باشد', + }); + } + + if (discountAmount && discountAmount < 1) { + return this.createError({ + path: 'discountAmount', + message: 'مبلغ تخفیف باید حداقل ۱ باشد', + }); + } + + return true; + }).test('products-validation', 'محصولات شامل و مستثنی نمی‌توانند همزمان انتخاب شوند', function (value) { + const { includedProducts, excludedProducts } = value; + + // چک کردن اینکه هر دو فیلد همزمان پر نشده باشند + if (includedProducts && includedProducts.length > 0 && excludedProducts && excludedProducts.length > 0) { + return this.createError({ + path: 'includedProducts', + message: 'نباید هر دو مقدار محصولات شامل و محصولات مستثنی را وارد کنید', + }); + } + + return true; + }), + onSubmit: async (values) => { + // تبدیل مقادیر به نوع مناسب + // اگر هر دو فیلد تخفیف پر شده باشند، فقط یکی را ارسال می‌کنیم + let discountPercentage: number | undefined + let discountAmount: number | undefined + + if (values.discountPercentage && values.discountPercentage > 0) { + discountPercentage = Number(values.discountPercentage) + discountAmount = undefined + } else if (values.discountAmount && values.discountAmount > 0) { + discountAmount = Number(values.discountAmount) + discountPercentage = undefined + } else { + discountPercentage = undefined + discountAmount = undefined + } + + const submitData = { + ...values, + usageLimit: Number(values.usageLimit), + userUsageLimit: Number(values.userUsageLimit), + discountPercentage, + discountAmount, + minPurchaseAmount: values.minPurchaseAmount ? Number(values.minPurchaseAmount) : undefined, + includedProducts: values.includedProducts?.length ? values.includedProducts : undefined, + excludedProducts: values.excludedProducts?.length ? values.excludedProducts : undefined, + } + + updateCouponMutation.mutate(submitData, { + onSuccess: () => { + toast.success('کوپن با موفقیت بروزرسانی شد') + navigate(Pages.coupon.list) + }, + onError: (error) => { + toast.error(extractErrorMessage(error)) + } + }) + } + }) + + // بروزرسانی initialValues وقتی داده‌ها از API دریافت شوند + useEffect(() => { + if (data?.results?.coupon && !hasInitialized.current) { + const coupon = data.results.coupon + formik.setValues({ + couponId: coupon._id, + discountPercentage: coupon.discountPercentage || 0, + discountAmount: coupon.discountAmount || 0, + usageLimit: coupon.usageLimit, + userUsageLimit: coupon.userUsageLimit, + minPurchaseAmount: coupon.minPurchaseAmount || 0, + category: coupon.category || [], + includedProducts: coupon.includedProducts || [], + excludedProducts: coupon.excludedProducts || [], + description: coupon.description, + includeSpecialSale: coupon.includeSpecialSale, + expirationDate: coupon.expirationDate, + }) + hasInitialized.current = true + } + }, [data, formik]) + + const categoryOptions = categoriesData?.results?.data?.map((category: CategoryTreeNode) => ({ + value: category._id, + label: category.title_fa + })) || [] + + if (!data) { + return
در حال بارگذاری...
+ } + return ( -
UpdateCoupon
+
+
+
+

ویرایش کوپن

+
+
+ +
+
+ +
+ {/* اطلاعات اصلی */} +
+

اطلاعات اصلی

+ +
+
+ + +
+ +
+ + +
+ + + + formik.setFieldValue('expirationDate', date)} + error_text={formik.touched.expirationDate && formik.errors.expirationDate ? formik.errors.expirationDate : ''} + /> + +