import { useQuery } from "@tanstack/react-query"; import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useStore } from "@/components/Layout"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Calendar, Package, ShoppingCart, TrendingUp, Clock, MapPin, User, AlertTriangle, CheckCircle, Truck, FileText, BarChart3, Megaphone, Shield, XCircle, CheckSquare, Circle } from "lucide-react"; import { safeFormat, safeDate } from "@/lib/dateUtils"; import type { PublicityWithRelations } from "@shared/schema"; export default function Dashboard() { const { user } = useAuthUnified(); const { selectedStoreId } = useStore(); const { data: stats } = useQuery({ queryKey: ['/api/stats/monthly', selectedStoreId], queryFn: async () => { const currentDate = new Date(); const params = new URLSearchParams({ year: currentDate.getFullYear().toString(), month: (currentDate.getMonth() + 1).toString(), }); if (selectedStoreId && user?.role === 'admin') { params.append('storeId', selectedStoreId.toString()); } const response = await fetch(`/api/stats/monthly?${params.toString()}`, { credentials: 'include' }); if (!response.ok) { throw new Error('Failed to fetch stats'); } return response.json(); }, }); // Construire les URLs pour récupérer toutes les données (pas de filtrage par date) const ordersUrl = `/api/orders${selectedStoreId && user?.role === 'admin' ? `?storeId=${selectedStoreId}` : ''}`; const deliveriesUrl = `/api/deliveries${selectedStoreId && user?.role === 'admin' ? `?storeId=${selectedStoreId}` : ''}`; const customerOrdersUrl = `/api/customer-orders${selectedStoreId && user?.role === 'admin' ? `?storeId=${selectedStoreId}` : ''}`; // Utiliser les mêmes clés de cache que les autres pages pour assurer la cohérence const { data: allOrders = [] } = useQuery({ queryKey: [ordersUrl, selectedStoreId], }); const { data: allDeliveries = [] } = useQuery({ queryKey: [deliveriesUrl, selectedStoreId], }); const { data: customerOrders = [] } = useQuery({ queryKey: [customerOrdersUrl, selectedStoreId], }); // Récupérer les publicités à venir (chercher dans 2024 ET 2025) - TOUTES les publicités const { data: upcomingPublicities = [] } = useQuery({ queryKey: ['/api/publicities', 'upcoming'], queryFn: async () => { // Essayer d'abord 2024, puis 2025 pour avoir toutes les publicités const years = [2024, 2025]; let allPublicities: PublicityWithRelations[] = []; for (const year of years) { const params = new URLSearchParams(); params.append('year', year.toString()); // NE PAS filtrer par magasin - on veut toutes les publicités try { const response = await fetch(`/api/publicities?${params}`, { credentials: 'include' }); if (response.ok) { const yearPublicities = await response.json(); allPublicities = [...allPublicities, ...yearPublicities]; } } catch (error) { console.log(`Erreur lors de la récupération des publicités ${year}:`, error); } } // Filtrer les publicités à venir et les trier par date const futurePublicities = allPublicities .filter((publicity: any) => new Date(publicity.startDate) > new Date()) .sort((a: any, b: any) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime()); return futurePublicities; }, }); // Fetch DLC stats const { data: dlcStats = { active: 0, expiringSoon: 0, expired: 0 } } = useQuery({ queryKey: ["/api/dlc-products/stats", selectedStoreId], queryFn: () => { const params = new URLSearchParams(); if (selectedStoreId && user?.role === 'admin') params.append("storeId", selectedStoreId.toString()); return fetch(`/api/dlc-products/stats?${params.toString()}`, { credentials: 'include' }).then(res => res.json()); }, }); // Fetch tasks const { data: tasks = [] } = useQuery({ queryKey: ["/api/tasks", selectedStoreId], queryFn: () => { const params = new URLSearchParams(); if (selectedStoreId && user?.role === 'admin') params.append("storeId", selectedStoreId.toString()); return fetch(`/api/tasks?${params.toString()}`, { credentials: 'include' }).then(res => res.json()); }, }); // Données dérivées pour les sections const recentOrders = Array.isArray(allOrders) ? allOrders .sort((a: any, b: any) => { const dateA = safeDate(a.createdAt); const dateB = safeDate(b.createdAt); return (dateB ? dateB.getTime() : 0) - (dateA ? dateA.getTime() : 0); }) .slice(0, 4) : []; // Afficher les 4 dernières commandes const upcomingDeliveries = Array.isArray(allDeliveries) ? allDeliveries .filter((d: any) => { const isPlanned = d.status === 'planned'; console.log('🚚 Dashboard - Delivery filter:', { id: d.id, status: d.status, isPlanned, scheduledDate: d.scheduledDate }); return isPlanned; }) .sort((a: any, b: any) => new Date(a.scheduledDate).getTime() - new Date(b.scheduledDate).getTime()) .slice(0, 4) : []; // Afficher les 4 prochaines livraisons console.log('🚚 Dashboard - Upcoming deliveries result:', upcomingDeliveries.length, upcomingDeliveries); // Calculs pour les statistiques const pendingOrdersCount = Array.isArray(allOrders) ? allOrders.filter((order: any) => order.status === 'pending').length : 0; const averageDeliveryTime = Math.round(stats?.averageDeliveryTime || 0); const deliveredThisMonth = Array.isArray(allDeliveries) ? allDeliveries.filter((delivery: any) => { const deliveryDate = safeDate(delivery.deliveredDate || delivery.createdAt); if (!deliveryDate) return false; const now = new Date(); return deliveryDate.getMonth() === now.getMonth() && deliveryDate.getFullYear() === now.getFullYear() && delivery.status === 'delivered'; }).length : 0; // Calculer le total des palettes pour les livraisons 'delivered' du mois en cours const totalPalettes = Array.isArray(allDeliveries) ? allDeliveries.reduce((total: number, delivery: any) => { if (delivery.status === 'delivered' && delivery.unit === 'palettes') { const deliveryDate = safeDate(delivery.deliveredDate || delivery.createdAt); if (!deliveryDate) return total; const now = new Date(); if (deliveryDate.getMonth() === now.getMonth() && deliveryDate.getFullYear() === now.getFullYear()) { return total + (delivery.quantity || 0); } } return total; }, 0) : 0; // Fonction pour obtenir la configuration des priorités (identique au module Tasks) const getPriorityConfig = (priority: string) => { switch (priority) { case 'high': return { color: 'destructive' as const, icon: AlertTriangle, label: 'Élevée' }; case 'medium': return { color: 'default' as const, icon: Clock, label: 'Moyenne' }; case 'low': return { color: 'secondary' as const, icon: Circle, label: 'Faible' }; default: return { color: 'secondary' as const, icon: Circle, label: 'Moyenne' }; } }; console.log('📊 Dashboard Debug - Raw Data:', { allOrders: Array.isArray(allOrders) ? allOrders.length : 'NOT_ARRAY', allDeliveries: Array.isArray(allDeliveries) ? allDeliveries.length : 'NOT_ARRAY', customerOrders: Array.isArray(customerOrders) ? customerOrders.length : 'NOT_ARRAY', upcomingPublicities: Array.isArray(upcomingPublicities) ? upcomingPublicities.length : 'NOT_ARRAY', stats: stats, samples: { order: Array.isArray(allOrders) && allOrders.length > 0 ? allOrders[0] : null, delivery: Array.isArray(allDeliveries) && allDeliveries.length > 0 ? allDeliveries[0] : null, customerOrder: Array.isArray(customerOrders) && customerOrders.length > 0 ? customerOrders[0] : null, publicity: Array.isArray(upcomingPublicities) && upcomingPublicities.length > 0 ? upcomingPublicities[0] : null } }); // Statistiques pour les commandes clients const ordersByStatus = { pending: Array.isArray(allOrders) ? allOrders.filter((o: any) => o.status === 'pending').length : 0, planned: Array.isArray(allOrders) ? allOrders.filter((o: any) => o.status === 'planned').length : 0, delivered: Array.isArray(allOrders) ? allOrders.filter((o: any) => o.status === 'delivered').length : 0, total: Array.isArray(allOrders) ? allOrders.length : 0 }; // Statistiques pour les commandes clients (nouveau module) const customerOrderStats = { waiting: Array.isArray(customerOrders) ? customerOrders.filter((o: any) => o.status === 'En attente de Commande').length : 0, inProgress: Array.isArray(customerOrders) ? customerOrders.filter((o: any) => o.status === 'Commande en Cours').length : 0, available: Array.isArray(customerOrders) ? customerOrders.filter((o: any) => o.status === 'Disponible').length : 0, withdrawn: Array.isArray(customerOrders) ? customerOrders.filter((o: any) => o.status === 'Retiré').length : 0, canceled: Array.isArray(customerOrders) ? customerOrders.filter((o: any) => o.status === 'Annulé').length : 0, total: Array.isArray(customerOrders) ? customerOrders.length : 0 }; return (
{/* Header */}

Tableau de Bord

Vue d'ensemble des performances et statistiques

{selectedStoreId && user?.role === 'admin' && ( Magasin sélectionné )}
{/* Alerts */}
{pendingOrdersCount > 0 && (
{pendingOrdersCount} commande(s) en attente nécessitent une planification
)} {dlcStats.expiringSoon > 0 && (
{dlcStats.expiringSoon} produit(s) DLC expirent dans les 15 prochains jours
)} {dlcStats.expired > 0 && (
{dlcStats.expired} produit(s) DLC sont expirés et nécessitent une action immédiate
)}
{/* Stats Cards */}

Livraisons ce mois

{deliveredThisMonth}

Commandes en attente

{pendingOrdersCount}

Délai moyen (jours)

{averageDeliveryTime}

Total palettes

{totalPalettes}

{/* Main Content Grid */}
{/* Dernières Commandes */} Dernières Commandes {recentOrders.length > 0 ? recentOrders.map((order: any) => (

{order.supplier?.name}

{order.group?.name}

{order.status === 'delivered' ? 'Livrée' : order.status === 'planned' ? 'Planifiée' : 'En attente'}

{safeFormat(order.plannedDate, "d MMM")}

)) : (

Aucune commande récente

)}
{/* Livraisons à Venir */} Livraisons à Venir {Array.isArray(upcomingDeliveries) && upcomingDeliveries.length > 0 ? upcomingDeliveries.map((delivery: any) => (

{delivery.supplier?.name}

{delivery.group?.name}

{delivery.quantity} {delivery.unit}

{safeFormat(delivery.scheduledDate, "d MMM")}

)) : (

Aucune livraison programmée

({Array.isArray(allDeliveries) ? allDeliveries.length : 0} livraisons totales)

)}
{/* Publicités à Venir */} Publicités à Venir {upcomingPublicities .slice(0, 3) .map((publicity: any) => { const participatingStores = publicity.participations || []; const isCurrentStoreParticipating = selectedStoreId && participatingStores.some((p: any) => p.groupId === parseInt(selectedStoreId.toString())); return (

{publicity.pubNumber}

{publicity.designation}

À venir

{safeFormat(publicity.startDate, "d MMM")}

{/* Magasins participants */}
Magasins: {participatingStores.length === 0 ? ( Aucun magasin ) : (
{participatingStores.map((participation: any) => ( {participation.group.name} ))}
)}
); })} {(!Array.isArray(upcomingPublicities) || upcomingPublicities.length === 0) && (

Aucune publicité à venir

(API: {Array.isArray(upcomingPublicities) ? upcomingPublicities.length : 'NOT_ARRAY'})

)}
{/* Section Rapprochement BL */}
{/* Statistiques des commandes clients */} Commandes Clients
En attente
{customerOrderStats.waiting}
En cours
{customerOrderStats.inProgress}
Disponibles
{customerOrderStats.available}
Retirées
{customerOrderStats.withdrawn}
Total commandes
{customerOrderStats.total}
{/* Tâches à faire */} Tâches à faire {Array.isArray(tasks) && tasks.length > 0 ? tasks .filter((task: any) => task.status !== 'completed') .sort((a: any, b: any) => { const dateA = safeDate(a.createdAt); const dateB = safeDate(b.createdAt); return (dateA ? dateA.getTime() : 0) - (dateB ? dateB.getTime() : 0); }) .slice(0, 5) .map((task: any) => { const priorityConfig = getPriorityConfig(task.priority); const PriorityIcon = priorityConfig.icon; return (

{task.title}

{task.assignedUser?.username || task.assignedTo}

{priorityConfig.label}

{safeFormat(task.createdAt, "d MMM")}

); }) : (

Aucune tâche en cours

Toutes les tâches sont terminées

)}
); }