import { useState, useEffect } from 'react'; import { ShoppingCart, Eye, Search, X, Printer, CheckCircle2, Clock, Truck, XCircle, MapPin, User, Phone, Mail, Package, CreditCard, Save, Download } from 'lucide-react'; import api from '../services/api'; import Skeleton from '../components/ui/Skeleton'; import Spinner from '../components/ui/Spinner'; import Pagination from '../components/ui/Pagination'; const statusStyles: Record = { pending: { label: 'در حال بررسی', color: 'bg-orange-100 text-orange-700 border-orange-200', icon: Clock }, processing: { label: 'در حال پردازش', color: 'bg-amber-100 text-amber-800 border-amber-200', icon: Clock }, shipped: { label: 'ارسال شده', color: 'bg-blue-100 text-blue-700 border-blue-200', icon: Truck }, delivered: { label: 'تحویل داده شده', color: 'bg-green-100 text-green-700 border-green-200', icon: CheckCircle2 }, cancelled: { label: 'لغو شده', color: 'bg-red-100 text-red-700 border-red-200', icon: XCircle }, }; const toPersianDigits = (n: string | number): string => { if (n === null || n === undefined) return ''; const farsiDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; return n.toString().replace(/\d/g, (x) => farsiDigits[parseInt(x)]); }; export default function Orders() { const [orders, setOrders] = useState([]); const [isLoading, setIsLoading] = useState(true); // Queries const [page, setPage] = useState(1); const [search, setSearch] = useState(''); const [status, setStatus] = useState(''); const [totalPages, setTotalPages] = useState(1); const [processingId, setProcessingId] = useState(null); // Modal State const [selectedOrder, setSelectedOrder] = useState(null); const [modalTrackingCode, setModalTrackingCode] = useState(''); const [modalStatus, setModalStatus] = useState(''); const [isSavingTracking, setIsSavingTracking] = useState(false); const fetchOrders = async () => { try { setIsLoading(true); const params = new URLSearchParams(); params.append('page', page.toString()); if (search) params.append('search', search); if (status) params.append('status', status); const response = await api.get(`/admin/orders?${params.toString()}`); if (response.data?.success) { setOrders(response.data.data); setTotalPages(response.data.meta?.lastPage || 1); // Update selectedOrder if open if (selectedOrder) { const updated = response.data.data.find((o: any) => o.id === selectedOrder.id); if (updated) { setSelectedOrder(updated); } } } } catch (err) { console.error('Failed to fetch orders', err); } finally { setIsLoading(false); } }; useEffect(() => { const delayDebounceFn = setTimeout(() => { fetchOrders(); }, 500); return () => clearTimeout(delayDebounceFn); }, [page, search, status]); const handleUpdateStatus = async (id: string, newStatus: string, trackingNumber?: string) => { try { setProcessingId(id); await api.put(`/admin/orders/${id}/status`, { status: newStatus, trackingNumber: trackingNumber !== undefined ? trackingNumber : undefined }); await fetchOrders(); } catch (err) { console.error('Failed to update status', err); } finally { setProcessingId(null); } }; const openOrderModal = (order: any) => { setSelectedOrder(order); setModalTrackingCode(order.trackingNumber || ''); setModalStatus(order.status || 'processing'); }; const handleSaveModalChanges = async () => { if (!selectedOrder) return; try { setIsSavingTracking(true); await api.put(`/admin/orders/${selectedOrder.id}/status`, { status: modalStatus, trackingNumber: modalTrackingCode }); await fetchOrders(); } catch (err) { console.error('Failed to save tracking number', err); } finally { setIsSavingTracking(false); } }; const handlePrintInvoice = (orderToPrint: any) => { if (!orderToPrint) return; const printWindow = window.open('', '_blank'); if (!printWindow) return; const orderDate = new Date(orderToPrint.createdAt || orderToPrint.date); const formattedDate = toPersianDigits(orderDate.toLocaleDateString("fa-IR")); const formattedTime = toPersianDigits(orderDate.toLocaleTimeString("fa-IR", { hour: '2-digit', minute: '2-digit' })); const trackingCode = orderToPrint.trackingNumber || orderToPrint.id.substring(0, 8); const items = orderToPrint.orderItems || orderToPrint.items || []; const itemsSubtotal = items.reduce((sum: number, item: any) => { const price = Number(item.product?.priceValue || item.priceValue || 0); return sum + price * (item.quantity || 1); }, 0); const charityAmount = Number(orderToPrint.charityDonation || 0); const totalAmount = Number(orderToPrint.totalAmount || orderToPrint.total || itemsSubtotal + charityAmount); const isRefill = Boolean(orderToPrint.isRefill); const refillDiscount = isRefill ? Math.round(itemsSubtotal * 0.05) : 0; const customerName = orderToPrint.user ? `${orderToPrint.user.firstName || ''} ${orderToPrint.user.lastName || ''}`.trim() : 'مشتری مهمان'; const paymentMethodText = orderToPrint.paymentMethod === 'wallet' ? 'پرداخت از کیف پول الکترونیک' : 'پرداخت آنلاین از درگاه شتاب'; const itemsHtml = items.map((item: any) => { const pName = item.product?.nameFa || item.product?.name || item.name || 'محصول کانینا'; const pImg = item.product?.imageUrl || item.product?.image || '/products/product-placeholder.png'; const pPrice = Number(item.product?.priceValue || item.priceValue || 0); const rowTotal = pPrice * (item.quantity || 1); return `
${pName} ${pName}
${toPersianDigits(item.quantity)} ${toPersianDigits(pPrice.toLocaleString())} تومان ${toPersianDigits(rowTotal.toLocaleString())} تومان `; }).join(''); printWindow.document.write(` فاکتور رسمی کانینا - ${trackingCode}
CANINA | کانینا
کد پیگیری: ${toPersianDigits(trackingCode)}
تاریخ: ${formattedDate} - ساعت ${formattedTime}

نام خریدار: ${customerName}

گیرنده و آدرس: ${orderToPrint.shippingAddress || "ثبت‌شده در حساب کاربری"}

روش پرداخت: ${paymentMethodText}

تلفن خریدار: ${toPersianDigits(orderToPrint.user?.phone || 'ثبت نشده')}

وضعیت سفارش: ${statusStyles[orderToPrint.status]?.label || orderToPrint.status}

نوع سفارش: ${isRefill ? 'تمدید خودکار (Refill)' : 'عادی'}

${itemsHtml}
شرح محصول تعداد قیمت واحد مجموع
مجموع اولیه اقلام: ${toPersianDigits(itemsSubtotal.toLocaleString())} تومان
${isRefill ? `
تخفیف تمدید خودکار (۵٪): ${toPersianDigits(refillDiscount.toLocaleString())}- تومان
` : ''} ${charityAmount > 0 ? `
کمک اهدایی (ردپای مهربانی): ${toPersianDigits(charityAmount.toLocaleString())}+ تومان
` : ''}
هزینه ارسال: رایگان
مبلغ نهایی پرداخت شده: ${toPersianDigits(totalAmount.toLocaleString())} تومان
`); printWindow.document.close(); }; return (
{/* Header */}

مدیریت سفارشات

مشاهده فاکتورها، تغییر وضعیت، ثبت کد رهگیری پستی و چاپ فاکتور رسمی

{ setSearch(e.target.value); setPage(1); }} />
{/* Orders Table */}
{isLoading ? ( Array.from({ length: 4 }).map((_, i) => ( )) ) : orders.length === 0 ? ( ) : ( orders.map((order) => { const isProcessing = processingId === order.id; const displayCode = order.trackingNumber || order.id.slice(0, 8); const orderDate = new Date(order.createdAt || order.date); const customerName = order.user ? `${order.user.firstName || ''} ${order.user.lastName || ''}`.trim() : 'کاربر مهمان'; const itemsCount = (order.orderItems || order.items || []).length; const totalAmt = Number(order.totalAmount || order.total || 0); return ( ); }) )}
شماره / کد پیگیری مشتری مبلغ کل تاریخ و ساعت ثبت اقلام وضعیت سفارش عملیات ادمین
هیچ سفارشی یافت نشد.
{toPersianDigits(displayCode)}
{order.isRefill && ( Refill 5% )}
{customerName}
{toPersianDigits(order.user?.phone || '')}
{toPersianDigits(totalAmt.toLocaleString())} تومان
{toPersianDigits(orderDate.toLocaleDateString('fa-IR'))}
{toPersianDigits(orderDate.toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }))}
{toPersianDigits(itemsCount)} قلم
{/* Admin Order Details Modal */} {selectedOrder && (
{/* Modal Header */}

جزئیات سفارش {toPersianDigits(selectedOrder.trackingNumber || selectedOrder.id.slice(0, 8))}

ثبت شده در {toPersianDigits(new Date(selectedOrder.createdAt || selectedOrder.date).toLocaleDateString('fa-IR'))} - ساعت {toPersianDigits(new Date(selectedOrder.createdAt || selectedOrder.date).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }))}

{/* Modal Body */}
{/* Customer & Shipping Info */}

اطلاعات خریدار

{selectedOrder.user ? `${selectedOrder.user.firstName || ''} ${selectedOrder.user.lastName || ''}` : 'خریدار مهمان'}
{toPersianDigits(selectedOrder.user?.phone || 'شماره ثبت نشده')}
{selectedOrder.user?.email && (
{selectedOrder.user.email}
)}

آدرس ارسال سفارش

{selectedOrder.shippingAddress || 'آدرس ثبتی کاربر در حساب کاربری'}

{/* Status & Tracking Editor */}

مدیریت وضعیت و کد رهگیری پستی

setModalTrackingCode(e.target.value)} className="flex-1 bg-white border border-gray-300 text-gray-900 text-sm font-mono font-bold rounded-xl px-3 py-2.5 outline-none focus:ring-2 focus:ring-purple-500" dir="ltr" />
{/* Items Table */}

اقلام خریده شده ({toPersianDigits((selectedOrder.orderItems || selectedOrder.items || []).length)})

{(selectedOrder.orderItems || selectedOrder.items || []).map((item: any, idx: number) => { const pName = item.product?.nameFa || item.product?.name || item.name || 'محصول کانینا'; const pImg = item.product?.imageUrl || item.product?.image || '/products/product-placeholder.png'; const pPrice = Number(item.product?.priceValue || item.priceValue || 0); const qty = item.quantity || 1; return (
{pName}
{pName}
{toPersianDigits(qty)} عدد × {toPersianDigits(pPrice.toLocaleString())} تومان
{toPersianDigits((pPrice * qty).toLocaleString())} تومان
); })}
{/* Financial Summary */}
روش پرداخت: {selectedOrder.paymentMethod === 'wallet' ? 'کیف پول الکترونیک' : 'درگاه پرداخت آنلاین'}
{selectedOrder.isRefill && (
رزرو هوشمند تمدید دوره (۵٪ پاداش خرید بعدی): ثبت و رزرو گردید
)} {Number(selectedOrder.charityDonation || 0) > 0 && (
کمک اهدایی (ردپای مهربانی): {toPersianDigits(Number(selectedOrder.charityDonation).toLocaleString())}+ تومان
)}
مبلغ نهایی پرداخت شده: {toPersianDigits(Number(selectedOrder.totalAmount || selectedOrder.total || 0).toLocaleString())} تومان
{/* Modal Footer */}
)}
); }