diff --git a/src/config/Pages.ts b/src/config/Pages.ts index b06f4f9..8c03a61 100644 --- a/src/config/Pages.ts +++ b/src/config/Pages.ts @@ -78,4 +78,7 @@ export const Pages = { pagers: { list: "/pagers/list", }, + notifications: { + list: "/notifications/list", + }, }; diff --git a/src/langs/fa.json b/src/langs/fa.json index e0a2a14..b523067 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -66,7 +66,8 @@ "admins": "مدیران", "shipment_methods": "روش های ارسال", "reviews": "نظرات", - "pagers": "پیجرها" + "pagers": "پیجرها", + "notifications": "تنظیمات اعلانات" }, "shipment": { "dineIn": "سرو در محل", diff --git a/src/pages/notifications/List.tsx b/src/pages/notifications/List.tsx new file mode 100644 index 0000000..cda4b4e --- /dev/null +++ b/src/pages/notifications/List.tsx @@ -0,0 +1,57 @@ +import { type FC } from 'react' +import Table from '@/components/Table' +import Filters from '@/components/Filters' +import { useGetNotifications } from './hooks/useNotificationData' +import { useNotificationFilters } from './hooks/useNotificationFilters' +import { getNotificationTableColumns } from './components/NotificationTableColumns' +import { useNotificationFiltersFields } from './components/NotificationFiltersFields' +import type { Notification } from './types/Types' + +const NotificationsList: FC = () => { + const { + filters, + currentPage, + apiParams, + handleFiltersChange, + handlePageChange, + limit, + } = useNotificationFilters() + + const { data: notificationsData, isLoading } = useGetNotifications(apiParams) + + const notifications = notificationsData?.data || [] + const columns = getNotificationTableColumns() + const filterFields = useNotificationFiltersFields() + + const totalPages = Math.ceil(notifications.length / limit) || 1 + + return ( +
+
+

لیست اعلانات

+
+ +
+ +
+ + + columns={columns} + data={notifications as Notification[]} + isloading={isLoading} + pagination={{ + currentPage, + totalPages, + onPageChange: handlePageChange, + }} + /> +
+ ) +} + +export default NotificationsList \ No newline at end of file diff --git a/src/pages/notifications/components/NotificationFiltersFields.tsx b/src/pages/notifications/components/NotificationFiltersFields.tsx new file mode 100644 index 0000000..6e6cc0d --- /dev/null +++ b/src/pages/notifications/components/NotificationFiltersFields.tsx @@ -0,0 +1,22 @@ +import { useMemo } from 'react' +import type { FieldType } from '@/components/Filters' + +export const useNotificationFiltersFields = (): FieldType[] => { + return useMemo(() => [ + { + type: 'input', + name: 'search', + placeholder: 'جستجو (عنوان یا رستوران)', + }, + { + type: 'select', + name: 'notificationType', + placeholder: 'نوع اعلان', + options: [ + { value: 'sms', label: 'پیامک' }, + { value: 'push', label: 'پوش' }, + { value: 'both', label: 'هر دو' }, + ], + }, + ], []) +} diff --git a/src/pages/notifications/components/NotificationTableColumns.tsx b/src/pages/notifications/components/NotificationTableColumns.tsx new file mode 100644 index 0000000..a1f6308 --- /dev/null +++ b/src/pages/notifications/components/NotificationTableColumns.tsx @@ -0,0 +1,69 @@ +import { Eye } from 'iconsax-react' +import type { ColumnType } from '@/components/types/TableTypes' +import type { Notification } from '../types/Types' +import { formatOptionalDate } from '@/helpers/func' +import Status from '@/components/Status' + +const getNotificationTypeVariant = (type: string): 'success' | 'error' | 'warning' | 'info' | 'pending' => { + const variantMap: Record = { + 'sms': 'info', + 'push': 'warning', + 'both': 'success', + } + return variantMap[type] || 'info' +} + +export const getNotificationTableColumns = (): ColumnType[] => { + return [ + { + key: 'title', + title: 'عنوان', + render: (item: Notification) => { + return ( +
+ {item.title || '-'} +
+ ) + } + }, + { + key: 'notificationType', + title: 'نوع اعلان', + render: (item: Notification) => { + const typeLabels: Record = { + 'sms': 'پیامک', + 'push': 'پوش', + 'both': 'هر دو', + } + return ( + + ) + } + }, + { + key: 'createdAt', + title: 'تاریخ ثبت', + render: (item: Notification) => { + return formatOptionalDate(item.createdAt, { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + }) + } + }, + { + key: 'details', + title: '', + render: () => { + return ( + + ) + } + }, + ] +} diff --git a/src/pages/notifications/hooks/useNotificationData.ts b/src/pages/notifications/hooks/useNotificationData.ts new file mode 100644 index 0000000..b63fb8e --- /dev/null +++ b/src/pages/notifications/hooks/useNotificationData.ts @@ -0,0 +1,11 @@ +import * as api from "../service/NotificationService"; +import { useQuery } from "@tanstack/react-query"; + +export const useGetNotifications = ( + params?: Record +) => { + return useQuery({ + queryKey: ["notifications", params], + queryFn: () => api.getNotifications(params), + }); +}; diff --git a/src/pages/notifications/hooks/useNotificationFilters.ts b/src/pages/notifications/hooks/useNotificationFilters.ts new file mode 100644 index 0000000..65b9371 --- /dev/null +++ b/src/pages/notifications/hooks/useNotificationFilters.ts @@ -0,0 +1,46 @@ +import { useState, useMemo } from "react"; +import type { FilterValues } from "@/components/Filters"; + +const DEFAULT_PAGE = 1; +const DEFAULT_LIMIT = 10; + +export const useNotificationFilters = () => { + const [filters, setFilters] = useState({}); + const [currentPage, setCurrentPage] = useState(DEFAULT_PAGE); + const [limit] = useState(DEFAULT_LIMIT); + + const apiParams = useMemo(() => { + const params: Record = { + page: currentPage, + limit: limit, + }; + + if (filters.search) { + params.search = filters.search as string; + } + + if (filters.notificationType) { + params.notificationType = filters.notificationType as string; + } + + 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/notifications/service/NotificationService.ts b/src/pages/notifications/service/NotificationService.ts new file mode 100644 index 0000000..40e6cb1 --- /dev/null +++ b/src/pages/notifications/service/NotificationService.ts @@ -0,0 +1,12 @@ +import axios from "@/config/axios"; +import type { GetNotificationsResponse } from "../types/Types"; + +export const getNotifications = async ( + params?: Record +): Promise => { + const { data } = await axios.get( + "/admin/notification-preferences", + { params } + ); + return data; +}; diff --git a/src/pages/notifications/types/Types.ts b/src/pages/notifications/types/Types.ts new file mode 100644 index 0000000..6fad2fd --- /dev/null +++ b/src/pages/notifications/types/Types.ts @@ -0,0 +1,14 @@ +import type { IResponse } from "@/types/response.types"; +import type { RowDataType } from "@/components/types/TableTypes"; + +export interface Notification extends RowDataType { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + restaurant: string; + title: string; + notificationType: "sms" | "push" | "both"; +} + +export type GetNotificationsResponse = IResponse; diff --git a/src/router/Main.tsx b/src/router/Main.tsx index e4f741f..6009e76 100644 --- a/src/router/Main.tsx +++ b/src/router/Main.tsx @@ -39,6 +39,7 @@ import UpdateCoupon from '@/pages/coupon/Update' import ReviewsList from '@/pages/review/List' import DetailReview from '@/pages/review/Detail' import PagersList from '@/pages/pager/List' +import NotificationsList from '@/pages/notifications/List' const MainRouter: FC = () => { const { hasSubMenu } = useSharedStore() @@ -95,6 +96,8 @@ const MainRouter: FC = () => { } /> } /> + + } /> diff --git a/src/shared/SideBar.tsx b/src/shared/SideBar.tsx index 4de094a..c09175a 100644 --- a/src/shared/SideBar.tsx +++ b/src/shared/SideBar.tsx @@ -173,6 +173,15 @@ const SideBar: FC = () => { name='roles' activeName='roles' /> + + } + title={t('sidebar.notifications')} + isActive={isActive('notifications')} + link={Pages.notifications.list} + name='notifications' + activeName='notifications' + />