diff --git a/src/components/Radio.tsx b/src/components/Radio.tsx index a8282af..4f7dcdc 100644 --- a/src/components/Radio.tsx +++ b/src/components/Radio.tsx @@ -2,8 +2,8 @@ import { FC } from 'react' type Props = { isActive: boolean, - value: 'year' | '3month' | 'month', - onChange: (value: 'year' | '3month' | 'month') => void + value: string, + onChange: (value: string) => void } const Radio: FC = (props: Props) => { diff --git a/src/components/RadioGroup.tsx b/src/components/RadioGroup.tsx index f68c939..0421109 100644 --- a/src/components/RadioGroup.tsx +++ b/src/components/RadioGroup.tsx @@ -5,10 +5,10 @@ import Radio from './Radio' type Props = { items: { label: string - value: 'year' | '3month' | 'month' + value: string }[] selected: string - onChange: (value: 'year' | '3month' | 'month') => void + onChange: (value: string) => void } const RadioGroup: FC = (props: Props) => { diff --git a/src/langs/fa.json b/src/langs/fa.json index d4d1778..92f09d9 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -296,7 +296,12 @@ "discount_code": "کد تخفیف", "direct_discount": "تخفیف مستقیم", "fixed_amount": "مبلغ ثابت", - "percent": "درصد" + "percent": "درصد", + "errors_plan": "سرویس و پلن را انتخاب کنید", + "DIRECT": "مستقیم", + "FIXED": "ثابت", + "plan": "پلن", + "user": "کاربر" }, "blog": { "blog": "بلاگ", diff --git a/src/pages/discounts/CreateDiscount.tsx b/src/pages/discounts/CreateDiscount.tsx index 7657150..1a26ffb 100644 --- a/src/pages/discounts/CreateDiscount.tsx +++ b/src/pages/discounts/CreateDiscount.tsx @@ -1,13 +1,80 @@ -import { FC } from 'react' +import { FC, 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, TickSquare, Refresh } from 'iconsax-react' +import { TickCircle } from 'iconsax-react' import DatePickerComponent from '../../components/DatePicker' import RadioGroup from '../../components/RadioGroup' +import { useFormik } from 'formik' +import { CreateDiscountType, DiscountCalculationType, DiscountType } from './types/DiscountTypes' +import * as Yup from 'yup' +import { useGetAllServices, useGetPlans } from '../service/hooks/useServiceData' +import { PlanItemType, ServiceItemType } from '../service/types/ServiceTypes' +import CheckBoxComponent from '../../components/CheckBoxComponent' +import { toast } from 'react-toastify' +import { useCreateDiscount } from './hooks/useDiscountData' +import { useGetCustomers } from '../customer/hooks/useCustomerData' +import { CustomerItemType } from '../customer/types/CustomerTypes' +import { useNavigate } from 'react-router-dom' +import { Pages } from '../../config/Pages' +import { ErrorType } from '../../helpers/types' + const CreateDiscount: FC = () => { + + 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 createDiscount = useCreateDiscount() + + const formik = useFormik({ + initialValues: { + amount: '', + calculationType: DiscountCalculationType.FIXED, + endDate: '', + startDate: '', + title: '', + type: DiscountType.DIRECT, + subscriptionPlanIds: [], + userIds: [] + }, + validationSchema: Yup.object({ + amount: Yup.string().required(t('errors.required')), + calculationType: Yup.string().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) { + if (serviceId && planId) { + values.subscriptionPlanIds = [planId] + values.userIds = [customerId] + const params: CreateDiscountType = { + ...values, + startDate: new Date(values.startDate).toISOString(), + endDate: new Date(values.endDate).toISOString(), + amount: +values.amount + } + createDiscount.mutate(params, { + onSuccess: () => { + toast.success(t('success')) + navigate(Pages.discount.list) + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error?.message[0]) + } + }) + } else { + toast.error(t('discount.errors_plan')) + } + }, + }) return (
@@ -16,6 +83,7 @@ const CreateDiscount: FC = () => {
@@ -50,8 +118,10 @@ const CreateDiscount: FC = () => { -
+ {/*
-
+
*/}
@@ -70,35 +140,37 @@ const CreateDiscount: FC = () => { items={[ { label: t('discount.fixed_amount'), - value: '3month' + value: DiscountCalculationType.FIXED }, { label: t('discount.percent'), - value: 'year' + value: DiscountCalculationType.PERCENTAGE } ]} - onChange={() => ''} - selected={''} + onChange={(value) => formik.setFieldValue('calculationType', value)} + selected={formik.values.calculationType} />
{ }} + onChange={(value) => formik.setFieldValue('startDate', value)} placeholder='' - defaulValue='1400-01-01' + error_text={formik.touched.startDate && formik.errors.startDate ? formik.errors.startDate : ''} /> { }} + onChange={(value) => formik.setFieldValue('endDate', value)} placeholder='' - defaulValue='1400-01-01' + error_text={formik.touched.endDate && formik.errors.endDate ? formik.errors.endDate : ''} />
@@ -117,37 +189,70 @@ const CreateDiscount: FC = () => { /> -

{t('discount.availabe_service')}

-
+

{t('discount.availabe_service')}

+
-
-
- -
-
- لورم ایپسوم -
-
-
-
- -
-
- لورم ایپسوم -
-
-
-
- -
-
- لورم ایپسوم -
-
+ { + getServices.data?.data?.services?.map((item: ServiceItemType) => ( +
+
+ setServiceId(e.target.checked ? 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} +
+
+ )) + } +
+ }
diff --git a/src/pages/discounts/DiscountsList.tsx b/src/pages/discounts/DiscountsList.tsx index 7b6ac89..28b2bb6 100644 --- a/src/pages/discounts/DiscountsList.tsx +++ b/src/pages/discounts/DiscountsList.tsx @@ -1,14 +1,23 @@ -import { FC } from 'react' +import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '../../components/Button' -import { Add, Eye } from 'iconsax-react' +import { Add } from 'iconsax-react' import Td from '../../components/Td' import { Link } from 'react-router-dom' import { Pages } from '../../config/Pages' -import SwitchComponent from '../../components/Switch' import Input from '../../components/Input' +import { useGetDiscounts } from './hooks/useDiscountData' +import { DiscountItemType } from './types/DiscountTypes' +import moment from 'moment-jalaali' +import ToggleStatusDiscount from './components/ToggleStatusDiscount' +import PageLoading from '../../components/PageLoading' + const DiscountList: FC = () => { + const { t } = useTranslation('global') + const [search, setSearch] = useState('') + const getDiscounts = useGetDiscounts(search) + return (
@@ -41,6 +50,7 @@ const DiscountList: FC = () => { variant='search' placeholder={t('ads.search')} className='bg-white w-full' + onChangeSearchFinal={(value) => setSearch(value)} />
@@ -50,65 +60,52 @@ const DiscountList: FC = () => { -
- - - - - - - - - - - - - - - -
- - - - - - - - -
- - - - - - - - { }} - /> - - - - -
- - - - - - - - { }} - /> - - - - -
-
+ { + getDiscounts.isPending ? + + : +
+ + + + + + + { + getDiscounts.data?.data?.discounts?.map((item: DiscountItemType, index: number) => { + return ( + + + + ) + }) + } + +
+ + + + + + + + +
+ + + + + + + + + +
+
+ }
) } diff --git a/src/pages/discounts/components/ToggleStatusDiscount.tsx b/src/pages/discounts/components/ToggleStatusDiscount.tsx new file mode 100644 index 0000000..592fc87 --- /dev/null +++ b/src/pages/discounts/components/ToggleStatusDiscount.tsx @@ -0,0 +1,28 @@ +import { FC, useState } from 'react' +import SwitchComponent from '../../../components/Switch' +import { useToggleStatusDiscount } from '../hooks/useDiscountData' + +type Props = { + defaultActive: boolean, + id: string +} + +const ToggleStatusDiscount: FC = (props: Props) => { + + const [isActive, setIsActive] = useState(props.defaultActive) + const toggleStatus = useToggleStatusDiscount() + + const handleChange = (value: boolean) => { + setIsActive(value) + toggleStatus.mutate(props.id) + } + + return ( + + ) +} + +export default ToggleStatusDiscount \ No newline at end of file diff --git a/src/pages/discounts/hooks/useDiscountData.ts b/src/pages/discounts/hooks/useDiscountData.ts new file mode 100644 index 0000000..61ed078 --- /dev/null +++ b/src/pages/discounts/hooks/useDiscountData.ts @@ -0,0 +1,23 @@ +import { useMutation, useQuery } from "@tanstack/react-query"; +import * as api from "../service/DiscountService"; +import { CreateDiscountType } from "../types/DiscountTypes"; + +export const useCreateDiscount = () => { + return useMutation({ + mutationFn: (variables: CreateDiscountType) => + api.createDiscount(variables), + }); +}; + +export const useGetDiscounts = (search: string) => { + return useQuery({ + queryKey: ["discounts", search], + queryFn: () => api.getDiscounts(search), + }); +}; + +export const useToggleStatusDiscount = () => { + return useMutation({ + mutationFn: (variables: string) => api.toggleStatusDiscount(variables), + }); +}; diff --git a/src/pages/discounts/service/DiscountService.ts b/src/pages/discounts/service/DiscountService.ts new file mode 100644 index 0000000..fabe3e6 --- /dev/null +++ b/src/pages/discounts/service/DiscountService.ts @@ -0,0 +1,17 @@ +import axios from "../../../config/axios"; +import { CreateDiscountType } from "../types/DiscountTypes"; + +export const createDiscount = async (params: CreateDiscountType) => { + const { data } = await axios.post(`/discounts`, params); + return data; +}; + +export const getDiscounts = async (search: string) => { + const { data } = await axios.get(`/discounts?q=${search}`); + return data; +}; + +export const toggleStatusDiscount = async (id: string) => { + const { data } = await axios.patch(`/discounts/${id}/toggle`); + return data; +}; diff --git a/src/pages/discounts/types/DiscountTypes.ts b/src/pages/discounts/types/DiscountTypes.ts new file mode 100644 index 0000000..9a93a37 --- /dev/null +++ b/src/pages/discounts/types/DiscountTypes.ts @@ -0,0 +1,37 @@ +export type CreateDiscountType = { + title: string; + type: string; + calculationType: DiscountCalculationType; + amount: string | number; + startDate: string; + endDate: string; + subscriptionPlanIds: string[]; + userIds: string[]; +}; + +export enum DiscountType { + DIRECT = "DIRECT", + CODE = "CODE", +} + +export enum DiscountCalculationType { + FIXED = "FIXED", + PERCENTAGE = "PERCENTAGE", +} + +export type DiscountItemType = { + id: string; + title: string; + type: DiscountType; + calculationType: DiscountCalculationType; + amount: string | number; + startDate: string; + endDate: string; + subscriptionPlans: { id: string; createdAt: string; name: string }[]; + userIds: string[]; + code: string; + createdAt: string; + updatedAt: string; + isActive: boolean; + users: { firstName: string; lastName: string }[]; +}; diff --git a/src/pages/service/hooks/useServiceData.ts b/src/pages/service/hooks/useServiceData.ts index 6f00775..466f31b 100644 --- a/src/pages/service/hooks/useServiceData.ts +++ b/src/pages/service/hooks/useServiceData.ts @@ -13,12 +13,28 @@ export const useGetAllServices = ( page: number, category: string, status: string, - isDanakSuggest: boolean + isDanakSuggest?: boolean, + limit?: number ) => { return useQuery({ - queryKey: ["services", search, page, category, status, isDanakSuggest], + queryKey: [ + "services", + search, + page, + category, + status, + isDanakSuggest, + limit, + ], queryFn: () => - api.getAllServicess(search, page, category, status, isDanakSuggest), + api.getAllServicess( + search, + page, + category, + status, + isDanakSuggest, + limit + ), }); }; diff --git a/src/pages/service/service/ServiceService.ts b/src/pages/service/service/ServiceService.ts index 7992ce9..c126fca 100644 --- a/src/pages/service/service/ServiceService.ts +++ b/src/pages/service/service/ServiceService.ts @@ -12,7 +12,8 @@ export const getAllServicess = async ( page: number, category: string, status: string, - isDanakSuggest: boolean + isDanakSuggest?: boolean, + limit?: number ) => { let query = `?page=${page}`; if (status) { @@ -25,9 +26,14 @@ export const getAllServicess = async ( query += `&q=${search}`; } - if (isDanakSuggest) { + if (isDanakSuggest !== undefined) { query += `&isDanakSuggest=${isDanakSuggest}`; } + + if (limit) { + query += `&limit=${limit}`; + } + const { data } = await axios.get(`/danak-services${query}`); return data; };