diff --git a/src/config/Pages.ts b/src/config/Pages.ts index 96fa881..6b80049 100644 --- a/src/config/Pages.ts +++ b/src/config/Pages.ts @@ -56,4 +56,8 @@ export const Pages = { add: "/roles/add", update: "/roles/update/", }, + admins: { + list: "/admins/list", + update: "/admins/update/", + }, }; diff --git a/src/langs/fa.json b/src/langs/fa.json index b983ea5..5227222 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -62,7 +62,8 @@ "setting": "تنظیمات", "logout": "خروج", "roles": "سطح دسترسی ها", - "payment_methods": "روش های پرداخت" + "payment_methods": "روش های پرداخت", + "admins": "مدیران" }, "slider": { "new_slider": "اسلایدر جدید", diff --git a/src/pages/admin/List.tsx b/src/pages/admin/List.tsx new file mode 100644 index 0000000..4bfaecd --- /dev/null +++ b/src/pages/admin/List.tsx @@ -0,0 +1,60 @@ +import { type FC } from 'react' +import Table from '@/components/Table' +import Filters from '@/components/Filters' +import { useGetAdmins, useDeleteAdmin } from './hooks/useAdminData' +import { useAdminFilters } from './hooks/useAdminFilters' +import { getAdminTableColumns } from './components/AdminTableColumns' +import { useAdminFiltersFields } from './components/AdminFiltersFields' + +const AdminList: FC = () => { + const { + filters, + currentPage, + apiParams, + handleFiltersChange, + handlePageChange, + limit, + } = useAdminFilters() + + const { data: adminsData, isLoading } = useGetAdmins(apiParams) + const { mutate: deleteAdmin, isPending: isDeleting } = useDeleteAdmin() + + const admins = adminsData?.data || [] + const columns = getAdminTableColumns({ + onDelete: (id: string) => deleteAdmin(id), + isDeleting + }) + const filterFields = useAdminFiltersFields() + + const totalPages = Math.ceil(admins.length / limit) || 1 + + return ( +
+
+

لیست مدیران

+
+ +
+ +
+ + + + ) +} + +export default AdminList \ No newline at end of file diff --git a/src/pages/admin/components/AdminFiltersFields.tsx b/src/pages/admin/components/AdminFiltersFields.tsx new file mode 100644 index 0000000..ad0dd94 --- /dev/null +++ b/src/pages/admin/components/AdminFiltersFields.tsx @@ -0,0 +1,13 @@ +import { useMemo } from 'react' +import type { FieldType } from '@/components/Filters' + +export const useAdminFiltersFields = (): FieldType[] => { + return useMemo(() => [ + { + type: 'input', + name: 'search', + placeholder: 'جستجو', + }, + ], []) +} + diff --git a/src/pages/admin/components/AdminTableColumns.tsx b/src/pages/admin/components/AdminTableColumns.tsx new file mode 100644 index 0000000..81b9676 --- /dev/null +++ b/src/pages/admin/components/AdminTableColumns.tsx @@ -0,0 +1,75 @@ +import type { ColumnType } from '@/components/types/TableTypes' +import type { Admin } from '../types/Types' +import TrashWithConfrim from '@/components/TrashWithConfrim' +import { Eye } from 'iconsax-react' +import { Link } from 'react-router-dom' +import { Pages } from '@/config/Pages' + +interface GetAdminTableColumnsParams { + onDelete?: (id: string) => void + isDeleting?: boolean +} + +export const getAdminTableColumns = ({ onDelete, isDeleting }: GetAdminTableColumnsParams = {}): ColumnType[] => { + return [ + { + key: 'firstName', + title: 'نام', + render: (item: Admin) => { + return item.firstName || '-' + } + }, + { + key: 'lastName', + title: 'نام خانوادگی', + render: (item: Admin) => { + return item.lastName || '-' + } + }, + { + key: 'phone', + title: 'شماره تماس', + render: (item: Admin) => { + return item.phone || '-' + } + }, + { + key: 'roles', + title: 'تعداد نقش‌ها', + render: (item: Admin) => { + return item.roles?.length || 0 + } + }, + { + key: 'createdAt', + title: 'تاریخ ایجاد', + render: (item: Admin) => { + return new Date(item.createdAt).toLocaleDateString('fa-IR') + } + }, + { + key: 'actions', + title: '', + render: (item: Admin) => { + return ( +
+ + + + {onDelete && ( + onDelete(item.id)} + isloading={isDeleting} + /> + )} +
+ ) + } + }, + ] +} + diff --git a/src/pages/admin/hooks/useAdminData.ts b/src/pages/admin/hooks/useAdminData.ts new file mode 100644 index 0000000..702c08e --- /dev/null +++ b/src/pages/admin/hooks/useAdminData.ts @@ -0,0 +1,22 @@ +import * as api from "../service/AdminService"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import type { GetAdminsParams } from "../types/Types"; + +export const useGetAdmins = (params?: GetAdminsParams) => { + return useQuery({ + queryKey: ["admins", params], + queryFn: () => api.getAdmins(params), + }); +}; + +export const useDeleteAdmin = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: api.deleteAdmin, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["admins"], + }); + }, + }); +}; diff --git a/src/pages/admin/hooks/useAdminFilters.ts b/src/pages/admin/hooks/useAdminFilters.ts new file mode 100644 index 0000000..e6c2b5a --- /dev/null +++ b/src/pages/admin/hooks/useAdminFilters.ts @@ -0,0 +1,44 @@ +import { useState, useMemo } from 'react' +import type { FilterValues } from '@/components/Filters' +import type { GetAdminsParams } from '../types/Types' + +const DEFAULT_PAGE = 1 +const DEFAULT_LIMIT = 10 + +export const useAdminFilters = () => { + const [filters, setFilters] = useState({}) + const [currentPage, setCurrentPage] = useState(DEFAULT_PAGE) + const [limit] = useState(DEFAULT_LIMIT) + + const apiParams = useMemo(() => { + const params: GetAdminsParams = { + 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/admin/service/AdminService.ts b/src/pages/admin/service/AdminService.ts new file mode 100644 index 0000000..6e73cf1 --- /dev/null +++ b/src/pages/admin/service/AdminService.ts @@ -0,0 +1,16 @@ +import axios from "@/config/axios"; +import type { GetAdminsResponse, GetAdminsParams } from "../types/Types"; + +export const getAdmins = async ( + params?: GetAdminsParams +): Promise => { + const { data } = await axios.get("/admin/admins", { + params, + }); + return data; +}; + +export const deleteAdmin = async (id: string) => { + const { data } = await axios.delete(`/admin/admins/${id}`); + return data; +}; diff --git a/src/pages/admin/types/Types.ts b/src/pages/admin/types/Types.ts new file mode 100644 index 0000000..5ba7e98 --- /dev/null +++ b/src/pages/admin/types/Types.ts @@ -0,0 +1,40 @@ +import type { IResponse } from "@/types/response.types"; + +export type RoleDetail = { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + name: string; + restaurant: string | null; +}; + +export type AdminRole = { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + admin: string; + role: RoleDetail; + restaurant: string; +}; + +export type Admin = { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + firstName: string; + lastName: string; + phone: string; + roles: AdminRole[]; +}; + +export type GetAdminsParams = { + page?: number + limit?: number + search?: string +} + +export type GetAdminsResponse = IResponse; + diff --git a/src/router/Main.tsx b/src/router/Main.tsx index 609aa03..44a6086 100644 --- a/src/router/Main.tsx +++ b/src/router/Main.tsx @@ -27,6 +27,7 @@ import Setting from '@/pages/settings/Setting' import PaymentMethodsList from '@/pages/paymentMethods/List' import CreatePaymentMethod from '@/pages/paymentMethods/Create' import UpdatePaymentMethod from '@/pages/paymentMethods/Update' +import AdminList from '@/pages/admin/List' const MainRouter: FC = () => { const { hasSubMenu } = useSharedStore() @@ -66,6 +67,8 @@ const MainRouter: FC = () => { } /> } /> } /> + + } /> diff --git a/src/shared/SideBar.tsx b/src/shared/SideBar.tsx index 1df712d..736a2e4 100644 --- a/src/shared/SideBar.tsx +++ b/src/shared/SideBar.tsx @@ -2,7 +2,7 @@ import { type FC, useEffect } from 'react' import LogoImage from '../assets/images/logo.svg' import LogoSmall from '../assets/images/logo-small.svg' import { useTranslation } from 'react-i18next' -import { DocumentText, Home2, Logout, Message, NotificationStatus, People, Setting2, TicketDiscount, Chart, Calendar, Security, Card } from 'iconsax-react' +import { DocumentText, Home2, Logout, Message, NotificationStatus, People, Setting2, TicketDiscount, Chart, Calendar, Security, Card, SecurityUser } from 'iconsax-react' import SideBarItem from './SideBarItem' import { useLocation } from 'react-router-dom' import { Pages } from '../config/Pages' @@ -110,6 +110,16 @@ const SideBar: FC = () => { name='customers' activeName='customers' /> + + } + title={t('sidebar.admins')} + isActive={isActive('admins')} + link={Pages.admins.list} + name='admins' + activeName='admins' + /> + } title={t('sidebar.discount')}