import React, { useMemo, useState } from 'react'; import { englishToPersian, persianToEnglish, formatWithThousands, parseFormattedNumber } from '@/utils/numberUtils'; import { useNavigate } from 'react-router-dom'; import { useOrders, useOrderStats, useUpdateOrderStatus } from '../core/_hooks'; import { OrderFilters, OrderStatus } from '../core/_models'; import { Button } from "@/components/ui/Button"; import { Modal } from "@/components/ui/Modal"; import { Pagination } from "@/components/ui/Pagination"; import { PageContainer, PageTitle } from "@/components/ui/Typography"; import { Table } from "@/components/ui/Table"; import { TableColumn } from "@/types"; import { StatsCard } from '@/components/dashboard/StatsCard'; import DatePicker from 'react-multi-date-picker'; import persian from 'react-date-object/calendars/persian'; import persian_fa from 'react-date-object/locales/persian_fa'; import DateObject from 'react-date-object'; import { ShoppingCart, DollarSign, Clock, Search, Filter, Eye, Edit3, TrendingUp } from 'lucide-react'; const getStatusColor = (status: OrderStatus) => { const colors = { pending: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200', processing: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200', shipped: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200', delivered: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200', cancelled: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200', refunded: 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200', }; return colors[status] || colors.pending; }; const getStatusText = (status: OrderStatus) => { const text = { pending: 'در انتظار', processing: 'در حال پردازش', shipped: 'ارسال شده', delivered: 'تحویل شده', cancelled: 'لغو شده', refunded: 'مرجوع شده', }; return text[status] || status; }; const formatCurrency = (amount: number) => { return new Intl.NumberFormat('fa-IR').format(amount) + ' تومان'; }; const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('fa-IR'); }; const ListSkeleton = () => ( ); const getDefaultFilters = (): OrderFilters => ({ page: 1, limit: 20, status: 'pending', payment_status: undefined, search: '', user_id: undefined, invoice_id: undefined, discount_code: undefined, created_from: undefined, created_to: undefined, updated_from: undefined, updated_to: undefined, min_total: undefined, max_total: undefined, }); const toIsoDate = (date?: DateObject | null) => { if (!date) return undefined; try { const g = date.convert(undefined); const yyyy = g.year.toString().padStart(4, '0'); const mm = g.month.toString().padStart(2, '0'); const dd = g.day.toString().padStart(2, '0'); return `${yyyy}-${mm}-${dd}`; } catch { return undefined; } }; const fromIsoDate = (value?: string) => { if (!value) return undefined; try { const d = new Date(value); if (isNaN(d.getTime())) return undefined; return new DateObject(d).convert(persian, persian_fa); } catch { return undefined; } }; const buildRangeValue = (from?: string, to?: string) => { const start = fromIsoDate(from); const end = fromIsoDate(to); if (start && end) return [start, end]; if (start) return [start]; if (end) return [end]; return []; }; const OrdersListPage = () => { const navigate = useNavigate(); const [statusUpdateId, setStatusUpdateId] = useState(null); const [newStatus, setNewStatus] = useState('processing'); const [filters, setFilters] = useState(getDefaultFilters()); const { data: ordersData, isLoading, error } = useOrders(filters); const { data: stats, isLoading: statsLoading, error: statsError } = useOrderStats(true); const { mutate: updateStatus, isPending: isUpdating } = useUpdateOrderStatus(); const handleIdFilterChange = (key: keyof OrderFilters, raw: string) => { const converted = persianToEnglish(raw).replace(/[^\d]/g, ''); const numeric = converted ? Number(converted) : undefined; setFilters(prev => ({ ...prev, [key]: numeric, page: 1, })); }; const handleTextFilterChange = (key: keyof OrderFilters, value: string) => { setFilters(prev => ({ ...prev, [key]: value || undefined, page: 1, })); }; const formatNumberDisplay = (val?: number) => { if (val === undefined || val === null || Number.isNaN(val)) return ''; return formatWithThousands(val); }; const handleAmountFilterChange = (key: keyof OrderFilters, raw: string) => { const converted = persianToEnglish(raw); const numeric = parseFormattedNumber(converted); setFilters(prev => ({ ...prev, [key]: numeric, page: 1, })); }; const handleDateRangeChange = (startKey: keyof OrderFilters, endKey: keyof OrderFilters, range: (DateObject | null)[] | DateObject | null) => { if (Array.isArray(range)) { const [start, end] = range; setFilters(prev => ({ ...prev, [startKey]: toIsoDate(start), [endKey]: toIsoDate(end), page: 1, })); return; } setFilters(prev => ({ ...prev, [startKey]: toIsoDate(range as DateObject | null), [endKey]: undefined, page: 1, })); }; const statsItems = useMemo(() => ([ { title: 'کل سفارشات', value: stats?.total_orders_count ?? 0, icon: ShoppingCart, color: 'yellow' as const, }, { title: 'مجموع فروش', value: stats?.total_amount_of_sale ?? 0, icon: DollarSign, color: 'green' as const, }, { title: 'سفارش‌های در انتظار', value: stats?.total_order_pending ?? 0, icon: Clock, color: 'blue' as const, }, { title: 'میانگین سفارش', value: stats?.order_avg ?? 0, icon: TrendingUp, color: 'purple' as const, }, ]), [stats]); const columns: TableColumn[] = useMemo(() => [ { key: 'order_number', label: 'شماره سفارش', sortable: true, align: 'right', render: (v: string) => `#${v}` }, { key: 'customer', label: 'مشتری', align: 'right', render: (_val, row: any) => (
{(row.user?.first_name || row.customer?.first_name || 'نامشخص')} {(row.user?.last_name || row.customer?.last_name || '')}
{row.user?.phone_number ? englishToPersian(row.user.phone_number) : '-'}
) }, { key: 'final_total', label: 'مبلغ نهایی', sortable: true, align: 'right', render: (v: number, row: any) => formatCurrency(row.final_total || row.total_amount || 0) }, { key: 'status', label: 'وضعیت', align: 'right', render: (v: OrderStatus) => ({getStatusText(v)}) }, { key: 'created_at', label: 'تاریخ', sortable: true, align: 'right', render: (v: string) => formatDate(v) }, { key: 'actions', label: 'عملیات', align: 'right', render: (_val, row: any) => (
) }, ], []); const handleStatusUpdate = () => { if (statusUpdateId) { updateStatus( { id: statusUpdateId, payload: { status: newStatus } }, { onSuccess: () => setStatusUpdateId(null) } ); } }; const handleViewOrder = (id: number) => { navigate(`/orders/${id}`); }; const handleUpdateStatus = (id: number, currentStatus: OrderStatus) => { setStatusUpdateId(id.toString()); setNewStatus(currentStatus); }; const handlePageChange = (page: number) => { setFilters(prev => ({ ...prev, page })); }; if (error) { return (

خطا در بارگذاری سفارشات

); } return (
مدیریت سفارشات

{ordersData?.total || 0} سفارش یافت شد

{statsLoading ? ( <> {[...Array(4)].map((_, idx) => (
))} ) : ( statsItems.map((stat, index) => ( )) )}
{statsError && (
خطا در دریافت آمار سفارشات
)} {/* فیلترها */}
setFilters(prev => ({ ...prev, search: e.target.value, page: 1 }))} className="w-full pr-10 px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary-500 dark:bg-gray-700 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-300" />
handleIdFilterChange('user_id', e.target.value)} placeholder="مثلاً ۱۰۲۴" className="w-full px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary-500 dark:bg-gray-700 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400" />
handleTextFilterChange('discount_code', e.target.value)} placeholder="مثلاً SPRING2025" className="w-full px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary-500 dark:bg-gray-700 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400" />
handleIdFilterChange('invoice_id', e.target.value)} placeholder="مثلاً ۱۲۳۴۵" className="w-full px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary-500 dark:bg-gray-700 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400" />
handleAmountFilterChange('min_total', e.target.value)} placeholder="مثلاً ۳,۰۰۰,۰۰۰" className="w-full px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary-500 dark:bg-gray-700 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400" />
handleAmountFilterChange('max_total', e.target.value)} placeholder="مثلاً ۹,۰۰۰,۰۰۰" className="w-full px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary-500 dark:bg-gray-700 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400" />
handleDateRangeChange('created_from', 'created_to', range as any)} format="YYYY/MM/DD" range calendar={persian} locale={persian_fa} calendarPosition="bottom-center" inputClass="w-full px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-500" containerClassName="w-full" editable={false} placeholder="از تاریخ / تا تاریخ" />
handleDateRangeChange('updated_from', 'updated_to', range as any)} format="YYYY/MM/DD" range calendar={persian} locale={persian_fa} calendarPosition="bottom-center" inputClass="w-full px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-500" containerClassName="w-full" editable={false} placeholder="از تاریخ / تا تاریخ" />
{/* جدول سفارشات */} {isLoading ? ( ) : !ordersData?.orders || ordersData.orders.length === 0 ? (

هیچ سفارشی یافت نشد

با تغییر فیلترها جستجو کنید

) : ( <>
)} {/* مودال تغییر وضعیت */} setStatusUpdateId(null)} title="تغییر وضعیت سفارش">
); }; export default OrdersListPage;