Chaque cellule de la grille (label de poste, en-têtes, créneaux, cases vides) recevait sa position via l'ordre d'émission des enfants, en comptant sur le fait que chaque ligne totalise exactement le nombre de tranches de 30 min d'un jour pour que l'auto-flow CSS reste aligné. Un poste dont les créneaux ne tombaient pas exactement sur ce total (deux créneaux adjacents mais non alignés sur 30 min, courant depuis la création de créneaux à la main) décalait tous les postes suivants — leur nom de poste n'apparaissait plus sur la bonne ligne. Chaque cellule reçoit maintenant sa position de grille explicitement (gridColumn/gridRow calculés), immunisé contre ce genre de décalage : l'anomalie d'un poste reste locale à sa propre ligne. Nouveau test de non-régression (scripts/smoke.tsx:checkGridLayout) : inspecte les gridRow réellement rendus plutôt que la simple absence de crash — un décalage CSS n'est pas détectable par un rendu SSR ordinaire. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Frontend — Gestion des bénévoles
React + Vite + TypeScript + Tailwind. Interface en français, code en anglais.
Intègre le design Claude Design « Bénévoles Festival » et consomme l'API
back-end (editions/{id}/volunteers · positions · shifts · assignments · assignments/validate · matrix).
Modes de données
VITE_API_MODE (voir .env.example) :
mock(défaut) — jeu de démonstration déterministe en mémoire, implémenté avec exactement la même surface que l'API (src/api/mock/). Aucun back-end requis ; utile tant que le seed serveur n'existe pas.http— API réelle.VITE_EDITION_IDdésigne l'édition ; en dev Vite proxifie/apiversVITE_API_TARGET(défauthttp://localhost:8000).
Le passage d'un mode à l'autre ne change aucun écran : tout passe par
src/api/ (client HTTP ou mock) et les hooks React Query de src/api/hooks.ts.
Authentification & rôles
Toutes les pages métier sont derrière <RequireAuth> (src/auth/).
- Coordination →
/connexion/coordination: email + mot de passe (POST /editions/{id}/auth/login). - Référents / bénévoles →
/connexion: lien magique (POST …/auth/magic-linkpuis…/auth/verify). L'email étant stubé, le modemockaffiche directement le lien de démo à cliquer. - Le jeton JWT est stocké en
localStorage, envoyé enAuthorization: Bearersur chaque requête ; une réponse 401 déconnecte. - Comptes de démo (mode
mock) : coordinationcoordination@lessonsdautomne.fr/demo; référentsreferent1..3@lessonsdautomne.fr; chaque bénévole ayant un email.
Visibilité des onglets par rôle :
| Onglet | Coordination | Référent | Bénévole |
|---|---|---|---|
| 01 Grille · 02 Vue bénévole · 06 Configuration | ✅ | — | — |
| 04 Bénévole · mobile | — | — | ✅ |
| 05 Référent · mobile | — | ✅ | — |
L'accueil après connexion dépend du rôle (homePath).
Écrans (onglets)
Depuis l'Étape 9 back-end, une affectation n'a plus de statut : elle est valide dès sa création, la retirer = supprimer la ligne. Il n'y a donc plus d'écran « Confirmations » (ancien onglet 03).
| # | Route | Source de données | Actions |
|---|---|---|---|
| 01 | /grille |
GET /matrix (assigned / ideal) |
clic cellule → panneau : GET /assignments?shift_id, POST /assignments/validate par candidat, POST / DELETE /assignments |
| 02 | /benevoles |
volunteers + shifts + assignments |
lecture (surcharge / oubliés calculés côté client) |
| 04 | /mobile-benevole |
assignments?volunteer_id (du compte) |
lecture¹ |
| 05 | /mobile-referent |
poste du référent → assignments?shift_id |
pointage local (hors périmètre) |
| 06 | /configuration |
positions · shifts · volunteers |
CRUD postes / créneaux (min/idéal) / bénévoles (dont suppression RGPD) ; onglet Messages → POST /notifications/planning (récapitulatif + lien de connexion) |
¹ En mode http, l'API n'expose pas encore aux bénévoles le détail des
créneaux : l'écran dégrade proprement (créneaux sans détail, message d'info).
Fonctionne intégralement en mode mock.
Commandes
| Action | Commande |
|---|---|
| Installer | npm install |
| Dev | npm run dev → http://localhost:5173 |
| Build | npm run build |
| Lint | npm run lint |
| Types | npm run typecheck |
| Rendu headless + vérifs API mock | npm run smoke |
Conventions
- Aucun texte d'interface en dur :
src/i18n(t(key, params)) +locales/fr.json. - Les libellés de données (nom de poste, etc.) viennent de l'API.
src/api/schema.tsest le miroir debackend/app/api/schemas.py— à garder synchrone.violationText()(src/lib/planning.ts) rend les violations du moteur ; lesparamssuivent le contrat debackend/app/validation/engine.py.- Alias
@/→src/.
Arborescence
src/
├── main.tsx
├── api/
│ ├── schema.ts # types miroir du back-end (dont auth)
│ ├── types.ts # interface PlanningApi + ApiError
│ ├── session.ts # jeton Bearer partagé + callback 401
│ ├── http-client.ts # implémentation HTTP
│ ├── mock/ # implémentation mémoire (dataset + client + comptes + moteur)
│ ├── index.ts # sélection mock / http, EDITION_ID
│ └── hooks.ts # hooks React Query (lectures + mutations)
├── auth/ auth-context (AuthProvider/useAuth) · RequireAuth · roles
├── app/ AppShell (onglets filtrés par rôle) · router
├── pages/ un fichier par écran (+ auth/, grille/, config/, mobile/)
├── components/ ui.tsx (primitives) · state.tsx (Loading / ErrorNote)
├── lib/ time.ts · planning.ts (treillis, heures, surcharge, violationText) · env.ts · cx.ts
└── i18n/ t() + locales/fr.json