feat(sites) : barre de selection de site (ticket 3/4)

Barre horizontale en haut de l'app qui liste les sites autorises de
l'utilisateur et permet de switcher d'un click. Consomme le composant
MalioSiteSelector de @malio/layer-ui 1.4.0 (upgrade depuis 1.3.0).

Composables :
- useModules (shared) : consomme /api/modules, expose isModuleActive.
  Pattern aligne sur useSidebar.
- useCurrentSite (layer sites) : singleton state, switchSite optimistic
  avec rollback sur erreur, garde anti-double-submit, propagation au
  store auth via action setCurrentSite dediee.

Composant :
- SiteSelector.vue : wrapper thin autour de MalioSiteSelector. Texte
  blanc uniforme (conforme maquette Figma) avec taille 24px forcee via
  labelClass="text-2xl". aria-label du group via ariaGroupLabel i18n.

Integration :
- Middleware auth.global.ts : chargement parallele sidebar + modules.
- layouts/default.vue : render conditionnel si module Sites actif ET
  user.sites.length > 0.
- logout.vue : reset des 3 composables (sidebar, modules, currentSite)
  dans un try/finally.
- nuxt.config.ts : auto-detection des composables/ de chaque layer
  module (necessaire car imports.dirs explicite override les defaults
  Nuxt).

Couleurs fixtures finales : Chatellerault #056CF2, Saint-Jean #F3CB00,
Pommevic #74BF04. Charge aux admins de choisir des teintes foncees
(texte blanc non contrastable via calcul WCAG, design choisi).

Tests : 40 Vitest (color, useModules, useSidebar, useCurrentSite,
SiteSelector) incluant garde anti-regression pour useI18n hors setup.
182/182 PHPUnit backend, avec et sans module actif.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-20 11:45:48 +02:00
parent d137828919
commit 03c761eed4
23 changed files with 951 additions and 76 deletions

View File

@@ -1,5 +1,44 @@
# Ticket #03 — 3/4 — Barre de sélection de site (navbar horizontale)
## 0. Pivots post-implémentation (2026-04-20)
Écarts assumés entre la spec initiale (écrite avant exploration de la lib) et
le code livré après implémentation et test visuel. À lire en premier pour
comprendre les divergences lors de la relecture.
1. **Contraste texte auto supprimé, texte blanc forcé conforme Figma.**
La spec (sections 5, 6, 10) prévoyait un calcul de luminance WCAG pour
décider entre texte noir et blanc sur chaque tile. Après test visuel, le
choix design retenu est d'imposer **texte blanc partout** (default Malio
`text-white font-bold uppercase tracking-wide`). Conséquence : charge à
l'admin de choisir des couleurs de site suffisamment foncées pour que le
blanc reste lisible. Les utilitaires `parseHex`, `getRelativeLuminance`,
`getReadableTextColor` ont été supprimés comme code mort. Seul
`isValidSiteColor(hex)` reste dans `shared/utils/color.ts` (consommé par
`SiteDrawer`).
2. **Taille texte explicite `text-2xl` (24 px) appliquée via `labelClass`.**
Malio applique `font-bold uppercase tracking-wide` sans taille explicite.
Le wrapper `SiteSelector.vue` passe `labelClass="text-2xl"` pour garantir
les 24 px de la maquette Figma.
3. **A11y : `ariaGroupLabel` au niveau radiogroup** au lieu de
`ariaLabelActive` / `ariaLabelInactive` par tile. La raison : Malio rend
déjà un `role="radio"` avec `aria-checked` par tile — le lecteur d'écran
annonce "bouton radio coché/non coché" + le nom visible. Ajouter un
`aria-label` par tile aurait dupliqué l'info et alourdi sans bénéfice.
Le seul ajout nécessaire était un label au groupe, fait via
`:aria-label="t('sites.selector.ariaGroupLabel')"` sur `MalioSiteSelector`.
4. **Auto-détection composables des layers dans `nuxt.config.ts`.**
Pas prévu dans la spec. Ajouté car `imports.dirs` explicite override les
auto-imports par défaut de Nuxt pour les composables de layer. Sans ça,
`useCurrentSite` n'est pas résolu par Nuxt. Scan dynamique aligné sur le
pattern `moduleLayers` existant.
5. **Couleurs fixtures finales :** `#056CF2` (Châtellerault), `#F3CB00`
(Saint-Jean), `#74BF04` (Pommevic). Choix client post-maquette.
## 1. Objectif
Ce ticket livre l'UI de consommation du module Sites pour l'utilisateur final : une barre horizontale en haut de l'application qui liste les sites autorises de l'utilisateur connecte, met en avant le site courant et permet de basculer d'un site a l'autre en un clic.