From 2c4db175c27b9385600f1246a2a6ce1273eb44ca Mon Sep 17 00:00:00 2001 From: rckrdmrd Date: Tue, 20 Jan 2026 02:15:04 -0600 Subject: [PATCH] [MCH-FE] feat: Connect Dashboard to real API Replace hardcoded mock data with TanStack Query hooks: - dashboardApi.getStats() for stats cards (sales, orders, customers, fiado) - ordersApi.getAll() for recent orders list - inventoryApi.getLowStock() for low stock alerts Add loading spinners and error states for each section. Add TypeScript interfaces for API response types. Add formatCurrency helper for MXN formatting. Add Spanish labels for order status. Co-Authored-By: Claude Opus 4.5 --- src/pages/Dashboard.tsx | 274 ++++++++++++++++++++++++++++++---------- 1 file changed, 206 insertions(+), 68 deletions(-) diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 477d4e6..3ddbb07 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -1,3 +1,4 @@ +import { useQuery } from '@tanstack/react-query'; import { TrendingUp, ShoppingCart, @@ -5,26 +6,35 @@ import { CreditCard, Package, AlertCircle, + Loader2, } from 'lucide-react'; +import { dashboardApi, ordersApi, inventoryApi } from '../lib/api'; -const stats = [ - { name: 'Ventas Hoy', value: '$1,240', change: '+12%', icon: TrendingUp, color: 'green' }, - { name: 'Pedidos', value: '23', change: '+5', icon: ShoppingCart, color: 'blue' }, - { name: 'Clientes', value: '156', change: '+3', icon: Users, color: 'purple' }, - { name: 'Fiados Pendientes', value: '$2,100', change: '-$450', icon: CreditCard, color: 'orange' }, -]; +interface DashboardStats { + salesToday: number; + ordersCount: number; + customersCount: number; + pendingFiado: number; + salesChange: string; + ordersChange: string; + customersChange: string; + fiadoChange: string; +} -const recentOrders = [ - { id: 'MCH-001', customer: 'Maria Lopez', total: 156.00, status: 'ready', time: '10:30 AM' }, - { id: 'MCH-002', customer: 'Juan Perez', total: 89.50, status: 'preparing', time: '10:45 AM' }, - { id: 'MCH-003', customer: 'Ana Garcia', total: 234.00, status: 'pending', time: '11:00 AM' }, -]; +interface Order { + id: string; + customer: { name: string } | null; + total: number; + status: string; + createdAt: string; +} -const lowStockProducts = [ - { name: 'Coca-Cola 600ml', stock: 5, minStock: 10 }, - { name: 'Pan Bimbo', stock: 2, minStock: 5 }, - { name: 'Leche Lala 1L', stock: 3, minStock: 8 }, -]; +interface LowStockProduct { + id: string; + name: string; + stock: number; + minStock: number; +} const statusColors: Record = { pending: 'bg-yellow-100 text-yellow-800', @@ -33,7 +43,111 @@ const statusColors: Record = { completed: 'bg-gray-100 text-gray-800', }; +const statusLabels: Record = { + pending: 'Pendiente', + preparing: 'Preparando', + ready: 'Listo', + completed: 'Completado', +}; + +function formatCurrency(amount: number): string { + return new Intl.NumberFormat('es-MX', { + style: 'currency', + currency: 'MXN', + }).format(amount); +} + +function LoadingSpinner() { + return ( +
+ +
+ ); +} + +function ErrorMessage({ message }: { message: string }) { + return ( +
+ + {message} +
+ ); +} + export function Dashboard() { + // Fetch dashboard stats + const { + data: statsData, + isLoading: statsLoading, + error: statsError, + } = useQuery({ + queryKey: ['dashboard-stats'], + queryFn: async () => { + const response = await dashboardApi.getStats(); + return response.data as DashboardStats; + }, + }); + + // Fetch recent orders + const { + data: ordersData, + isLoading: ordersLoading, + error: ordersError, + } = useQuery({ + queryKey: ['recent-orders'], + queryFn: async () => { + const response = await ordersApi.getAll({ status: undefined }); + return (response.data as Order[]).slice(0, 5); + }, + }); + + // Fetch low stock products + const { + data: lowStockData, + isLoading: lowStockLoading, + error: lowStockError, + } = useQuery({ + queryKey: ['low-stock'], + queryFn: async () => { + const response = await inventoryApi.getLowStock(); + return response.data as LowStockProduct[]; + }, + }); + + // Build stats array from API data + const stats = statsData + ? [ + { + name: 'Ventas Hoy', + value: formatCurrency(statsData.salesToday), + change: statsData.salesChange, + icon: TrendingUp, + color: 'green', + }, + { + name: 'Pedidos', + value: String(statsData.ordersCount), + change: statsData.ordersChange, + icon: ShoppingCart, + color: 'blue', + }, + { + name: 'Clientes', + value: String(statsData.customersCount), + change: statsData.customersChange, + icon: Users, + color: 'purple', + }, + { + name: 'Fiados Pendientes', + value: formatCurrency(statsData.pendingFiado), + change: statsData.fiadoChange, + icon: CreditCard, + color: 'orange', + }, + ] + : []; + return (
@@ -42,26 +156,32 @@ export function Dashboard() {
{/* Stats Grid */} -
- {stats.map((stat) => ( -
-
-
-

{stat.name}

-

{stat.value}

-

- {stat.change} -

-
-
- + {statsLoading ? ( + + ) : statsError ? ( + + ) : ( +
+ {stats.map((stat) => ( +
+
+
+

{stat.name}

+

{stat.value}

+

+ {stat.change} +

+
+
+ +
-
- ))} -
+ ))} +
+ )}
{/* Recent Orders */} @@ -75,25 +195,35 @@ export function Dashboard() { Ver todos
-
- {recentOrders.map((order) => ( -
-
-

{order.id}

-

{order.customer}

+ {ordersLoading ? ( + + ) : ordersError ? ( + + ) : ordersData && ordersData.length > 0 ? ( +
+ {ordersData.map((order) => ( +
+
+

{order.id.slice(0, 8).toUpperCase()}

+

+ {order.customer?.name || 'Cliente General'} +

+
+
+

{formatCurrency(order.total)}

+ + {statusLabels[order.status] || order.status} + +
-
-

${order.total.toFixed(2)}

- - {order.status} - -
-
- ))} -
+ ))} +
+ ) : ( +

No hay pedidos recientes

+ )}
{/* Low Stock Alert */} @@ -107,23 +237,31 @@ export function Dashboard() { Ver inventario
-
- {lowStockProducts.map((product) => ( -
-
- -

{product.name}

+ {lowStockLoading ? ( + + ) : lowStockError ? ( + + ) : lowStockData && lowStockData.length > 0 ? ( +
+ {lowStockData.map((product) => ( +
+
+ +

{product.name}

+
+
+

{product.stock} unidades

+

Min: {product.minStock}

+
-
-

{product.stock} unidades

-

Min: {product.minStock}

-
-
- ))} -
+ ))} +
+ ) : ( +

No hay productos con stock bajo

+ )}