/** * Hook pour les données du dashboard - Version 2025 BTP Xpress * Utilise UNIQUEMENT les données réelles du backend Quarkus */ import { useState, useEffect, useCallback } from 'react'; import { apiClient } from '../services/api-client'; import { DashboardMetrics, ChantierActif, ActiviteRecente, TacheUrgente, DashboardPrincipalResponse, DashboardChantiersResponse, ChantierActifDTO } from '../types/dashboard'; // Ré-exporter les types pour compatibilité export type { DashboardMetrics, ChantierActif, ActiviteRecente, TacheUrgente }; interface DashboardData { metrics: DashboardMetrics | null; chantiersActifs: ChantierActif[]; activitesRecentes: ActiviteRecente[]; tachesUrgentes: TacheUrgente[]; loading: boolean; error: string | null; } export const useDashboard = (periode: 'semaine' | 'mois' | 'trimestre' | 'annee' = 'mois') => { const [data, setData] = useState({ metrics: null, chantiersActifs: [], activitesRecentes: [], tachesUrgentes: [], loading: true, error: null, }); const [currentPeriode, setCurrentPeriode] = useState(periode); const loadDashboardData = useCallback(async (abortController?: AbortController) => { try { setData(prev => ({ ...prev, loading: true, error: null })); console.log('📊 Dashboard: Démarrage du chargement des données depuis le backend...'); console.log('🔗 API Base URL:', process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080'); // Vérifier si la requête a été annulée if (abortController?.signal.aborted) { console.log('📊 Dashboard: Requête annulée avant le début'); return; } // Charger les données depuis les endpoints réels du backend const [dashboardPrincipalResponse, dashboardChantiersResponse] = await Promise.allSettled([ apiClient.get('/api/v1/dashboard'), apiClient.get('/api/v1/dashboard/chantiers') ]); // Vérifier les erreurs if (dashboardPrincipalResponse.status === 'rejected') { console.error('❌ Erreur dashboard principal:', dashboardPrincipalResponse.reason); throw new Error('Impossible de charger les métriques du dashboard'); } if (dashboardChantiersResponse.status === 'rejected') { console.error('❌ Erreur dashboard chantiers:', dashboardChantiersResponse.reason); throw new Error('Impossible de charger les chantiers actifs'); } const dashboardPrincipal = dashboardPrincipalResponse.value.data; const dashboardChantiers = dashboardChantiersResponse.value.data; console.log('✅ Dashboard principal:', dashboardPrincipal); console.log('✅ Dashboard chantiers:', dashboardChantiers); // Transformer les données backend vers le format attendu par le frontend const metrics: DashboardMetrics = { totalChantiers: dashboardPrincipal.chantiers.total, chantiersActifs: dashboardPrincipal.chantiers.actifs, chantiersEnRetard: dashboardChantiers.chantiersEnRetard.length, chantiersTermines: (dashboardChantiers.statistiques as any)?.termines || 0, totalEquipes: dashboardPrincipal.equipes.total, equipesDisponibles: dashboardPrincipal.equipes.disponibles, totalMateriel: dashboardPrincipal.materiel.total, materielDisponible: dashboardPrincipal.materiel.disponible, materielEnMaintenance: dashboardPrincipal.maintenance.enRetard + dashboardPrincipal.maintenance.planifiees, totalDocuments: dashboardPrincipal.documents.total, totalPhotos: 0, // TODO: Ajouter au backend si nécessaire budgetTotal: dashboardChantiers.chantiersActifs.reduce((sum, c) => sum + c.budget, 0), coutReel: dashboardChantiers.chantiersActifs.reduce((sum, c) => sum + c.coutReel, 0), chiffreAffaires: dashboardChantiers.chantiersActifs.reduce((sum, c) => sum + c.budget, 0), objectifCA: 0, // TODO: Ajouter au backend si nécessaire tauxReussite: (dashboardChantiers.statistiques as any)?.tauxReussite || 0, satisfactionClient: 0 // TODO: Ajouter au backend si nécessaire }; // Transformer les chantiers actifs du backend vers le format frontend const chantiersActifs: ChantierActif[] = dashboardChantiers.chantiersActifs.map((chantier: ChantierActifDTO) => ({ id: chantier.id, nom: chantier.nom, client: chantier.client, avancement: chantier.avancement, dateDebut: chantier.dateDebut, dateFinPrevue: chantier.dateFinPrevue, statut: mapStatutFromBackend(chantier.statut), budget: chantier.budget, coutReel: chantier.coutReel })); // Activités récentes et tâches urgentes - À implémenter avec des endpoints dédiés const activitesRecentes: ActiviteRecente[] = []; const tachesUrgentes: TacheUrgente[] = []; console.log('✅ Données transformées:', { metrics, chantiersActifs: chantiersActifs.length, activitesRecentes: activitesRecentes.length, tachesUrgentes: tachesUrgentes.length }); setData({ metrics, chantiersActifs, activitesRecentes, tachesUrgentes, loading: false, error: null, }); } catch (error: any) { console.error('💥 Erreur lors du chargement du dashboard:', error); const errorMessage = error.response?.status === 404 ? 'Endpoints du dashboard non trouvés. Vérifiez que le backend est à jour.' : error.response?.status === 401 ? 'Non authentifié. Veuillez vous reconnecter.' : error.response?.status === 500 ? 'Erreur serveur. Vérifiez les logs du backend.' : 'Erreur de communication avec le serveur. Vérifiez que le backend est démarré sur http://localhost:8080'; setData(prev => ({ ...prev, loading: false, error: errorMessage, })); } }, []); useEffect(() => { const abortController = new AbortController(); // Vérifier si on a des tokens d'authentification stockés if (typeof window !== 'undefined') { const hasTokens = localStorage.getItem('accessToken'); const currentUrl = window.location.href; const hasAuthCode = currentUrl.includes('code=') && currentUrl.includes('/dashboard'); console.log('📊 Dashboard Hook: État actuel', { hasTokens: !!hasTokens, hasAuthCode, url: currentUrl }); // Si on a des tokens, charger immédiatement même avec un code dans l'URL if (hasTokens) { console.log('📊 Dashboard Hook: Tokens trouvés, chargement immédiat des données...'); loadDashboardData(abortController); return () => abortController.abort(); } if (hasAuthCode && !hasTokens) { console.log('📊 Dashboard Hook: Attente de la fin du traitement d\'authentification...'); // NE PAS charger les données tant que l'authentification n'est pas terminée // La page dashboard appellera refresh() une fois l'authentification terminée console.log('📊 Dashboard Hook: Chargement différé en attente de l\'authentification'); return () => { abortController.abort(); }; } } // Charger par défaut seulement si on n'est pas en train de s'authentifier if (typeof window === 'undefined' || !window.location.href.includes('code=')) { console.log('📊 Dashboard Hook: Chargement par défaut...'); loadDashboardData(abortController); } else { console.log('📊 Dashboard Hook: Chargement différé (code d\'autorisation détecté)'); } return () => abortController.abort(); }, []); // Supprimer loadDashboardData des dépendances pour éviter la boucle const refresh = useCallback(() => { const abortController = new AbortController(); loadDashboardData(abortController); return () => abortController.abort(); }, []); const changePeriode = useCallback((nouvellePeriode: 'semaine' | 'mois' | 'trimestre' | 'annee') => { setCurrentPeriode(nouvellePeriode); }, []); return { ...data, refresh, changePeriode, periode: currentPeriode, isLoading: data.loading, }; }; // Fonctions utilitaires pour transformer les données backend function mapStatutFromBackend(statut: string): 'EN_COURS' | 'EN_RETARD' | 'PLANIFIE' | 'TERMINE' { switch (statut) { case 'EN_COURS': return 'EN_COURS'; case 'PLANIFIE': return 'PLANIFIE'; case 'TERMINE': return 'TERMINE'; case 'EN_RETARD': return 'EN_RETARD'; case 'SUSPENDU': return 'EN_COURS'; // Mapper SUSPENDU vers EN_COURS pour l'affichage case 'ANNULE': return 'TERMINE'; // Mapper ANNULE vers TERMINE pour l'affichage default: console.warn(`Statut inconnu reçu du backend: ${statut}`); return 'EN_COURS'; } } export default useDashboard;