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.
This commit is contained in:
2026-08-15 13:28:36 +02:00
parent e741065e14
commit 25a31feec4
2 changed files with 47 additions and 10 deletions
+35 -10
View File
@@ -1,8 +1,8 @@
/* Page principale du dashboard : KPI cards + grille de monitorings */ /* Page principale du dashboard : KPI cards + grille de monitorings */
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback, useMemo } from 'react';
import { RefreshCw } from 'lucide-react'; import { RefreshCw } from 'lucide-react';
import { getDashboard, getDashboardSummary, getServices, getCategories } from '../../services/api'; import { getDashboard, getDashboardSummary, getDashboardFiltres } from '../../services/api';
import MonitoringCard from '../../components/common/MonitoringCard'; import MonitoringCard from '../../components/common/MonitoringCard';
import LoadingSpinner from '../../components/common/LoadingSpinner'; import LoadingSpinner from '../../components/common/LoadingSpinner';
import ErrorMessage from '../../components/common/ErrorMessage'; import ErrorMessage from '../../components/common/ErrorMessage';
@@ -22,19 +22,22 @@ export default function Dashboard() {
const [summary, setSummary] = useState(null); const [summary, setSummary] = useState(null);
const [monitorings, setMonitorings] = useState([]); const [monitorings, setMonitorings] = useState([]);
const [services, setServices] = useState([]); const [services, setServices] = useState([]);
const [categories, setCategories] = useState([]); const [combinaisons, setCombinaisons] = useState([]);
const [filtreService, setFiltreService] = useState(''); const [filtreService, setFiltreService] = useState('');
const [filtreCategorie, setFiltreCategorie] = useState(''); const [filtreCategorie, setFiltreCategorie] = useState('');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [erreur, setErreur] = useState(''); const [erreur, setErreur] = useState('');
/* Chargement initial des filtres */ /* 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(() => { useEffect(() => {
async function loadFiltres() { async function loadFiltres() {
try { try {
const [svcs, cats] = await Promise.all([getServices(), getCategories()]); const filtres = await getDashboardFiltres();
setServices(svcs); setServices(filtres.services);
setCategories(cats); setCombinaisons(filtres.combinaisons);
} catch { } catch {
/* Non bloquant, les filtres restent vides */ /* Non bloquant, les filtres restent vides */
} }
@@ -42,6 +45,28 @@ export default function Dashboard() {
loadFiltres(); 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 */ /* Chargement des données dashboard */
const loadData = useCallback(async () => { const loadData = useCallback(async () => {
setLoading(true); setLoading(true);
@@ -105,11 +130,11 @@ export default function Dashboard() {
<select <select
className={styles.select} className={styles.select}
value={filtreService} value={filtreService}
onChange={e => setFiltreService(e.target.value)} onChange={e => handleServiceChange(e.target.value)}
> >
<option value="">Tous les services</option> <option value="">Tous les services</option>
{services.map(s => ( {services.map(s => (
<option key={s.id_service} value={s.nom_service}>{s.nom_service}</option> <option key={s} value={s}>{s}</option>
))} ))}
</select> </select>
<select <select
@@ -119,7 +144,7 @@ export default function Dashboard() {
> >
<option value="">Toutes les catégories</option> <option value="">Toutes les catégories</option>
{categories.map(c => ( {categories.map(c => (
<option key={c.id_categorie} value={c.intitule_categorie}>{c.intitule_categorie}</option> <option key={c} value={c}>{c}</option>
))} ))}
</select> </select>
</div> </div>
+12
View File
@@ -245,6 +245,18 @@ export async function getDashboard(service, categorie) {
})) : data; })) : data;
} }
/* Récupère les valeurs de filtre qui ramènent au moins un monitoring (VUE_CONSO).
Évite de proposer un service ou une catégorie du référentiel qui n'a aucun
monitoring rattaché, et donc de mener l'utilisateur sur un écran vide. */
export async function getDashboardFiltres() {
const data = await fetchApi('/dashboard/filtres');
return {
services: Array.isArray(data?.services) ? data.services : [],
categories: Array.isArray(data?.categories) ? data.categories : [],
combinaisons: Array.isArray(data?.combinaisons) ? data.combinaisons : [],
};
}
/* Récupère le résumé global KPI du dashboard */ /* Récupère le résumé global KPI du dashboard */
export async function getDashboardSummary() { export async function getDashboardSummary() {
const data = await fetchApi('/dashboard/summary'); const data = await fetchApi('/dashboard/summary');