Files
Front-DataSentinel/DataSentinel/src/pages/Dashboard/Dashboard.jsx
T
Antho 25a31feec4 feat(dashboard): filtres services/catégories restreints aux résultats
Les select de service et catégorie utilisaient getServices()/getCategories(),
soit les référentiels complets — dont certaines entrées (ex. "Business
Intelligence") n'ont aucun monitoring rattaché et menaient sur un dashboard
vide. Bascule vers le nouvel endpoint GET /dashboard/filtres (VUE_CONSO),
avec filtrage en cascade : sélectionner un service restreint la liste des
catégories proposées à celles qui ont réellement des résultats pour ce
service.
2026-08-15 13:28:36 +02:00

173 lines
5.5 KiB
React

/* 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 LoadingSpinner from '../../components/common/LoadingSpinner';
import ErrorMessage from '../../components/common/ErrorMessage';
import styles from './Dashboard.module.css';
/* Carte KPI affichant un indicateur clé */
function KpiCard({ label, value, color }) {
return (
<div className={styles.kpiCard}>
<span className={styles.kpiValue} style={{ color }}>{value}</span>
<span className={styles.kpiLabel}>{label}</span>
</div>
);
}
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('');
/* 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) {
setFiltreService(service);
if (filtreCategorie && !categoriesDisponibles(service).includes(filtreCategorie)) {
setFiltreCategorie('');
}
}
/* Chargement des données dashboard */
const loadData = useCallback(async () => {
setLoading(true);
setErreur('');
try {
const [sum, data] = await Promise.all([
getDashboardSummary(),
getDashboard(filtreService, filtreCategorie),
]);
setSummary(sum);
setMonitorings(Array.isArray(data) ? data : []);
} catch (e) {
setErreur(e.message || 'Erreur lors du chargement du dashboard.');
} finally {
setLoading(false);
}
}, [filtreService, filtreCategorie]);
useEffect(() => {
loadData();
}, [loadData]);
return (
<div className={styles.page}>
<div className={styles.pageHeader}>
<h1 className={styles.title}>Dashboard</h1>
<button className={styles.refreshBtn} onClick={loadData} disabled={loading}>
<RefreshCw size={14} className={loading ? styles.spinning : ''} />
Actualiser
</button>
</div>
{/* KPI Cards */}
{summary && (
<div className={styles.kpiGrid}>
<KpiCard
label="Total monitorings"
value={summary.total ?? 0}
color="var(--color-text)"
/>
<KpiCard
label="Sans erreur"
value={summary.ok ?? 0}
color="var(--color-ok)"
/>
<KpiCard
label="En erreur"
value={summary.critical ?? 0}
color="var(--color-primary)"
/>
<KpiCard
label="Total erreurs"
value={summary.total_erreurs ?? 0}
color="var(--color-warning)"
/>
</div>
)}
{/* Filtres */}
<div className={styles.filtres}>
<select
className={styles.select}
value={filtreService}
onChange={e => handleServiceChange(e.target.value)}
>
<option value="">Tous les services</option>
{services.map(s => (
<option key={s} value={s}>{s}</option>
))}
</select>
<select
className={styles.select}
value={filtreCategorie}
onChange={e => setFiltreCategorie(e.target.value)}
>
<option value="">Toutes les catégories</option>
{categories.map(c => (
<option key={c} value={c}>{c}</option>
))}
</select>
</div>
{/* Contenu principal */}
{loading ? (
<LoadingSpinner message="Chargement du dashboard..." />
) : erreur ? (
<ErrorMessage message={erreur} onRetry={loadData} />
) : (
<>
{monitorings.length === 0 ? (
<p className={styles.empty}>Aucun monitoring trouvé pour ces filtres.</p>
) : (
<div className={styles.grid}>
{monitorings.map(m => (
<MonitoringCard key={m.id} monitoring={m} />
))}
</div>
)}
</>
)}
</div>
);
}