From a946d7f6e9ad400b9b6b969b0fdcc1781dcbddc7 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sun, 20 Apr 2025 12:13:38 +0330 Subject: [PATCH] crud discount admin --- src/config/Pages.ts | 1 + src/langs/fa.json | 5 +- src/pages/discounts/DiscountsList.tsx | 47 ++- src/pages/discounts/UpdateDiscount.tsx | 285 ++++++++++++++++++ src/pages/discounts/hooks/useDiscountData.ts | 20 ++ .../discounts/service/DiscountService.ts | 18 ++ src/pages/discounts/types/DiscountTypes.ts | 15 +- src/router/Main.tsx | 3 +- 8 files changed, 381 insertions(+), 13 deletions(-) create mode 100644 src/pages/discounts/UpdateDiscount.tsx diff --git a/src/config/Pages.ts b/src/config/Pages.ts index 568fd2c..5271b4b 100644 --- a/src/config/Pages.ts +++ b/src/config/Pages.ts @@ -58,6 +58,7 @@ export const Pages = { discount: { list: "/discounts/list", create: "/discounts/create", + detail: "/discounts/detail/", }, referralCode: { list: "/referral-code/list", diff --git a/src/langs/fa.json b/src/langs/fa.json index 0e64d86..cc25f3c 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -329,7 +329,10 @@ "DIRECT": "مستقیم", "FIXED": "ثابت", "plan": "پلن", - "user": "کاربر" + "user": "کاربر", + "0": "درصد", + "1": "مبلغ", + "update_discount": "ویرایش تخفیف" }, "blog": { "blog": "بلاگ", diff --git a/src/pages/discounts/DiscountsList.tsx b/src/pages/discounts/DiscountsList.tsx index 8841158..cd1260c 100644 --- a/src/pages/discounts/DiscountsList.tsx +++ b/src/pages/discounts/DiscountsList.tsx @@ -1,23 +1,38 @@ import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '../../components/Button' -import { Add } from 'iconsax-react' +import { Add, Edit, Trash } from 'iconsax-react' import Td from '../../components/Td' import { Link } from 'react-router-dom' import { Pages } from '../../config/Pages' import Input from '../../components/Input' -import { useGetDiscounts } from './hooks/useDiscountData' +import { useGetDiscounts, useDeleteDiscount } from './hooks/useDiscountData' import { DiscountItemType } from './types/DiscountTypes' import moment from 'moment-jalaali' import ToggleStatusDiscount from './components/ToggleStatusDiscount' import PageLoading from '../../components/PageLoading' +import { NumberFormat } from '../../config/func' +import { toast } from 'react-toastify' +import { ErrorType } from '../../helpers/types' const DiscountList: FC = () => { const { t } = useTranslation('global') const [search, setSearch] = useState('') const getDiscounts = useGetDiscounts(search) + const deleteDiscount = useDeleteDiscount() + const handleDelete = (id: string) => { + deleteDiscount.mutate(id, { + onSuccess: () => { + getDiscounts.refetch() + toast.success(t('success')) + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error.message[0]) + } + }) + } return (
@@ -68,36 +83,48 @@ const DiscountList: FC = () => { - { - getDiscounts.data?.data?.discounts?.map((item: DiscountItemType, index: number) => { + getDiscounts.data?.data?.discounts?.map((item: DiscountItemType) => { return ( - + ) }) diff --git a/src/pages/discounts/UpdateDiscount.tsx b/src/pages/discounts/UpdateDiscount.tsx new file mode 100644 index 0000000..473e37e --- /dev/null +++ b/src/pages/discounts/UpdateDiscount.tsx @@ -0,0 +1,285 @@ +import { FC, Fragment, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Input from '../../components/Input' +import Button from '../../components/Button' +import SwitchComponent from '../../components/Switch' +import { TickCircle } from 'iconsax-react' +import DatePickerComponent from '../../components/DatePicker' +import RadioGroup from '../../components/RadioGroup' +import { useFormik } from 'formik' +import { CreateDiscountType, SubscriptionPlanType } from './types/DiscountTypes' +import * as Yup from 'yup' +import { useGetAllServices } from '../service/hooks/useServiceData' +import { ServiceItemType } from '../service/types/ServiceTypes' +import CheckBoxComponent from '../../components/CheckBoxComponent' +import { toast } from 'react-toastify' +import { useGetDiscountDetail, useUpdateDiscount } from './hooks/useDiscountData' +import { useNavigate, useParams } from 'react-router-dom' +import { Pages } from '../../config/Pages' +import { ErrorType } from '../../helpers/types' +import moment from 'moment-jalaali' + +const UpdateDiscount: FC = () => { + + const { id } = useParams() + const navigate = useNavigate() + const { t } = useTranslation('global') + const [serviceId, setServiceId] = useState([]) + // const [planId, setPlanId] = useState('') + // const [customerId, setCustomerId] = useState('') + const getServices = useGetAllServices('', 1, '', '', undefined, 50) + // const getPlans = useGetPlans(serviceId) + // const getCustomers = useGetCustomers() + const getDiscountDetail = useGetDiscountDetail(id || '') + const updateDiscount = useUpdateDiscount() + + const formik = useFormik({ + initialValues: { + endDate: '', + startDate: '', + title: '', + type: 1, + targetServices: [], + direct: true, + value: 0, + isActive: true + }, + validationSchema: Yup.object({ + value: Yup.number().required(t('errors.required')), + endDate: Yup.string().required(t('errors.required')), + startDate: Yup.string().required(t('errors.required')), + title: Yup.string().required(t('errors.required')), + type: Yup.string().required(t('errors.required')), + }), + onSubmit(values) { + values.targetServices = serviceId + const params: CreateDiscountType = { + ...values, + startDate: moment(values.startDate, 'jYYYY-jMM-jDD').format('YYYY-MM-DD'), + endDate: moment(values.endDate, 'jYYYY-jMM-jDD').format('YYYY-MM-DD'), + value: +values.value + } + updateDiscount.mutate({ id: id || '', params }, { + onSuccess: () => { + toast.success(t('success')) + navigate(Pages.discount.list) + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error?.message[0]) + } + }) + }, + }) + + useEffect(() => { + if (getDiscountDetail.data?.data?.discount) { + formik.setValues({ + ...getDiscountDetail.data?.data?.discount, + startDate: moment(getDiscountDetail.data?.data?.discount.startDate, 'YYYY-MM-DD').format('jYYYY-jMM-jDD'), + endDate: moment(getDiscountDetail.data?.data?.discount.endDate, 'YYYY-MM-DD').format('jYYYY-jMM-jDD'), + direct: getDiscountDetail.data?.data?.discount.applicationType === 'DIRECT', + }) + + // delete getDiscountDetail.data?.data?.discount.subscriptionPlans + + const ids = getDiscountDetail.data?.data?.discount.subscriptionPlans.map((item: SubscriptionPlanType) => item?.service?.id) + // Create an array of unique service IDs by removing duplicates + const uniqueIds = [...new Set(ids)]; + setServiceId(uniqueIds as string[]); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [getDiscountDetail.data]) + + return ( +
+
+
+ {t('discount.update_discount')} +
+ +
+
+
+
+ {t('discount.type')} +
+ formik.setFieldValue('direct', value)} + selected={formik.values.direct} + /> +
+
+
+ + {/*
+ +
*/} +
+
+
+ {t('discount.price')} +
+ formik.setFieldValue('type', value)} + selected={formik.values.type.toString()} + /> +
+
+ +
+
+ formik.setFieldValue('startDate', value)} + placeholder='' + error_text={formik.touched.startDate && formik.errors.startDate ? formik.errors.startDate : ''} + defaulValue={formik.values.startDate} + /> + formik.setFieldValue('endDate', value)} + placeholder='' + error_text={formik.touched.endDate && formik.errors.endDate ? formik.errors.endDate : ''} + defaulValue={formik.values.endDate} + /> +
+
+
+
+

+ {t('discount.service_status')} +

+
+
+ {t('ads.deactive')} +
+ { }} + /> +
+
+ + { + formik.values.direct && + +

{t('discount.availabe_service')}

+
+ + { + getServices.data?.data?.services?.map((item: ServiceItemType) => ( +
+
+ setServiceId(e.target.checked ? [...serviceId, item.id] : serviceId.filter(id => id !== item.id))} + /> +
+
+ {item.name} +
+
+ )) + } +
+
+ } + + {/* { + getPlans.data && +
+ { + getPlans.data?.data?.subscriptions?.map((item: PlanItemType) => ( +
+
+ setPlanId(e.target.checked ? item.id : '')} + /> +
+
+ {item.name} +
+
+ )) + } +
+ } + + { + planId && getCustomers.data && +
+ { + getCustomers.data?.data?.customers?.map((item: CustomerItemType) => ( +
+
+ setCustomerId(e.target.checked ? item.id : '')} + /> +
+
+ {item.firstName + ' ' + item.lastName} +
+
+ )) + } +
+ } */} +
+
+
+ ) +} +export default UpdateDiscount \ No newline at end of file diff --git a/src/pages/discounts/hooks/useDiscountData.ts b/src/pages/discounts/hooks/useDiscountData.ts index 61ed078..659bd36 100644 --- a/src/pages/discounts/hooks/useDiscountData.ts +++ b/src/pages/discounts/hooks/useDiscountData.ts @@ -21,3 +21,23 @@ export const useToggleStatusDiscount = () => { mutationFn: (variables: string) => api.toggleStatusDiscount(variables), }); }; + +export const useGetDiscountDetail = (id: string) => { + return useQuery({ + queryKey: ["discount", id], + queryFn: () => api.getDiscountDetail(id), + }); +}; + +export const useUpdateDiscount = () => { + return useMutation({ + mutationFn: (variables: { id: string; params: CreateDiscountType }) => + api.updateDiscount(variables.id, variables.params), + }); +}; + +export const useDeleteDiscount = () => { + return useMutation({ + mutationFn: (id: string) => api.deleteDiscount(id), + }); +}; diff --git a/src/pages/discounts/service/DiscountService.ts b/src/pages/discounts/service/DiscountService.ts index fabe3e6..5599a95 100644 --- a/src/pages/discounts/service/DiscountService.ts +++ b/src/pages/discounts/service/DiscountService.ts @@ -15,3 +15,21 @@ export const toggleStatusDiscount = async (id: string) => { const { data } = await axios.patch(`/discounts/${id}/toggle`); return data; }; + +export const getDiscountDetail = async (id: string) => { + const { data } = await axios.get(`/discounts/${id}`); + return data; +}; + +export const updateDiscount = async ( + id: string, + params: CreateDiscountType +) => { + const { data } = await axios.patch(`/discounts/${id}`, params); + return data; +}; + +export const deleteDiscount = async (id: string) => { + const { data } = await axios.delete(`/discounts/${id}`); + return data; +}; diff --git a/src/pages/discounts/types/DiscountTypes.ts b/src/pages/discounts/types/DiscountTypes.ts index e9edae1..7d10cb9 100644 --- a/src/pages/discounts/types/DiscountTypes.ts +++ b/src/pages/discounts/types/DiscountTypes.ts @@ -24,7 +24,7 @@ export type DiscountItemType = { title: string; type: DiscountType; calculationType: DiscountCalculationType; - amount: string | number; + value: string | number; startDate: string; endDate: string; subscriptionPlans: { id: string; createdAt: string; name: string }[]; @@ -35,3 +35,16 @@ export type DiscountItemType = { isActive: boolean; users: { firstName: string; lastName: string }[]; }; + +export type SubscriptionPlanType = { + createdAt: string; + deletedAt: string | null; + duration: number; + id: string; + isActive: boolean; + name: string; + originalPrice: number; + price: number; + service: { id: string; createdAt: string }; + author: string; +}; diff --git a/src/router/Main.tsx b/src/router/Main.tsx index b760472..e62f622 100644 --- a/src/router/Main.tsx +++ b/src/router/Main.tsx @@ -59,7 +59,7 @@ import UpdateService from '../pages/service/UpdateService' import UpdateCustomer from '../pages/customer/Update' import UpdateBlog from '../pages/blog/Update' import Update from '../pages/annoncement/Update' - +import UpdateDiscount from '../pages/discounts/UpdateDiscount' const MainRouter: FC = () => { const { hasSubMenu } = useSharedStore() @@ -96,6 +96,7 @@ const MainRouter: FC = () => { } /> } /> } /> + } /> } /> } /> } />
- + - +
- + - - + +
+ + + + + handleDelete(item.id)} + /> +
+