/* Page principale du dashboard : KPI cards + grille de monitorings */ import { useState, useEffect, useCallback, useMemo } from 'react'; import { RefreshCw } from 'lucide-react'; import { getDashboard, getDashboardSummary, getDashboardFiltres } from '../../services/api'; import MonitoringCard from '../../components/common/MonitoringCard'; import KpiCard from '../../components/common/KpiCard'; import LoadingSpinner from '../../components/common/LoadingSpinner'; import ErrorMessage from '../../components/common/ErrorMessage'; import styles from './Dashboard.module.css'; export default function Dashboard() { const [summary, setSummary] = useState(null); const [monitorings, setMonitorings] = useState([]); const [services, setServices] = useState([]); const [combinaisons, setCombinaisons] = useState([]); const [filtreService, setFiltreService] = useState(''); const [filtreCategorie, setFiltreCategorie] = useState(''); const [loading, setLoading] = useState(true); const [erreur, setErreur] = useState(''); const [rafraichissement, setRafraichissement] = useState(0); /* Chargement initial des filtres. On n'utilise pas les référentiels SERVICE / CATEGORIE complets : ils contiennent des entrées sans aucun monitoring rattaché (ex. « Business Intelligence »), qui mèneraient sur un dashboard vide. */ useEffect(() => { async function loadFiltres() { try { const filtres = await getDashboardFiltres(); setServices(filtres.services); setCombinaisons(filtres.combinaisons); } catch { /* Non bloquant, les filtres restent vides */ } } loadFiltres(); }, []); /* Catégories proposées : restreintes au service sélectionné, le cas échéant */ const categoriesDisponibles = useCallback(service => { const pertinentes = service ? combinaisons.filter(c => c.service === service) : combinaisons; return [...new Set(pertinentes.map(c => c.categorie))].sort(); }, [combinaisons]); const categories = useMemo( () => categoriesDisponibles(filtreService), [categoriesDisponibles, filtreService], ); /* Changer de service peut rendre la catégorie sélectionnée sans résultat : on la remet à zéro dans le même rendu, pour ne déclencher qu'un seul appel. */ function handleServiceChange(service) { setLoading(true); setFiltreService(service); if (filtreCategorie && !categoriesDisponibles(service).includes(filtreCategorie)) { setFiltreCategorie(''); } } function handleCategorieChange(categorie) { setLoading(true); setFiltreCategorie(categorie); } /* Le bouton Actualiser relance l'effet en changeant cette valeur. */ function handleActualiser() { setLoading(true); setRafraichissement(compteur => compteur + 1); } /* Les données sont rechargées à chaque changement de filtre. L'indicateur de chargement est allumé par les gestionnaires d'événement ci-dessus, jamais dans l'effet : y appeler setState de façon synchrone provoquerait un rendu en cascade. */ useEffect(() => { let annule = false; async function charger() { try { const [sum, data] = await Promise.all([ getDashboardSummary(), getDashboard(filtreService, filtreCategorie), ]); if (annule) return; setSummary(sum); setMonitorings(Array.isArray(data) ? data : []); setErreur(''); } catch (e) { if (!annule) setErreur(e.message || 'Erreur lors du chargement du dashboard.'); } finally { if (!annule) setLoading(false); } } charger(); return () => { annule = true; }; }, [filtreService, filtreCategorie, rafraichissement]); return (

Dashboard

{/* KPI Cards */} {summary && (
)} {/* Filtres */}
{/* Contenu principal */} {loading ? ( ) : erreur ? ( ) : ( <> {monitorings.length === 0 ? (

Aucun monitoring trouvé pour ces filtres.

) : (
{monitorings.map(m => ( ))}
)} )}
); }