diff --git a/src/config/Pages.ts b/src/config/Pages.ts index 67fc5dd..96fa881 100644 --- a/src/config/Pages.ts +++ b/src/config/Pages.ts @@ -12,7 +12,7 @@ export const Pages = { }, payment_methods: { list: "/payment_methods/list", - add: "/payment_methods/add", + add: "/payment_methods/create", update: "/payment_methods/update/", }, foods: { diff --git a/src/pages/paymentMethods/Create.tsx b/src/pages/paymentMethods/Create.tsx new file mode 100644 index 0000000..4c92bb6 --- /dev/null +++ b/src/pages/paymentMethods/Create.tsx @@ -0,0 +1,106 @@ +import { type FC } from 'react' +import { useFormik } from 'formik' +import * as Yup from 'yup' +import { TickCircle } from 'iconsax-react' +import { toast } from 'react-toastify' +import { useNavigate } from 'react-router-dom' +import Button from '@/components/Button' +import Input from '@/components/Input' +import Select from '@/components/Select' +import Switch from '@/components/Switch' +import type { CreateRestaurantPaymentMethodType, PaymentMethod } from './types/Types' +import { useCreateRestaurantPaymentMethod, useGetPaymentMethods } from './hooks/usePaymentMethodData' +import { Pages } from '@/config/Pages' +import type { ErrorType } from '@/helpers/types' +import { extractErrorMessage } from '@/config/func' + +const CreatePaymentMethod: FC = () => { + const navigate = useNavigate() + const { mutate: createPaymentMethod, isPending } = useCreateRestaurantPaymentMethod() + const { data: paymentMethodsData } = useGetPaymentMethods() + + const paymentMethods = paymentMethodsData?.data?.map((method: PaymentMethod) => ({ + label: method.name, + value: method.id + })) || [] + + const formik = useFormik({ + initialValues: { + paymentMethodId: '', + merchantId: '', + isActive: true, + }, + validationSchema: Yup.object().shape({ + paymentMethodId: Yup.string().required('روش پرداخت الزامی است'), + merchantId: Yup.string().required('شناسه مرچنت الزامی است'), + isActive: Yup.boolean(), + }), + onSubmit: (values) => { + createPaymentMethod(values, { + onSuccess: () => { + toast.success('روش پرداخت با موفقیت ایجاد شد') + navigate(Pages.payment_methods.list) + }, + onError: (error: ErrorType) => { + toast.error(extractErrorMessage(error)) + }, + }) + }, + }) + + return ( +
+
+
+ افزودن روش پرداخت +
+
+ +
+
+ +
+
+
+ +
+
+ formik.setFieldValue('isActive', value)} + label='فعال' + /> +
+
+
+
+ ) +} + +export default CreatePaymentMethod \ No newline at end of file diff --git a/src/pages/paymentMethods/List.tsx b/src/pages/paymentMethods/List.tsx index 0c3d82f..02eb58c 100644 --- a/src/pages/paymentMethods/List.tsx +++ b/src/pages/paymentMethods/List.tsx @@ -1,9 +1,70 @@ import { type FC } from 'react' +import Table from '@/components/Table' +import Filters from '@/components/Filters' +import { Add } from 'iconsax-react' +import Button from '@/components/Button' +import { useGetRestaurantPaymentMethods, useDeleteRestaurantPaymentMethod } from './hooks/usePaymentMethodData' +import { usePaymentMethodFilters } from './hooks/usePaymentMethodFilters' +import { getPaymentMethodTableColumns } from './components/PaymentMethodTableColumns' +import { usePaymentMethodFiltersFields } from './components/PaymentMethodFiltersFields' +import { Link } from 'react-router-dom' +import { Pages } from '@/config/Pages' const PaymentMethodsList: FC = () => { + const { + filters, + currentPage, + apiParams, + handleFiltersChange, + handlePageChange, + limit, + } = usePaymentMethodFilters() + + const { data: restaurantPaymentMethodsData, isLoading } = useGetRestaurantPaymentMethods(apiParams) + const { mutate: deletePaymentMethod, isPending: isDeleting } = useDeleteRestaurantPaymentMethod() + + const paymentMethods = restaurantPaymentMethodsData?.data || [] + const columns = getPaymentMethodTableColumns({ + onDelete: (id: string) => deletePaymentMethod(id), + isDeleting + }) + const filterFields = usePaymentMethodFiltersFields() + + const totalPages = Math.ceil(paymentMethods.length / limit) || 1 + return (
-

لیست روش های پرداخت

+
+

لیست روش های پرداخت

+ + + +
+ +
+ +
+ + ) } diff --git a/src/pages/paymentMethods/components/PaymentMethodFiltersFields.tsx b/src/pages/paymentMethods/components/PaymentMethodFiltersFields.tsx new file mode 100644 index 0000000..d6f2135 --- /dev/null +++ b/src/pages/paymentMethods/components/PaymentMethodFiltersFields.tsx @@ -0,0 +1,13 @@ +import { useMemo } from 'react' +import type { FieldType } from '@/components/Filters' + +export const usePaymentMethodFiltersFields = (): FieldType[] => { + return useMemo(() => [ + { + type: 'input', + name: 'search', + placeholder: 'جستجو', + }, + ], []) +} + diff --git a/src/pages/paymentMethods/components/PaymentMethodTableColumns.tsx b/src/pages/paymentMethods/components/PaymentMethodTableColumns.tsx new file mode 100644 index 0000000..7c10d1d --- /dev/null +++ b/src/pages/paymentMethods/components/PaymentMethodTableColumns.tsx @@ -0,0 +1,76 @@ +import type { ColumnType } from '@/components/types/TableTypes' +import type { PaymentMethod } from '../types/Types' +import TrashWithConfrim from '@/components/TrashWithConfrim' +import { Eye } from 'iconsax-react' +import { Link } from 'react-router-dom' +import { Pages } from '@/config/Pages' +import Status from '@/components/Status' + +interface GetPaymentMethodTableColumnsParams { + onDelete?: (id: string) => void + isDeleting?: boolean +} + +export const getPaymentMethodTableColumns = ({ onDelete, isDeleting }: GetPaymentMethodTableColumnsParams = {}): ColumnType[] => { + return [ + { + key: 'name', + title: 'نام', + }, + { + key: 'keyName', + title: 'کلید', + }, + { + key: 'description', + title: 'توضیحات', + }, + { + key: 'isActive', + title: 'وضعیت', + render: (item: PaymentMethod) => { + return ( + + ) + } + }, + { + key: 'order', + title: 'ترتیب', + }, + { + key: 'createdAt', + title: 'تاریخ ایجاد', + render: (item: PaymentMethod) => { + return new Date(item.createdAt).toLocaleDateString('fa-IR') + } + }, + { + key: 'actions', + title: '', + render: (item: PaymentMethod) => { + return ( +
+ + + + {onDelete && ( + onDelete(item.id)} + isloading={isDeleting} + /> + )} +
+ ) + } + }, + ] +} + diff --git a/src/pages/paymentMethods/hooks/usePaymentMethodData.ts b/src/pages/paymentMethods/hooks/usePaymentMethodData.ts index fd4682d..1f2f3a8 100644 --- a/src/pages/paymentMethods/hooks/usePaymentMethodData.ts +++ b/src/pages/paymentMethods/hooks/usePaymentMethodData.ts @@ -1,5 +1,6 @@ import * as api from "../service/PaymentMethodService"; -import { useQuery } from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import type { GetRestaurantPaymentMethodsParams } from "../types/Types"; export const useGetPaymentMethods = () => { return useQuery({ @@ -8,9 +9,35 @@ export const useGetPaymentMethods = () => { }); }; -export const useGetRestaurantPaymentMethods = () => { +export const useGetRestaurantPaymentMethods = ( + params?: GetRestaurantPaymentMethodsParams +) => { return useQuery({ - queryKey: ["restaurant-payment-methods"], - queryFn: api.getRestaurantPaymentMethods, + queryKey: ["restaurant-payment-methods", params], + queryFn: () => api.getRestaurantPaymentMethods(params), + }); +}; + +export const useCreateRestaurantPaymentMethod = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: api.createRestaurantPaymentMethod, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["restaurant-payment-methods"], + }); + }, + }); +}; + +export const useDeleteRestaurantPaymentMethod = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: api.deleteRestaurantPaymentMethod, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["restaurant-payment-methods"], + }); + }, }); }; diff --git a/src/pages/paymentMethods/hooks/usePaymentMethodFilters.ts b/src/pages/paymentMethods/hooks/usePaymentMethodFilters.ts new file mode 100644 index 0000000..08d29b9 --- /dev/null +++ b/src/pages/paymentMethods/hooks/usePaymentMethodFilters.ts @@ -0,0 +1,43 @@ +import { useState, useMemo } from "react"; +import type { FilterValues } from "@/components/Filters"; +import type { GetRestaurantPaymentMethodsParams } from "../types/Types"; + +const DEFAULT_PAGE = 1; +const DEFAULT_LIMIT = 10; + +export const usePaymentMethodFilters = () => { + const [filters, setFilters] = useState({}); + const [currentPage, setCurrentPage] = useState(DEFAULT_PAGE); + const [limit] = useState(DEFAULT_LIMIT); + + const apiParams = useMemo(() => { + const params: GetRestaurantPaymentMethodsParams = { + page: currentPage, + limit: limit, + }; + + if (filters.search) { + params.search = filters.search; + } + + return params; + }, [filters, currentPage, limit]); + + const handleFiltersChange = (newFilters: FilterValues) => { + setFilters(newFilters); + setCurrentPage(DEFAULT_PAGE); + }; + + const handlePageChange = (page: number) => { + setCurrentPage(page); + }; + + return { + filters, + currentPage, + apiParams, + handleFiltersChange, + handlePageChange, + limit, + }; +}; diff --git a/src/pages/paymentMethods/service/PaymentMethodService.ts b/src/pages/paymentMethods/service/PaymentMethodService.ts index 091c09e..b6e9b32 100644 --- a/src/pages/paymentMethods/service/PaymentMethodService.ts +++ b/src/pages/paymentMethods/service/PaymentMethodService.ts @@ -1,11 +1,41 @@ import axios from "@/config/axios"; +import type { + CreateRestaurantPaymentMethodType, + GetRestaurantPaymentMethodsParams, + PaymentMethodsResponse, + RestaurantPaymentMethodsResponse, +} from "../types/Types"; -export const getPaymentMethods = async () => { - const { data } = await axios.get("/admin/payment-methods"); +export const getPaymentMethods = async (): Promise => { + const { data } = await axios.get( + "/admin/payment-methods" + ); return data; }; -export const getRestaurantPaymentMethods = async () => { - const { data } = await axios.get("/admin/restaurant-payment-methods"); +export const getRestaurantPaymentMethods = async ( + params?: GetRestaurantPaymentMethodsParams +): Promise => { + const { data } = await axios.get( + "/admin/restaurant-payment-methods", + { params } + ); + return data; +}; + +export const deleteRestaurantPaymentMethod = async (id: string) => { + const { data } = await axios.delete( + `/admin/restaurant-payment-methods/${id}` + ); + return data; +}; + +export const createRestaurantPaymentMethod = async ( + params: CreateRestaurantPaymentMethodType +) => { + const { data } = await axios.post( + "/admin/restaurant-payment-methods", + params + ); return data; }; diff --git a/src/pages/paymentMethods/types/Types.ts b/src/pages/paymentMethods/types/Types.ts new file mode 100644 index 0000000..4e2e53a --- /dev/null +++ b/src/pages/paymentMethods/types/Types.ts @@ -0,0 +1,30 @@ +import type { IResponse } from "@/types/response.types"; + +export type PaymentMethod = { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + name: string; + keyName: string; + description: string; + icon: string | null; + isActive: boolean; + order: number; +}; + +export type PaymentMethodsResponse = IResponse; + +export type RestaurantPaymentMethodsResponse = IResponse; + +export type CreateRestaurantPaymentMethodType = { + paymentMethodId: string; + merchantId: string; + isActive: boolean; +}; + +export type GetRestaurantPaymentMethodsParams = { + page?: number; + limit?: number; + search?: string; +}; diff --git a/src/router/Main.tsx b/src/router/Main.tsx index d83d144..050b591 100644 --- a/src/router/Main.tsx +++ b/src/router/Main.tsx @@ -25,6 +25,7 @@ import UpdateRole from '@/pages/roles/Update' import UpdateSchedule from '@/pages/schedule/Update' import Setting from '@/pages/settings/Setting' import PaymentMethodsList from '@/pages/paymentMethods/List' +import CreatePaymentMethod from '@/pages/paymentMethods/Create' const MainRouter: FC = () => { const { hasSubMenu } = useSharedStore() @@ -62,6 +63,7 @@ const MainRouter: FC = () => { } /> } /> + } />