Deux bugs remontés sur l'écran référent (/mobile-referent), tous deux
révélés par le repli documenté « décocher un poste ne rétrograde jamais
le compte » (§2) :
- Un compte LEAD qui ne référence plus aucun poste (ex. décoché) reste
LEAD et atterrit donc sur /mobile-referent après son lien magique —
qui affichait un écran référent vide au lieu de sa vue de bénévole
classique. `MobileLeadPage` détecte maintenant ce cas et affiche,
quand le compte est rattaché à une fiche bénévole, le même contenu
que `/mobile-benevole` (planning : poste, jour, horaire, lieu,
responsable, téléphone) — extrait dans un composant partagé
`VolunteerHomeView`.
- Un poste avec plusieurs créneaux n'en montrait qu'un seul (le plus
proche de l'heure réelle) : le référent ne pouvait pas voir son
équipe sur les autres créneaux. La carte de chaque poste liste
maintenant tous ses créneaux, chacun avec sa propre équipe, avec un
repère « MAINTENANT » (composant partagé `NowLine`, déjà utilisé côté
bénévole) pour se situer parmi eux.
Non inclus dans ce commit (édités en parallèle par l'utilisateur, laissés
en l'état) : backend/app/seed.py, frontend/tailwind.config.js.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Deux bugs sur l'écran référent (/mobile-referent) :
- Il était resté à l'étape maquette (`PhoneFrame`/`DesignNote`, cadre de
téléphone à taille fixe) alors que la vue bénévole avait déjà été
convertie en vraie page responsive. Repli sur `positions.data[0]` en
l'absence de poste géré par le compte connecté : un référent qui
n'est encore désigné sur aucun poste se voyait quand même affiché
comme référent du premier poste de l'édition (« Accueil artistes »
dans les données de démo) au lieu de voir qu'il n'en a aucun.
Réécriture de `MobileLeadPage.tsx` en page responsive (mobile d'abord,
`sm:` pour l'élargissement, comme `MobileVolunteerPage.tsx`) : plus de
maquette de téléphone, une carte par poste réellement géré
(`Position.lead_account_id === account.id`, sans repli), créneau en
cours ou à venir calculé sur l'heure réelle (nouveau hook partagé
`useNowMinutes`, extrait de la vue bénévole), équipe affectée avec
pointage présent/absent (toujours local à l'écran : le pointage sur
site reste hors périmètre, CLAUDE.md §5). État vide explicite si le
compte n'est référent d'aucun poste.
`PhoneFrame`/`DesignNote` n'avaient plus d'autre utilisateur : supprimés
avec leurs clés i18n devenues mortes.
Non inclus dans ce commit (édités en parallèle par l'utilisateur, laissés
en l'état) : backend/app/seed.py, frontend/tailwind.config.js.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
La vue bénévole reprenait les infos essentielles (poste, jour, horaire,
lieu) mais affichait un badge de validation factice (« TU ES ATTENDU
SUR CE CRÉNEAU ») sans plus-value, sans jamais donner le nom ni le
téléphone du référent du poste à contacter — pourtant déjà modélisés
(`Position.lead_account_id`) depuis la gestion des référents.
Un compte VOLUNTEER n'a pas accès à `positions`/`shifts` (403, cf. § 2
matrice des accès) : ces infos ne pouvaient donc pas être jointes côté
front à partir des listes existantes. Nouvel endpoint dédié et
minimal, réservé à la lecture du planning propre du bénévole :
GET /editions/{id}/volunteers/{vid}/planning
Renvoie, par affectation : poste, lieu, et référent (nom + téléphone,
`null` si le poste n'a pas de référent ou si son compte n'est pas
rattaché à une fiche bénévole) — sans exposer la liste complète des
postes/créneaux de l'édition. Tests : accès (soi-même / autre
bénévole 403 / COORD+LEAD), présence/absence du référent, tri par
heure de début.
Front : retrait du badge de validation, ajout des lignes responsable/
téléphone par créneau, et un repère horizontal « MAINTENANT » qui se
positionne dans la liste triée des créneaux selon l'heure réelle (juste
avant le prochain créneau à venir) pour situer le bénévole dans son
planning.
Non inclus dans ce commit (édités en parallèle par l'utilisateur,
laissés en l'état) : backend/app/seed.py, frontend/tailwind.config.js.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Un poste avait bien un `lead_account_id`, lu par l'écran référent
(`/mobile-referent`) et par les droits d'écriture (`ensure_can_write_position`),
mais rien ne permettait de le renseigner : aucun écran ne l'exposait, et
l'API ne validait même pas que la valeur envoyée désignait un compte référent
existant de la bonne édition.
Backend :
- `PATCH /editions/{id}/accounts/{account_id}` (nouveau) : change le `role`
d'un compte, seulement entre LEAD et VOLUNTEER (lien magique dans les deux
cas) — 422 `account.role_change_not_allowed` dès que COORDINATOR est
impliqué (mot de passe en jeu, jamais touché ici). C'est ce qui permet de
faire passer le compte d'un bénévole en référent.
- `POST`/`PATCH /positions` : `lead_account_id` vérifié — doit désigner un
compte `Role.LEAD` de la même édition, sinon 422
`position.lead_must_be_referent_account` (silencieusement accepté
jusqu'ici, y compris un compte d'une autre édition ou du mauvais rôle).
Frontend (`ConfigVolunteers.tsx`) : nouvelle section « Référent des postes »
sur la fiche du bénévole, juste après l'accès de connexion — mêmes badges à
cocher que les vœux de poste/artiste. Cocher un poste crée ou met à niveau
(LEAD) l'accès de connexion du bénévole automatiquement ; décocher retire
seulement le poste, sans jamais rétrograder l'accès. Un bénévole dont le
compte est déjà COORDINATOR ne peut pas être désigné référent (il a déjà
accès à tout). Miroir complet côté mock (`accounts.update`,
`checkLeadAccount`) pour que la démonstration se comporte pareil.
Vérifié : 204 tests backend pertinents (14 nouveaux : 6 sur `PATCH accounts`,
8 sur la validation `lead_account_id`), ruff, alembic check ; typecheck/
lint/build/smoke frontend ; parcours complet testé en direct sur le jeu de
démo (création de compte, promotion LEAD, rejet COORDINATOR, affectation
référent) avant nettoyage des données de test.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
C'est l'écran d'accueil réel d'un compte bénévole (`homePath`) — consulté sur
son propre téléphone la plupart du temps, mais la page rendait un cadre de
téléphone factice à taille fixe (376×772 px, fausse barre de statut) pensé
pour être visionné sur un écran d'ordinateur, plus une note de choix de
conception destinée à l'équipe, pas au bénévole. Sur un vrai téléphone,
c'était un téléphone dessiné à l'intérieur du vrai téléphone.
- Retiré le cadre (`PhoneFrame`) et la note de conception (`DesignNote`) —
toujours utilisés par la page référent (`MobileLeadPage.tsx`, non touchée
ici), donc le composant partagé reste en place.
- Le contenu (bandeau d'accueil + cartes de créneaux) devient la page
elle-même : pleine largeur sur mobile, centrée avec une largeur de lecture
confortable (max 640 px) et un peu plus d'air à partir de `sm:` pour un
écran d'ordinateur. Mêmes repères de conception qu'avant (fonds opaques,
cibles tactiles ≥ 52 px), déplacés en commentaire de code.
- `AppShell` : la barre d'onglets ne s'affiche plus quand il n'y a qu'un seul
écran accessible (bénévole, référent) — elle n'a de sens que pour la
coordination (3 écrans) ; simplifie l'en-tête sur les rôles qui n'ont nulle
part où naviguer.
Vérifié : typecheck, lint, build, smoke frontend OK.
À noter : `MobileLeadPage.tsx` (référent) a exactement le même défaut
(maquette de téléphone + note de conception) — pas touchée ici, la demande
portait sur la vue bénévole.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Chaque case ne montrait qu'un nombre (affecté/idéal) ; il fallait cliquer et
ouvrir le panneau pour savoir qui exactement. Une bulle au survol affiche
maintenant les noms des bénévoles déjà affectés à ce créneau, sans clic.
- Construit une table (créneau → noms) à partir des affectations et des
bénévoles de l'édition (mêmes requêtes déjà utilisées par le panneau
d'affectation, donc pas de coût réseau supplémentaire une fois en cache).
- Remplace l'ancien `title` natif (lent, sans style) par la même bulle que
`HelpTip` (bordures franches, apparaît sous la case) ; garde le rappel
affecté/idéal/minimum en tête de bulle, puis la liste triée des noms, ou
le message « personne d'affecté » sinon.
Vérifié : typecheck, lint, build, smoke frontend OK.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
La liste des candidats se limitait aux 10 mieux classés par le score de
chance — un bénévole précis pouvait ne jamais y apparaître. Un champ de
recherche (nom/prénom, insensible aux accents/casse) permet maintenant de le
retrouver et de l'affecter directement, sans être limité au classement.
- Recherche vide : comportement inchangé (top 10 par score de chance).
- Recherche non vide : jusqu'à 20 bénévoles non affectés dont le nom
correspond, triés par le même score ; message dédié si aucun ne correspond.
- `foldAccents` (accents/casse) déplacé de `ConfigVolunteers.tsx` vers
`lib/planning.ts` pour être partagé avec le panneau d'affectation.
- `GrillePage.tsx` : `key={shift.shift_id}` sur `AssignmentPanel` pour que la
recherche (et tout futur état local) reparte à zéro en changeant de
créneau, plutôt que de persister sur le suivant.
Vérifié : typecheck, lint, build, smoke frontend OK.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
- Vœu de compagnon : le sélecteur (déjà trié par ordre alphabétique) gagne un
champ de recherche (insensible aux accents/casse) qui filtre la liste
déroulante — plus pratique avec 100+ bénévoles.
- Vœux d'artiste et de poste : remplacé le menu déroulant par des badges à
cocher (`BadgeWishPicker`, réutilise le composant `Chip`) — toutes les
options sont visibles d'un coup, un clic les bascule, plus besoin d'ouvrir
une liste pour un petit nombre d'options.
Vérifié : typecheck, lint, build, smoke frontend OK.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Nouveau vœu sur la fiche du bénévole — « je voudrais être affecté avec tel(s)
bénévole(s) » — pris en compte à la fois pour le rendre visible et par le
solveur.
Backend :
- `VolunteerCompanionWish` (`volunteer_id`, `companion_id`, à sens unique,
`CHECK (volunteer_id <> companion_id)`) — migration 0010.
- API : `wished_companion_ids` sur `VolunteerCreate`/`Update`/`Read`, mêmes
règles que les autres vœux (ids inconnus → 422 `volunteer.unknown` ; se
souhaiter soi-même → 422 `volunteer.cannot_wish_self`).
- Solveur (`engine.py`) : bonus (+25 par correspondance) si un des compagnons
souhaités du candidat est déjà affecté (décision déjà prise) au créneau —
cumulable si plusieurs compagnons sont présents. Comme les autres termes de
l'objectif, un bonus/malus, jamais une contrainte.
- Seed de démo : ~20 % des bénévoles ont un vœu de compagnon (couple, amis).
Frontend :
- Fiche bénévole (`ConfigVolunteers.tsx`) : sélecteur de compagnons (ajout et
édition), avec une bulle d'aide expliquant le sens unique et l'effet sur le
solveur — c'est ce qui « fait apparaître » le lien entre bénévoles.
- Panneau d'affectation : badge « avec N compagnon(s) souhaité(s) » sur un
candidat, et bonus correspondant dans le classement par défaut
(`candidateChanceScore`).
- Mock (`runSolver`) : même bonus, pour que la démonstration se comporte
pareil que le vrai solveur.
CLAUDE.md à jour (modèle de domaine, section 4, journal).
Vérifié : 194 tests backend (dont 13 nouveaux), ruff, alembic check ;
typecheck/lint/build/smoke frontend ; solveur testé en direct sur le jeu de
démo reseedé (0,25 s, 47 suggestions).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Root cause du « je n'ajoute un bénévole avec son mail, je ne reçois aucun
mail » : renseigner un email sur la fiche d'un bénévole ne crée jamais de
compte (`Account`). `POST .../auth/magic-link` cherche un compte par email
et ne fait rien s'il n'en trouve pas (202 quoi qu'il arrive, par conception,
pour ne jamais révéler si un compte existe) — silencieusement, sans erreur.
L'API `POST/GET/DELETE /editions/{id}/accounts` existait déjà côté backend
(testée) mais n'avait jamais été branchée au frontend.
- `api/schema.ts` / `types.ts` / `http-client.ts` / `hooks.ts` : nouvelle
surface `accounts` (list/create/remove) + `useAccounts`, `useCreateAccount`,
`useDeleteAccount`.
- `mock-client.ts` : même surface (règles miroir du backend : email unique,
mot de passe interdit hors COORDINATOR) ; la suppression d'un bénévole
supprime aussi son compte (cascade RGPD, alignée sur le backend).
- `ConfigVolunteers.tsx` : section « Accès de connexion » sur la fiche du
bénévole — bouton pour donner l'accès (crée un compte VOLUNTEER, lien
magique) si un email est renseigné, ou le révoquer si un accès existe déjà.
- Corrigé au passage : `config.vol.addHint` affirmait qu'un email donnait
l'accès automatiquement — c'était faux, texte mis à jour.
- CLAUDE.md à jour (section authentification).
Vérifié : typecheck, lint, build, smoke (7 routes) OK.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Plusieurs bénévoles peuvent légitimement partager une même adresse email —
l'architecture l'interdisait : l'import ignorait silencieusement le second
(« duplicate_email_in_file »), et l'ajout manuel renvoyait un 409. Les deux
sont corrigés.
- Migration 0009 : retrait de la contrainte d'unicité (edition_id, email) sur
`volunteer` (l'index sur `email` seul est conservé).
- Import (`app/importing/service.py`) : l'upsert se fait désormais sur
(email, nom + prénom), pas sur l'email seul. Un même email revu avec un nom
différent dans le fichier crée un second bénévole (nouveau code
`import.row.shared_email`, WARNING) au lieu d'être ignoré ;
`duplicate_email_in_file` ne se déclenche plus que sur une ligne
littéralement répétée (même email ET même nom).
- API (`POST`/`PATCH /volunteers`) : retrait du rejet 409
`volunteer.duplicate_email`, devenu sans objet (le mock ne l'a d'ailleurs
jamais appliqué à la création manuelle).
- Mock frontend (`mock-client.ts`) : même correction de l'upsert par import,
pour rester cohérent avec le backend.
- CLAUDE.md à jour (modèle de domaine, règles d'import, journal).
Contrepartie assumée (documentée) : un email re-importé avec un nom
légèrement différent (faute de frappe corrigée) crée désormais un bénévole
de plus signalé, plutôt que de renommer silencieusement l'existant — même
philosophie que la détection sans email, qui ne fusionne jamais rien
automatiquement.
Vérifié : 179 tests backend, ruff, alembic check ; typecheck/lint/build/smoke
frontend ; reseed de démonstration.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Dans le panneau d'affectation, la liste des candidats d'un créneau
n'était triée que par disponibilité puis par nombre d'heures déjà
assignées — les vœux n'y jouaient aucun rôle tant qu'on ne cliquait pas
sur « Suggestions (solveur) ».
Nouveau `candidateChanceScore` (`lib/planning.ts`), portage local et
synchrone du scoring du solveur : vœu de poste (bonus), disponibilité
déclarée y compris conflit avec un artiste souhaité (malus), charge déjà
assignée (malus). C'est ce score qui classe désormais la liste par
défaut, dès l'ouverture du détail d'un poste — sans requête réseau ni
clic sur le solveur. Si celui-ci a effectivement tourné, ses candidats
retenus (vraie optimisation jointe) restent prioritaires en tête.
Chaque candidat affiche maintenant, en plus de sa disponibilité, des
badges « Vœu de poste » et « Manquerait un artiste souhaité » quand
applicable — pour que le classement reste lisible.
typecheck / lint / build / smoke au vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
- ArtistRead/Create/Update gagnent start_time/end_time.
- Onglet Programmation : chaque artiste a désormais deux champs heure
(début/fin de passage), éditables comme le nom.
- Mock du solveur : porte le même malus de conflit d'artiste que le
back-end (un créneau qui chevauche un passage souhaité est pénalisé),
pour un comportement de démo cohérent.
- Jeu de démo : 2 artistes par jour avec des horaires distincts
(18h-19h / 20h30-22h) au lieu d'un horaire par défaut implicite.
typecheck / lint / build / smoke au vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
- Surface API `solver.run(editionId)` (HTTP + mock) et hooks
`useRunSolver` (mutation, alimente le cache) / `useSolverSuggestions`
(lecture seule du cache, ne déclenche jamais de calcul).
- Grille des besoins : bouton « Suggestions (solveur) », affiche le
nombre de suggestions calculées.
- Panneau d'affectation d'un créneau : les bénévoles suggérés par le
solveur passent en tête de la liste des candidats (avant le tri par
disponibilité / charge), avec un badge « Suggéré ». Ne pré-affecte
jamais personne — la coordination choisit et confirme comme avant.
- Mock : portage glouton (par créneau, du plus urgent au moins urgent)
du scoring du moteur back-end, pour une démonstration cohérente sans
CP-SAT côté navigateur.
typecheck / lint / build / smoke au vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Aligne le frontend sur le back-end (étape 14).
- Types : suppression de `skill_codes` / `required_skill_codes` /
`AvailabilityStatus` / `MISSING_REQUIRED_SKILL` ; ajout de
`AvailabilityRead` / `AvailabilitySet` et de la surface API
`availability` (list / replace / listEdition) + hooks associés.
- Fiche bénévole : bloc « compétences » remplacé par un éditeur de
disponibilités (plages date + heures libres, `PUT` en bloc).
- Assistant d'import : plus de colonnes compétences ni disponibilités
(mapping réduit à l'identité) ; rapport simplifié.
- Grille des besoins : chaque candidat porte un badge de couverture
(disponible / partiel / non déclaré) calculé sur ses fenêtres, et la
liste est triée « disponibles d'abord ». Nouvel util
`availabilityCoverage` / `uncoveredMinutes` dans `lib/planning`,
portage du moteur back-end.
- Mock : `state.availability` passe à des fenêtres `AvailabilityRead[]`,
validation `OUTSIDE_AVAILABILITY` recalculée sur l'union des fenêtres,
jeu de démo et import CSV adaptés.
typecheck / lint / build / smoke au vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Aligne le frontend sur le back-end (étape 13).
- Types : retrait de `tshirt_size` / `diet` (Volunteer et mapping
d'import) ; ajout de `ArtistRead/Create/Update` et des champs
`artist_ids` / `wished_position_ids` sur la fiche bénévole.
- Surface API `artists` (HTTP + mock) et hooks
`useArtists` / `useCreateArtist` / `useUpdateArtist` / `useDeleteArtist`.
- Nouvel onglet Configuration « Programmation » : liste d'artistes
éditable, colonne par jour de l'édition (création, renommage,
suppression ; 409 doublon, 422 nom vide).
- Fiche bénévole & formulaire d'ajout manuel : champs t-shirt / régime
retirés, sélecteurs « Artistes à voir » et « Postes souhaités »
(puces retirables, 422 si id étranger).
- Mock : jeu de démo enrichi (2 artistes par jour, vœux aléatoires),
cascade sur suppression d'artiste / de poste, import CSV sans
t-shirt / régime.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Écran « Bénévoles & import » remanié autour des trois méthodes d'ajout
demandées (la 3e — formulaire d'inscription en ligne — reste à faire) :
- Ajout manuel : formulaire complet (prénom, nom, email, téléphone,
taille de t-shirt, régime, commentaire, compétences) ; l'erreur 409
`volunteer.duplicate_email` est affichée.
- Import de fichier : assistant en 3 étapes (fichier → association des
colonnes + colonnes de disponibilité optionnelles → simulation puis
import réel) avec aperçu et rapport ligne par ligne.
- Gestion : recherche par nom / email, fiche éditable étendue (régime et
commentaire ajoutés), suppression RGPD en cascade inchangée.
Le bouton « créer 4 exemples » (maquette) est retiré. Traductions
`config.vol.*` et miroir des messages `import.*` du back-end.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
- components/state : composant EmptyNote (encadré pointillés).
- GrillePage : si aucun créneau, message au lieu de la grille vide.
- VolunteerViewPage : si aucun bénévole, message ; si un filtre ne
renvoie rien, message à la place du tableau (avant, l'en-tête collant
restait affiché).
typecheck + lint + build + smoke OK.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Les créneaux viennent des créneaux types (génération à la création d'un poste
+ « Générer les créneaux »). Le bouton par jour n'a plus lieu d'être.
Nettoyage : hooks/clés i18n devenus inutiles.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Le paramétrage des créneaux se fait dans « Règles & édition » : la coordination
définit des créneaux types (DayPart). À la création d'un poste, un créneau à
besoin 0/0 est généré pour chaque jour de l'édition et chaque créneau type ;
les coordinateurs ajustent ensuite min/idéal.
Back-end :
- DayPart CRUD (GET/POST/PATCH/DELETE /editions/{id}/day-parts). Code figé
après création (slugifié depuis le libellé si absent), unique par édition.
- Shift.day_part_id (nullable, SET NULL) + migration 0004.
- create_position génère les créneaux ; POST .../positions/{pid}/generate-shifts
recrée les manquants (idempotent, ne touche pas aux besoins déjà réglés).
- 13 tests (day-parts, génération, extension d'édition, ajout de type).
Front-end :
- api/hooks/mock : dayParts CRUD + positions.generateShifts.
- ConfigRules : panneau « Créneaux types » (libellé + horaires éditables,
ajout, suppression).
- ConfigGrid : bouton « Générer les créneaux » sur l'en-tête du poste.
- GrillePage : case « à définir » (gris, glyphe ·) quand min=idéal=0, avec
entrée de légende.
155 tests back OK ; typecheck + lint + build + smoke front OK.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
- GrillePage : quand assigned > ideal_staff, la case passe en mauve (glyphe ▼)
et prend le pas sur l'état « complet ».
- Nouvelle entrée de légende « SUREFFECTIF ».
- tailwind : couleurs mauve / mauve-hover.
- mock : quelques créneaux volontairement en sureffectif pour la démo.
typecheck + lint + build + smoke OK.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Back-end :
- GET /editions/{id} (tout compte de l'édition) et PATCH /editions/{id}
(COORDINATION) : name, year, start_date, end_date, retention_days.
Refus si end_date < start_date (edition.dates_inconsistent). Tests.
Front-end :
- api : EditionRead/Update, editions.get/update (http + mock), hooks
useEdition / useUpdateEdition.
- ConfigGrid : le bouton « ajouter un poste » sélectionne le poste créé et
affiche l'état (pending) + les erreurs (ErrorNote) — avant, un échec ou
même un succès passaient inaperçus. Renommage du poste + choix de la
catégorie + suppression dans l'en-tête. Une colonne par jour de l'édition
(même vide) → on peut ajouter un créneau n'importe quel jour.
- ConfigRules : formulaire d'édition (nom + dates) → c'est là qu'on ajoute
ou retire des jours ; compteur de jours ; retour d'enregistrement.
- i18n mis à jour.
144 tests back OK ; typecheck + lint + build + smoke front OK.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
- api : PlanningNotificationRequest/Result dans schema.ts, méthode
notifications.sendPlanning (http + mock), hook useSendPlanningNotification
- mock : compte les destinataires avec/sans email, émet des liens magiques
quand {lien_connexion} est présent
- ConfigMail : plus une maquette. Éditeur objet/corps, variables cliquables
({prenom} {nom} {nb_creneaux} {nb_heures} {creneaux} {lien_connexion}),
boutons « Envoyer le récapitulatif » et « Envoyer avec lien de connexion »,
retour d'envoi (queued / sans adresse) et ErrorNote
- i18n config.mail.* mis à jour ; onglet « MESSAGES » (plus « (MAQUETTE) »)
typecheck + lint + build + smoke : OK
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Le gros de la suppression a été fait dans 8d333de ; il restait des libellés :
- i18n : « CONFIRMÉS / IDÉAL » → « AFFECTÉS / IDÉAL », note de bas de grille
sans « proposés », clé mobileVol.slotConfirmed → slotAssigned
- components/ui.tsx : tons de Badge « confirmed » / « proposed » (inutilisés)
- ConfigMail : texte d'email par défaut sans « confirmer chaque créneau »
typecheck + lint + build + smoke : OK
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
Le back-end (Étape 9) a retiré AssignmentStatus : une affectation est valide
dès sa création, la retirer = supprimer la ligne.
- schema.ts : AssignmentRead/Create/Update sans `status` ;
MatrixShift.{confirmed, proposed} → `assigned`.
- mock : générateur sans statut (plus de rollStatus / DECLINED) ; matrice
= compte unique ; create → validation toujours exécutée ; PATCH ne porte
plus que source/locked ; runValidation compte toutes les affectations.
- lib/planning : suppression de isActive() et statusLabel().
- Grille + panneau : cellule « affectés / idéal », plus de +proposés ni de
badge confirmé/proposé ni de bouton « confirmer ».
- Vue par bénévole : une seule trame d'affectation (bleu).
- Mobile bénévole : plus de bouton « JE CONFIRME » ni de bannière.
- Mobile référent : plus de libellé de statut par équipier.
- Onglet 03 « Confirmations » supprimé (route, page, nav, ROUTE_ROLES) —
il n'a plus d'objet. Coordination : onglets 01, 02, 06.
- i18n : clés de statut / de l'écran Confirmations retirées.
- Vérifs : build tsc + vite, eslint (0 problème), npm run smoke.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwswqSChc3Rwtu61pRXiY2
Authentification alignée sur le back-end (Étapes 7/8) :
- src/api : schema auth (AccountRead, TokenResponse…), session.ts (jeton
Bearer partagé + callback 401), méthodes auth du client HTTP
(POST .../auth/login, .../auth/magic-link, .../auth/verify, GET /auth/me)
et du mock (comptes coordination + référents + bénévoles, tokens magiques,
sessions).
- src/auth : AuthProvider/useAuth (jeton en localStorage, réhydratation via
/auth/me, déconnexion sur 401), RequireAuth (garde + contrôle de rôle),
HomeRedirect, table ROUTE_ROLES.
- Pages : /connexion (lien magique, avec lien de démo affiché en mode mock),
/connexion/coordination (email + mot de passe).
- Router : toutes les routes métier passées derrière <RequireAuth> ;
redirection vers l'accueil du rôle.
- AppShell : onglets filtrés par rôle (01/02/03/06 coordination, 04 bénévole,
05 référent), email + rôle + bouton Déconnexion.
- Mobile bénévole : cible le bénévole du compte connecté ; dégradation
propre quand l'API refuse le détail des créneaux à un bénévole.
- Mobile référent : cible le poste dont le compte est référent.
- i18n : clés auth / rôles.
- Vérifs : build tsc + vite, eslint (0 problème), npm run smoke (flux
login / lien magique + rendu des 6 écrans par rôle et des pages publiques).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwswqSChc3Rwtu61pRXiY2
Scaffold de base en attendant l'intégration du design Claude Design
(« Bénévoles Festival.dc.html ») : routage, plomberie i18n (t() +
locales/fr.json), client API avec proxy Vite vers le backend. Build et
lint passent.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwswqSChc3Rwtu61pRXiY2