From c8cf78e0db09a4b57d30610b376a98cf5e304e39 Mon Sep 17 00:00:00 2001 From: rckrdmrd Date: Tue, 20 Jan 2026 02:14:56 -0600 Subject: [PATCH] [MCH-FE] feat: Connect Orders to real API - Replace mock data with useQuery for fetching orders from ordersApi - Add useMutation for updating order status - Implement loading state with spinner - Add error state with retry button - Add empty state when no orders found - Show individual loading state on status update buttons Co-Authored-By: Claude Opus 4.5 --- src/pages/Orders.tsx | 290 ++++++++++++++++++++++++++----------------- 1 file changed, 179 insertions(+), 111 deletions(-) diff --git a/src/pages/Orders.tsx b/src/pages/Orders.tsx index 6fea074..95a0f35 100644 --- a/src/pages/Orders.tsx +++ b/src/pages/Orders.tsx @@ -1,47 +1,26 @@ import { useState } from 'react'; -import { Clock, CheckCircle, XCircle, ChefHat, Package } from 'lucide-react'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { Clock, CheckCircle, XCircle, ChefHat, Package, Loader2, AlertCircle, RefreshCw } from 'lucide-react'; import clsx from 'clsx'; +import { ordersApi } from '../lib/api'; -const mockOrders = [ - { - id: 'MCH-001', - customer: 'Maria Lopez', - phone: '5551234567', - items: [ - { name: 'Coca-Cola 600ml', qty: 2, price: 18.00 }, - { name: 'Sabritas', qty: 3, price: 15.00 }, - ], - total: 81.00, - status: 'pending', - source: 'whatsapp', - createdAt: '2024-01-15T10:30:00', - }, - { - id: 'MCH-002', - customer: 'Juan Perez', - phone: '5559876543', - items: [ - { name: 'Leche Lala 1L', qty: 2, price: 28.00 }, - { name: 'Pan Bimbo', qty: 1, price: 45.00 }, - ], - total: 101.00, - status: 'preparing', - source: 'pos', - createdAt: '2024-01-15T10:45:00', - }, - { - id: 'MCH-003', - customer: 'Ana Garcia', - phone: '5555555555', - items: [ - { name: 'Fabuloso 1L', qty: 1, price: 32.00 }, - ], - total: 32.00, - status: 'ready', - source: 'whatsapp', - createdAt: '2024-01-15T11:00:00', - }, -]; +// Order types based on API response +interface OrderItem { + name: string; + qty: number; + price: number; +} + +interface Order { + id: string; + customer: string; + phone: string; + items: OrderItem[]; + total: number; + status: string; + source: string; + createdAt: string; +} const statusConfig = { pending: { label: 'Pendiente', color: 'yellow', icon: Clock }, @@ -56,20 +35,85 @@ const statusFlow = ['pending', 'confirmed', 'preparing', 'ready', 'completed']; export function Orders() { const [filter, setFilter] = useState('all'); + const queryClient = useQueryClient(); - const filteredOrders = filter === 'all' - ? mockOrders - : mockOrders.filter(o => o.status === filter); + // Fetch orders from API + const { + data: ordersResponse, + isLoading, + isError, + error, + refetch, + } = useQuery({ + queryKey: ['orders', filter === 'all' ? undefined : filter], + queryFn: () => ordersApi.getAll(filter === 'all' ? undefined : { status: filter }), + }); - const updateStatus = (orderId: string, currentStatus: string) => { + // Get orders array from response + const orders: Order[] = ordersResponse?.data || []; + + // Fetch all orders for counting (unfiltered) + const { data: allOrdersResponse } = useQuery({ + queryKey: ['orders', 'all'], + queryFn: () => ordersApi.getAll(), + enabled: filter !== 'all', // Only fetch when we have a filter active + }); + + // Use all orders for counting, or current orders if no filter + const allOrders: Order[] = filter === 'all' ? orders : (allOrdersResponse?.data || orders); + + // Mutation for updating order status + const updateStatusMutation = useMutation({ + mutationFn: ({ id, status }: { id: string; status: string }) => + ordersApi.updateStatus(id, status), + onSuccess: () => { + // Invalidate and refetch orders + queryClient.invalidateQueries({ queryKey: ['orders'] }); + }, + onError: (err) => { + console.error('Error updating order status:', err); + // Could show a toast notification here + }, + }); + + const handleUpdateStatus = (orderId: string, currentStatus: string) => { const currentIndex = statusFlow.indexOf(currentStatus); if (currentIndex < statusFlow.length - 1) { const nextStatus = statusFlow[currentIndex + 1]; - console.log(`Updating ${orderId} to ${nextStatus}`); - // API call would go here + updateStatusMutation.mutate({ id: orderId, status: nextStatus }); } }; + // Loading state + if (isLoading) { + return ( +
+ +

Cargando pedidos...

+
+ ); + } + + // Error state + if (isError) { + return ( +
+ +

Error al cargar pedidos

+

+ {error instanceof Error ? error.message : 'Ocurrio un error inesperado'} +

+ +
+ ); + } + return (
@@ -88,17 +132,17 @@ export function Orders() { : 'bg-gray-100 text-gray-600 hover:bg-gray-200' )} > - Todos ({mockOrders.length}) + Todos ({allOrders.length}) - {Object.entries(statusConfig).slice(0, 4).map(([status, config]) => { - const count = mockOrders.filter(o => o.status === status).length; + {Object.entries(statusConfig).slice(0, 4).map(([statusKey, config]) => { + const count = allOrders.filter(o => o.status === statusKey).length; return ( - )} +
+
+ {order.items?.map((item, i) => ( + + {item.qty}x {item.name} + {i < order.items.length - 1 ? ', ' : ''} + + ))} +
+
+ +
+
+

${order.total?.toFixed(2) || '0.00'}

+

+ {new Date(order.createdAt).toLocaleTimeString('es-MX', { + hour: '2-digit', + minute: '2-digit', + })} +

+
+ + {order.status !== 'completed' && order.status !== 'cancelled' && ( + + )} +
- - ); - })} + ); + }) + )} );