'use client'; import React, { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import Sidebar from '@/app/admin/components/Sidebar/sidebar'; import { apiClient } from '@/app/utils/apiClient'; import { generateTrackingCode } from '@/app/utils/trackingCodeGenerator'; import { Box, Button, Card, Container, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControl, Grid, IconButton, InputAdornment, InputLabel, MenuItem, Paper, Select, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TextField, Typography, Chip, CircularProgress, Alert, Tooltip, } from '@mui/material'; import { Search as SearchIcon, Visibility as VisibilityIcon, FilterList as FilterListIcon, Refresh as RefreshIcon, Autorenew as AutorenewIcon, Lock as LockIcon, } from '@mui/icons-material'; interface OrderItem { id: number; order_id: number; product_id: number; product_name: string; price: number; quantity: number; total_price: number; product: { id: number; product_name: string; product_primary_image: string; product_price: number; }; } interface Order { id: number; order_number: string; user_id: number; status: string; total_amount: number; payment_method: string; payment_status: string; createdAt: string; updatedAt: string; shipping_address: string; shipping_city: string; shipping_postal_code: string; shipping_country: string; contact_phone: string; contact_email: string; tracking_number?: string; estimated_delivery_date?: string; user: { id: number; name: string; email: string; phone_number?: string; }; items: OrderItem[]; } const statusColors: Record = { pending: '#f59e0b', processing: '#3b82f6', shipped: '#8b5cf6', delivered: '#10b981', cancelled: '#ef4444' }; const paymentStatusColors: Record = { pending: '#f59e0b', paid: '#10b981', failed: '#ef4444', refunded: '#6b7280' }; const OrdersManagement = () => { const router = useRouter(); const [orders, setOrders] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(10); const [totalItems, setTotalItems] = useState(0); const [searchTerm, setSearchTerm] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); const [sortBy, setSortBy] = useState('createdAt'); const [sortOrder, setSortOrder] = useState('DESC'); const [selectedOrder, setSelectedOrder] = useState(null); const [openOrderDialog, setOpenOrderDialog] = useState(false); const [processingUpdate, setProcessingUpdate] = useState(false); const [updateSuccess, setUpdateSuccess] = useState(false); const [trackingNumber, setTrackingNumber] = useState(''); const [estimatedDeliveryDate, setEstimatedDeliveryDate] = useState(''); const [updateTrackingMode, setUpdateTrackingMode] = useState(false); const [autoGenerateTracking, setAutoGenerateTracking] = useState(true); const [trackingLocked, setTrackingLocked] = useState(false); // Auth check useEffect(() => { const token = localStorage.getItem('adminToken'); if (!token) { router.push('/admin/login'); } }, [router]); // Fetch orders const fetchOrders = async () => { setLoading(true); setError(null); try { const queryParams = new URLSearchParams({ page: String(page + 1), limit: String(rowsPerPage), sortBy, order: sortOrder, }); if (statusFilter !== 'all') { queryParams.append('status', statusFilter); } if (searchTerm) { queryParams.append('search', searchTerm); } const response = await apiClient(`/admin/orders?${queryParams.toString()}`); if (response.success) { setOrders(response.data.orders); setTotalItems(response.data.totalItems); } else { setError('Failed to fetch orders'); } } catch (err) { setError('Error loading orders. Please try again.'); console.error('Error fetching orders:', err); } finally { setLoading(false); } }; useEffect(() => { fetchOrders(); }, [page, rowsPerPage, statusFilter, sortBy, sortOrder]); // Handle search submit const handleSearchSubmit = (e: React.FormEvent) => { e.preventDefault(); setPage(0); fetchOrders(); }; // Pagination handlers const handleChangePage = (event: unknown, newPage: number) => { setPage(newPage); }; const handleChangeRowsPerPage = (event: React.ChangeEvent) => { setRowsPerPage(parseInt(event.target.value, 10)); setPage(0); }; // Order details handlers const handleOpenOrderDetails = async (orderId: number) => { try { setLoading(true); const response = await apiClient(`/admin/orders/${orderId}`); if (response.success) { setSelectedOrder(response.data); setOpenOrderDialog(true); if (response.data.tracking_number) { setTrackingNumber(response.data.tracking_number); setTrackingLocked(true); } else { setTrackingLocked(false); if (autoGenerateTracking) { setTrackingNumber(generateTrackingCode(orderId)); } } if (response.data.estimated_delivery_date) { setEstimatedDeliveryDate(response.data.estimated_delivery_date.split('T')[0]); } } else { setError('Failed to fetch order details'); } } catch (err) { setError('Error loading order details'); console.error('Error fetching order details:', err); } finally { setLoading(false); } }; const handleCloseOrderDialog = () => { setOpenOrderDialog(false); setSelectedOrder(null); setUpdateSuccess(false); }; // Update order status const updateOrderStatus = async (orderId: number, status: string) => { try { setProcessingUpdate(true); const response = await apiClient(`/admin/orders/${orderId}/status`, { method: 'PUT', body: JSON.stringify({ status }), }); if (response.success) { setSelectedOrder(prev => { if (!prev) return null; return { ...prev, status: status, ...response.data }; }); setOrders(prevOrders => prevOrders.map(order => order.id === orderId ? { ...order, status } : order ) ); setUpdateSuccess(true); setTimeout(() => setUpdateSuccess(false), 3000); } else { setError('Failed to update order status'); } } catch (err) { setError('Error updating order status'); console.error('Error updating order status:', err); } finally { setProcessingUpdate(false); } }; const updatePaymentStatus = async (orderId: number, paymentStatus: string) => { try { setProcessingUpdate(true); const response = await apiClient(`/admin/orders/${orderId}/status`, { method: 'PUT', body: JSON.stringify({ payment_status: paymentStatus }), }); if (response.success) { setSelectedOrder(prev => { if (!prev) return null; return { ...prev, payment_status: paymentStatus, ...response.data }; }); setOrders(prevOrders => prevOrders.map(order => order.id === orderId ? { ...order, payment_status: paymentStatus } : order ) ); setUpdateSuccess(true); setTimeout(() => setUpdateSuccess(false), 3000); } else { setError('Failed to update payment status'); } } catch (err) { setError('Error updating payment status'); console.error('Error updating payment status:', err); } finally { setProcessingUpdate(false); } }; const updateShippingInfo = async (orderId: number) => { try { setProcessingUpdate(true); const originalOrder = orders.find(order => order.id === orderId); const finalTrackingNumber = originalOrder && originalOrder.tracking_number ? originalOrder.tracking_number : trackingNumber; const response = await apiClient(`/admin/orders/${orderId}/status`, { method: 'PUT', body: JSON.stringify({ tracking_number: finalTrackingNumber, estimated_delivery_date: estimatedDeliveryDate }), }); if (response.success) { setSelectedOrder((prev) => prev ? { ...prev, tracking_number: finalTrackingNumber, estimated_delivery_date: estimatedDeliveryDate } : null); setOrders(prevOrders => prevOrders.map(order => order.id === orderId ? { ...order, tracking_number: finalTrackingNumber, estimated_delivery_date: estimatedDeliveryDate } : order ) ); if (finalTrackingNumber && !originalOrder?.tracking_number) { setTrackingLocked(true); } setUpdateSuccess(true); setUpdateTrackingMode(false); setTimeout(() => setUpdateSuccess(false), 3000); } else { setError('Failed to update shipping information'); } } catch (err) { setError('Error updating shipping information'); console.error('Error updating shipping information:', err); } finally { setProcessingUpdate(false); } }; const regenerateTrackingCode = () => { if (selectedOrder && !trackingLocked) { setTrackingNumber(generateTrackingCode(selectedOrder.id)); } }; const formatDate = (dateString: string) => { return new Date(dateString).toLocaleString(); }; const calculateOrderTotal = (items: OrderItem[]) => { return items.reduce((sum, item) => sum + Number(item.total_price), 0).toFixed(2); }; return (
Order Management Manage customer orders and update order statuses {error && ( {error} )}
setSearchTerm(e.target.value)} fullWidth InputProps={{ startAdornment: ( ), }} />
Status fetchOrders()} color="primary">
{loading && orders.length === 0 ? ( ) : ( Order # Date Customer Total Status Payment Actions {orders.length === 0 ? ( No orders found ) : ( orders.map((order) => ( {order.order_number} {formatDate(order.createdAt)} {order.user.name} {order.user.email} ${Number(order.total_amount).toFixed(2)} handleOpenOrderDetails(order.id)} > )) )}
)}
{selectedOrder ? ( <> Order #{selectedOrder.order_number} {updateSuccess && ( Order status updated successfully )} Order Information Order Date {formatDate(selectedOrder.createdAt)} Total Amount ${Number(selectedOrder.total_amount).toFixed(2)} Payment Method {selectedOrder.payment_method} Payment Status {selectedOrder.tracking_number && ( Tracking Number {selectedOrder.tracking_number} )} Customer Information {selectedOrder.user.name} {selectedOrder.contact_email} {selectedOrder.contact_phone} Shipping Address: {selectedOrder.shipping_address} {selectedOrder.shipping_city}, {selectedOrder.shipping_postal_code} {selectedOrder.shipping_country} Order Items Product Price Quantity Total {selectedOrder.items.map((item) => ( {item.product?.product_primary_image && ( {item.product_name} )} {item.product_name} ${Number(item.price).toFixed(2)} {item.quantity} ${Number(item.total_price).toFixed(2)} ))} Total: ${calculateOrderTotal(selectedOrder.items)}
Update Order Status {['pending', 'processing', 'shipped', 'delivered', 'cancelled'].map((status) => ( ))} {processingUpdate && ( )} Update Payment Status {['pending', 'paid', 'failed', 'refunded'].map((status) => ( ))} {processingUpdate && ( )} Shipping Information {!updateTrackingMode ? ( <> Tracking Number {selectedOrder?.tracking_number || 'Not available'} Estimated Delivery {selectedOrder?.estimated_delivery_date ? new Date(selectedOrder.estimated_delivery_date).toLocaleDateString() : 'Not available'} ) : ( <> !trackingLocked && setTrackingNumber(e.target.value)} disabled={trackingLocked} helperText={trackingLocked ? "Tracking number is permanent and cannot be changed" : ""} InputProps={{ endAdornment: ( {trackingLocked ? ( ) : ( )} ), }} /> setEstimatedDeliveryDate(e.target.value)} InputLabelProps={{ shrink: true, }} /> {processingUpdate && ( )} )}
) : ( )}
); }; export default OrdersManagement;