Le bouton cloche portait un aria-label « Notifications » mais aucun gestionnaire
de clic : il n'ouvrait rien et n'était relié à aucune fonctionnalité. Un contrôle
annoncé aux lecteurs d'écran comme actionnable alors qu'il ne fait rien dégrade
l'accessibilité plutôt qu'il ne l'améliore.
- suppression du bouton et de l'icône Bell dans Header.jsx
- retrait de Bell de l'import lucide-react
- suppression de la règle .iconBtn et de son sélecteur :focus-visible dans
Header.module.css, devenus morts
Aucun test ne référençait ce contrôle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Plusieurs pages statiques affirmaient des faits contredits par le code, le dump
SQL ou le dossier de projet. Corrections vérifiées une à une contre les sources.
Documentation :
- React 18 -> React 19 (package.json : react ^19.2.5)
- token « stocké en sessionStorage » -> localStorage (api.js, AuthContext.jsx),
avec HS256 / 60 min / sans refresh token
- « jobs CRON » -> jobs SQL Server Agent (aucun CRON dans le dump)
- « Infrastructure NEXERN / hébergement souverain » -> conteneurs Docker derrière
un reverse proxy ; NEXERN redevient la cible métier
- suppression du monitoring « Infocentre » qui n'existe pas : la nomenclature
livrée compte 6 monitorings Sage et 1 CRM, répartis sur les pôles Contrat (5)
et Fournisseur (2)
- « contrastes conformes » -> objectif WCAG 2.1 AA, audit des contrastes à mener
- rôle « Administrateur » -> « Admin » (valeur réelle de l'énumération)
Mentions légales et CGU :
- éditeur : projet académique de COYAUD Anthony ; XEFI SA devient commanditaire.
L'instance de démonstration n'est ni éditée ni exploitée par XEFI SA
- hébergeur : environnement de démonstration auto-hébergé, NEXERN en cible métier
- section Cookies : l'application ne dépose aucun cookie. La session et le choix
de consentement sont en localStorage, et aucun traceur tiers n'est utilisé
- conservation : la purge à 24 mois porte sur l'historique de monitoring
(TABLE_FINAL) ; la mention « 24 mois à compter de la dernière connexion »
n'était adossée à aucun traitement
- droits RGPD : mise en avant du libre-service réellement implémenté
(GET /me/data-export art. 20, DELETE /me art. 17)
- accessibilité : la conformité WCAG 2.1 AA devient un objectif, avec la liste
des mesures en place et celle des points restant à traiter
- contact dpo@xefi.fr remplacé par a.coyaud@xefi.fr (6 occurrences)
- CGV et modération de commentaires déclarées explicitement sans objet
Build :
- vite.config.js : build.target déclaré explicitement (chrome111, edge111,
firefox114, safari16.4, ios16.4) au lieu de dépendre du défaut de Vite, pour
que la matrice de compatibilité annoncée dans le README soit tracée au dépôt
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le dossier affirmait un focus trap sur les modales et des régions aria-live
pour les mises à jour dynamiques. Ni l'un ni l'autre n'existait : Modal.jsx
tenait en 19 lignes, sans role="dialog", sans aria-modal, sans gestion du
clavier, et aucune occurrence d'aria-live dans le front. Plutôt que retirer
ces affirmations, elles sont rendues vraies.
Modal :
- role="dialog", aria-modal et aria-labelledby relié au titre
- focus placé à l'ouverture sur le premier champ du corps, à défaut sur le
premier élément focusable
- tabulation contenue dans la modale, dans les deux sens
- fermeture par Échap et restitution du focus à l'élément d'origine
- identifiant produit par useId : ni tirage aléatoire pendant le rendu, ni
collision entre deux modales
ErrorMessage et LoadingSpinner reçoivent role="alert"/"status" avec le niveau
d'annonce correspondant ; les éléments purement décoratifs passent en
aria-hidden.
9 tests ajoutés pour que ces comportements soient vérifiables autrement qu'en
relisant le code (41 -> 50 tests front).
Les fonctionnalités ajoutées récemment n'étaient couvertes par aucun test,
et deux utilitaires historiques ne l'avaient jamais été.
- statutMonitoring : bornes du seuil testées de part et d'autre, là où une
confusion entre < et <= se verrait.
- dates : protège la correction du décalage de fuseau (toISOString basculait
en UTC, le 1er janvier local devenait le 31 décembre). Les helpers sont
sortis de Historique.jsx vers utils/dates.js, un fichier de composant ne
pouvant pas exporter autre chose sans casser le rafraîchissement à chaud.
- exportToCSV : échappement des virgules, guillemets et sauts de ligne, et
conservation du zéro, qui est falsy alors que « 0 erreur » est justement
l'information utile. Cet utilitaire sert quatre écrans et n'avait aucun test.
- Dashboard : les menus ne proposent que les valeurs ayant des résultats, les
catégories se restreignent au service choisi, et une catégorie devenue sans
résultat est réinitialisée. Vérifié par mutation : casser la cascade fait
bien échouer deux tests.
- Admin : création, modification pré-remplie, confirmation avant suppression,
remontée du refus 409 de l'API, désactivation et non suppression d'un
monitoring, affichage des libellés à la place des identifiants.
Corrige un défaut révélé par ces tests : referentielsConfig capturait les
fonctions de l'API à l'import, figeant l'implémentation. Elles sont désormais
résolues à l'appel, ce qui découple la configuration du module d'API.
Le dépôt sortait 7 erreurs eslint au début de cette série de correctifs,
dont certaines antérieures. Il n'en reste aucune.
- Dashboard et Users appelaient setState de façon synchrone dans un
effet, ce qui provoque des rendus en cascade. Le chargement initial
passe par une fonction asynchrone qui n'écrit qu'après l'await, avec
annulation au démontage. Sur le Dashboard, l'indicateur de chargement
est allumé par les gestionnaires d'événement, et le bouton Actualiser
relance l'effet via un compteur plutôt qu'en appelant le chargement.
- L'import ExternalLink de Documentation.jsx n'était pas utilisé.
- AuthContext.jsx exportait à la fois un composant, le contexte et un
hook, ce qui désactivait le rafraîchissement à chaud de Vite pour ce
fichier. Le contexte et le hook vivent désormais dans leurs propres
modules ; les 5 fichiers concernés sont mis à jour.
Les boutons Modifier et Désactiver de la page Administration
n'appelaient rien : handleDelete se contentait d'un console.log et le
bouton Enregistrer de la modale ne faisait que la refermer. L'écran
donnait l'illusion d'un CRUD.
Branche les 4 onglets sur les endpoints POST/PUT/DELETE ajoutés côté
API, avec confirmation avant suppression, rechargement après
enregistrement et remontée des erreurs de l'API (dont le refus 409
quand des enregistrements sont rattachés).
Les 4 blocs de colonnes de tableau, quasi identiques d'un onglet à
l'autre, laissent place à une description déclarative
(referentielsConfig.js) : un seul tableau et un seul formulaire
générique (ReferentielForm) servent les 4 référentiels. Les colonnes
qui portaient un identifiant brut affichent maintenant le libellé.
La modale, jusqu'ici déclarée dans Admin.jsx, devient un composant
commun réutilisable.
- KpiCard était déclaré dans Dashboard.jsx : sorti dans son propre
fichier avec ses styles, les règles CSS devenues orphelines sont
supprimées de Dashboard.module.css.
- La clé localStorage 'ds_user' était définie séparément dans api.js
(USER_KEY) et AuthContext.jsx (STORAGE_KEY). AuthContext importe
désormais la constante exportée par api.js : un renommage ne peut
plus casser silencieusement la restauration de session.
- hooks/useApi.js n'était importé nulle part : supprimé.
Le seuil de 10 erreurs qui fait basculer un monitoring en « Critique »
était réécrit dans trois fichiers (MonitoringCard, MonitoringDetail,
VueConso), la version de VueConso ne renvoyant qu'une couleur. Faire
évoluer ce seuil imposait de corriger trois endroits sans en oublier.
Il vit maintenant dans utils/statutMonitoring.js, avec la constante
SEUIL_ATTENTION nommée.
Les colonnes du tableau de monitorings étaient également identiques
entre VueConso et ServiceMonitorings : extraites dans
utils/monitoringColumns.jsx.
La page se limitait aux 5 derniers jours par défaut, alors que
l'historique disponible remonte au 1er janvier. Corrige aussi un bug de
fuseau horaire : les dates par défaut étaient calculées avec
toISOString(), qui bascule en UTC (un 1er janvier saisi depuis Paris,
UTC+1, devenait le 31 décembre de l'année précédente) — remplacé par un
formatage en heure locale.
La page charge maintenant directement cette période au montage, au lieu
d'attendre un clic sur "Afficher" pour afficher autre chose qu'un écran
vide sous les filtres.
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.