diff --git a/src/helpers/func.ts b/src/helpers/func.ts
index c3f4700..8d890a8 100644
--- a/src/helpers/func.ts
+++ b/src/helpers/func.ts
@@ -25,3 +25,31 @@ export const formatDateShort = (dateString: string): string => {
return dateString;
}
};
+
+export const formatOptionalDate = (
+ value?: string | null,
+ options?: Intl.DateTimeFormatOptions
+): string => {
+ if (!value) {
+ return "-";
+ }
+ const date = new Date(value);
+ if (Number.isNaN(date.getTime())) {
+ return "-";
+ }
+ return new Intl.DateTimeFormat(
+ "fa-IR",
+ options ?? {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ }
+ ).format(date);
+};
+
+export const formatFaNumber = (value?: number | null): string => {
+ if (value === undefined || value === null || Number.isNaN(value)) {
+ return "0";
+ }
+ return value.toLocaleString("fa-IR");
+};
diff --git a/src/pages/admin/components/AdminTableColumns.tsx b/src/pages/admin/components/AdminTableColumns.tsx
index 81b9676..096edf4 100644
--- a/src/pages/admin/components/AdminTableColumns.tsx
+++ b/src/pages/admin/components/AdminTableColumns.tsx
@@ -4,6 +4,7 @@ import TrashWithConfrim from '@/components/TrashWithConfrim'
import { Eye } from 'iconsax-react'
import { Link } from 'react-router-dom'
import { Pages } from '@/config/Pages'
+import { formatOptionalDate } from '@/helpers/func'
interface GetAdminTableColumnsParams {
onDelete?: (id: string) => void
@@ -44,7 +45,7 @@ export const getAdminTableColumns = ({ onDelete, isDeleting }: GetAdminTableColu
key: 'createdAt',
title: 'تاریخ ایجاد',
render: (item: Admin) => {
- return new Date(item.createdAt).toLocaleDateString('fa-IR')
+ return formatOptionalDate(item.createdAt)
}
},
{
diff --git a/src/pages/comments/List.tsx b/src/pages/comments/List.tsx
index abf5197..8358e57 100644
--- a/src/pages/comments/List.tsx
+++ b/src/pages/comments/List.tsx
@@ -7,7 +7,7 @@ import type { RowDataType, ActionType } from '@/components/types/TableTypes'
import type { FilterValues } from '@/components/Filters'
import { Link } from 'react-router-dom'
import { Pages } from '@/config/Pages'
-import moment from 'moment-jalaali'
+import { formatOptionalDate } from '@/helpers/func'
interface CommentData extends RowDataType {
id: number
@@ -49,15 +49,6 @@ const CommentsList: FC = () => {
]
}
- const formatDate = (dateString: string): string => {
- try {
- const jalaliDate = moment(dateString, 'YYYY-MM-DD').format('jYYYY/jMM/jDD')
- return `${jalaliDate}`
- } catch {
- return dateString
- }
- }
-
const handleDisplayToggle = (item: CommentData, value: boolean) => {
console.log('تغییر وضعیت نمایش کامنت:', item.id, value)
}
@@ -87,7 +78,7 @@ const CommentsList: FC = () => {
key: 'date',
title: 'تاریخ',
render: (item: CommentData) => {
- return {formatDate(item.date)}
+ return {formatOptionalDate(item.date)}
},
},
{
diff --git a/src/pages/customers/List.tsx b/src/pages/customers/List.tsx
index fb96277..8927d12 100644
--- a/src/pages/customers/List.tsx
+++ b/src/pages/customers/List.tsx
@@ -1,205 +1,168 @@
import Button from '@/components/Button'
import Table from '@/components/Table'
-import Filters from '@/components/Filters'
+import Filters, { type FilterValues, type FieldType } from '@/components/Filters'
import { Add, Eye } from 'iconsax-react'
-import { type FC, useState } from 'react'
-import type { RowDataType, ActionType } from '@/components/types/TableTypes'
-import type { FilterValues } from '@/components/Filters'
+import { type FC, useState, useMemo, useCallback } from 'react'
import { Pages } from '@/config/Pages'
import { Link } from 'react-router-dom'
+import { useGetUsers } from './hooks/useUsersData'
+import type { Customer, CustomerListRow } from '@/pages/customers/types/Types'
+import { formatOptionalDate, formatFaNumber } from '@/helpers/func'
-interface CustomerData extends RowDataType {
- id: number
- name: string
- phone: string
- email: string
- customerCode: string
- ordersCount: number
- ticketsCount: number
+const mapCustomerToRow = (customer: Customer): CustomerListRow => {
+ const fullName = [customer.firstName, customer.lastName].filter(Boolean).join(' ').trim()
+ return {
+ id: customer.id,
+ fullName: fullName || 'بدون نام',
+ phone: customer.phone || '-',
+ birthDate: formatOptionalDate(customer.birthDate),
+ marriageDate: formatOptionalDate(customer.marriageDate),
+ wallet: formatFaNumber(customer.wallet),
+ points: formatFaNumber(customer.points),
+ status: customer.isActive ? 'فعال' : 'غیرفعال',
+ }
}
const CustomersList: FC = () => {
+ const { data: usersData, isLoading } = useGetUsers()
+ const [filters, setFilters] = useState({ search: '', status: 'all' })
const [currentPage, setCurrentPage] = useState(1)
- const totalPages = 10
+ const limit = 10
- const handlePageChange = (page: number) => {
- setCurrentPage(page)
- }
-
- const handleFiltersChange = (newFilters: FilterValues) => {
- // اینجا میتوانید API call جدید برای فیلتر کردن دادهها بزنید
- console.log('فیلترها:', newFilters)
- }
-
- const handleRowActions = (item: CustomerData): ActionType[] => {
- return [
- {
- label: 'ویرایش',
- onClick: () => {
- console.log('ویرایش مشتری:', item.id)
- },
- },
- {
- label: 'حذف',
- onClick: () => {
- console.log('حذف مشتری:', item.id)
- },
- },
- ]
- }
-
- const columns = [
+ const filterFields = useMemo(() => [
{
- key: 'name',
- title: 'نام',
+ type: 'input',
+ name: 'search',
+ placeholder: 'نام یا شماره تماس',
+ },
+ {
+ type: 'select',
+ name: 'status',
+ placeholder: 'انتخاب وضعیت',
+ defaultValue: 'all',
+ options: [
+ { label: 'همه وضعیتها', value: 'all' },
+ { label: 'فعال', value: 'active' },
+ { label: 'غیرفعال', value: 'inactive' },
+ ],
+ },
+ ], [])
+
+ const customers = useMemo(() => usersData?.data ?? [], [usersData])
+
+ const filteredCustomers = useMemo(() => {
+ const searchValue = (filters.search || '').toLowerCase().trim()
+ const statusFilter = filters.status || 'all'
+
+ return customers.filter((customer) => {
+ const fullName = [customer.firstName, customer.lastName].filter(Boolean).join(' ').toLowerCase().trim()
+ const matchesSearch =
+ searchValue === '' ||
+ fullName.includes(searchValue) ||
+ (customer.phone || '').includes(searchValue)
+
+ const matchesStatus =
+ statusFilter === 'all' ||
+ (statusFilter === 'active' && customer.isActive) ||
+ (statusFilter === 'inactive' && !customer.isActive)
+
+ return matchesSearch && matchesStatus
+ })
+ }, [customers, filters])
+
+ const totalPages = Math.max(1, Math.ceil(filteredCustomers.length / limit) || 1)
+
+ const paginatedCustomers = useMemo(() => {
+ const startIndex = (currentPage - 1) * limit
+ return filteredCustomers.slice(startIndex, startIndex + limit)
+ }, [filteredCustomers, currentPage, limit])
+
+ const tableData = useMemo(() => paginatedCustomers.map(mapCustomerToRow), [paginatedCustomers])
+
+ const columns = useMemo(() => [
+ {
+ key: 'fullName',
+ title: 'نام و نام خانوادگی',
},
{
key: 'phone',
title: 'شماره تماس',
},
{
- key: 'email',
- title: 'ایمیل',
+ key: 'birthDate',
+ title: 'تاریخ تولد',
},
{
- key: 'customerCode',
- title: 'کد مشتری',
+ key: 'marriageDate',
+ title: 'تاریخ ازدواج',
},
{
- key: 'ordersCount',
- title: 'سفارشات',
- render: (item: CustomerData) => {
- return (
-
- )
- },
+ key: 'wallet',
+ title: 'کیف پول (تومان)',
},
{
- key: 'ticketsCount',
- title: 'تیکتها',
- render: (item: CustomerData) => {
- return (
-
- )
- },
+ key: 'points',
+ title: 'امتیاز',
+ },
+ {
+ key: 'status',
+ title: 'وضعیت',
+ render: (item: CustomerListRow) => (
+
+ {item.status}
+
+ ),
},
{
key: 'details',
- title: 'جزییات',
- render: (item: CustomerData) => {
- return (
-
-
-
- )
- },
+ title: 'جزئیات',
+ render: (item: CustomerListRow) => (
+
+
+
+ ),
},
- ]
+ ], [])
- const data: CustomerData[] = [
- {
- id: 1,
- name: 'مهرداد مظفری',
- phone: '۰۹۱۲۹۲۸۳۳۹۵',
- email: 'info@example.com',
- customerCode: '۱۲۲',
- ordersCount: 4,
- ticketsCount: 4,
- },
- {
- id: 2,
- name: 'مهرداد مظفری',
- phone: '۰۹۱۲۹۲۸۳۳۹۵',
- email: 'info@example.com',
- customerCode: '۱۲۲',
- ordersCount: 4,
- ticketsCount: 4,
- },
- {
- id: 3,
- name: 'مهرداد مظفری',
- phone: '۰۹۱۲۹۲۸۳۳۹۵',
- email: 'info@example.com',
- customerCode: '۱۲۲',
- ordersCount: 4,
- ticketsCount: 4,
- },
- {
- id: 4,
- name: 'مهرداد مظفری',
- phone: '۰۹۱۲۹۲۸۳۳۹۵',
- email: 'info@example.com',
- customerCode: '۱۲۲',
- ordersCount: 4,
- ticketsCount: 4,
- },
- {
- id: 5,
- name: 'مهرداد مظفری',
- phone: '۰۹۱۲۹۲۸۳۳۹۵',
- email: 'info@example.com',
- customerCode: '۱۲۲',
- ordersCount: 4,
- ticketsCount: 4,
- },
- {
- id: 6,
- name: 'مهرداد مظفری',
- phone: '۰۹۱۲۹۲۸۳۳۹۵',
- email: 'info@example.com',
- customerCode: '۱۲۲',
- ordersCount: 4,
- ticketsCount: 4,
- },
- ]
+ const handlePageChange = (page: number) => {
+ setCurrentPage(page)
+ }
+
+ const handleFiltersChange = useCallback((newFilters: FilterValues) => {
+ setFilters(newFilters)
+ setCurrentPage(1)
+ }, [])
return (
لیست مشتریان
-
+
+
+
{
+ return useQuery({
+ queryKey: ["users"],
+ queryFn: api.getUsers,
+ });
+};
diff --git a/src/pages/customers/service/UsersService.ts b/src/pages/customers/service/UsersService.ts
new file mode 100644
index 0000000..de42b17
--- /dev/null
+++ b/src/pages/customers/service/UsersService.ts
@@ -0,0 +1,7 @@
+import axios from "@/config/axios";
+import type { GetCustomersResponse } from "@/pages/customers/types/Types";
+
+export const getUsers = async () => {
+ const { data } = await axios.get(`/admin/users`);
+ return data;
+};
diff --git a/src/pages/customers/types/Types.ts b/src/pages/customers/types/Types.ts
new file mode 100644
index 0000000..040f893
--- /dev/null
+++ b/src/pages/customers/types/Types.ts
@@ -0,0 +1,32 @@
+import type { IResponse } from "@/types/response.types";
+import type { RowDataType } from "@/components/types/TableTypes";
+
+export interface Customer {
+ id: string;
+ createdAt: string;
+ updatedAt: string;
+ deletedAt: string | null;
+ restaurant: string;
+ firstName: string;
+ lastName: string;
+ phone: string;
+ birthDate: string;
+ marriageDate: string;
+ referrer: string | null;
+ isActive: boolean;
+ gender: boolean;
+ wallet: number;
+ points: number;
+}
+
+export type GetCustomersResponse = IResponse;
+
+export type CustomerListRow = RowDataType & {
+ fullName: string;
+ phone: string;
+ birthDate: string;
+ marriageDate: string;
+ wallet: string;
+ points: string;
+ status: string;
+};
diff --git a/src/pages/roles/components/RoleTableColumns.tsx b/src/pages/roles/components/RoleTableColumns.tsx
index 17ed69e..8a7b08e 100644
--- a/src/pages/roles/components/RoleTableColumns.tsx
+++ b/src/pages/roles/components/RoleTableColumns.tsx
@@ -4,6 +4,7 @@ import TrashWithConfrim from '@/components/TrashWithConfrim'
import { Eye } from 'iconsax-react'
import { Link } from 'react-router-dom'
import { Pages } from '@/config/Pages'
+import { formatOptionalDate } from '@/helpers/func'
interface GetRoleTableColumnsParams {
onDelete?: (id: string) => void
@@ -27,7 +28,7 @@ export const getRoleTableColumns = ({ onDelete, isDeleting }: GetRoleTableColumn
key: 'createdAt',
title: 'تاریخ ایجاد',
render: (item: RoleType) => {
- return new Date(item.createdAt).toLocaleDateString('fa-IR')
+ return formatOptionalDate(item.createdAt)
}
},
{
diff --git a/src/pages/schedule/components/ScheduleTableColumns.tsx b/src/pages/schedule/components/ScheduleTableColumns.tsx
index f67ac9b..b6604de 100644
--- a/src/pages/schedule/components/ScheduleTableColumns.tsx
+++ b/src/pages/schedule/components/ScheduleTableColumns.tsx
@@ -6,6 +6,7 @@ import { Eye } from 'iconsax-react'
import { Link } from 'react-router-dom'
import { Pages } from '@/config/Pages'
import type { TFunction } from 'i18next'
+import { formatOptionalDate } from '@/helpers/func'
interface GetScheduleTableColumnsParams {
onDelete?: (id: string) => void
@@ -43,7 +44,7 @@ export const getScheduleTableColumns = ({ onDelete, isDeleting, t }: GetSchedule
key: 'createdAt',
title: 'تاریخ ایجاد',
render: (item: Schedule) => {
- return new Date(item.createdAt).toLocaleDateString('fa-IR')
+ return formatOptionalDate(item.createdAt)
}
},
{
diff --git a/src/pages/shipmentMethod/components/ShipmentMethodTableColumns.tsx b/src/pages/shipmentMethod/components/ShipmentMethodTableColumns.tsx
index 88daebe..c446b98 100644
--- a/src/pages/shipmentMethod/components/ShipmentMethodTableColumns.tsx
+++ b/src/pages/shipmentMethod/components/ShipmentMethodTableColumns.tsx
@@ -5,6 +5,7 @@ import { Eye } from 'iconsax-react'
import { Link } from 'react-router-dom'
import { Pages } from '@/config/Pages'
import Status from '@/components/Status'
+import { formatFaNumber } from '@/helpers/func'
interface GetShipmentMethodTableColumnsParams {
onDelete?: (id: string) => void
@@ -31,14 +32,14 @@ export const getShipmentMethodTableColumns = ({ onDelete, isDeleting }: GetShipm
key: 'price',
title: 'قیمت',
render: (item: RestaurantShipmentMethod) => {
- return item.price ? `${item.price.toLocaleString()} تومان` : 'رایگان'
+ return item.price ? `${formatFaNumber(item.price)} تومان` : 'رایگان'
}
},
{
key: 'minOrderPrice',
title: 'حداقل قیمت سفارش',
render: (item: RestaurantShipmentMethod) => {
- return item.minOrderPrice ? `${item.minOrderPrice.toLocaleString()} تومان` : '-'
+ return item.minOrderPrice ? `${formatFaNumber(item.minOrderPrice)} تومان` : '-'
}
},
{