Étape 55 : écrans /mobile-benevole et /mobile-referent d'après la maquette
(design/Planning bénévole mobile) — créneau en cours ou prochain mis en
avant, planning par journée de festival avec frise horaire, compteurs et
pointage du référent par créneau.
Étape 56 : message de l'organisation (Edition.volunteer_message, onglet
« Message aux bénévoles ») et message de chaque poste (Position.message,
saisi par le référent), migration 0029, affichés sur l'écran bénévole.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Créateur de badges refait d'après la maquette : champs sur le recto et le
verso (BadgeField.face, verso placé en miroir dans le PDF pour le
recto-verso), fond de zone, réduction automatique du texte, photo ovale,
initiales sans photo, nom sur deux lignes (migration 0026) ; éditeur en
trois colonnes (champs, aperçu recto/verso avec de vrais bénévoles,
inspecteur), formats prédéfinis, annuler, flèches, zoom, repères de
découpe, dépôt d'image sur l'aperçu, contrôle de tous les badges avant
génération.
Nombre de badges par bénévole (Volunteer.badge_count, 1 par défaut,
migration 0027) : champ sur la fiche, autant d'exemplaires sur les
planches, total affiché à la génération, colonne dans l'export Excel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Champ « Photo du bénévole » (PHOTO) : photo du formulaire recadrée au
cadre, orientation EXIF appliquée, réduite à 300 dpi ; cadre vide sans
photo. Champ « Texte libre » (FREE_TEXT) alimenté par le nouveau « Texte
du badge » de la fiche bénévole (Volunteer.badge_text, migration 0024),
repris dans l'export Excel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Une ligne de log par requête (route modèle, statut, durée, code d'erreur,
compte), identifiant X-Request-ID repris dans chaque log et affiché à
l'utilisateur sous une erreur, exceptions non gérées tracées (500 JSON)
- Journal d'accès d'uvicorn coupé : il exposait le jeton du formulaire public
- Logs métier : paiement SumUp, connexion, import, plannings, solveur
- Nom du bénévole dans la description du checkout SumUp
- Code et identifiant de transaction SumUp enregistrés au paiement
(migration 0023), affichés sur la fiche et dans l'export Excel
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Disponibilités déclarées supprimées (table `availability`, migration 0022,
API, éditeur de la fiche bénévole, badge de couverture du panneau
d'affectation, colonnes des statistiques) ainsi que les règles
OUTSIDE_AVAILABILITY, DAILY_HOURS_EXCEEDED et INSUFFICIENT_REST, les malus
de disponibilité et de repos du solveur et le filtre « Surchargés » de la
vue bénévoles. Seul le chevauchement reste contrôlé. La case « je suis
disponible » du formulaire public est conservée.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nouvel onglet « Statistiques » (/statistiques, COORDINATION), alimenté
par GET /editions/{id}/stats (app/api/stats.py, recalculé à chaque appel,
aucune nouvelle entité) :
- inscriptions : total, 7 derniers jours, évolution cumulée par jour
- réponses : part des bénévoles ayant renseigné chaque information,
artistes les plus demandés
- cotisations : en ligne / sur place / aucune, montants par type,
mécénat, formulaires remplis sans paiement abouti
- besoins face aux bénévoles, par journée de festival : heures × personnes
demandées vs disponibilités déclarées vs affectées ; par poste, places
pourvues (plafonnées à l'effectif idéal de chaque créneau) et rapport
vœux / place
Graphiques SVG maison (components/charts.tsx) avec infobulles au survol
et au clavier, vue tableau pour chacun, palette validée (daltonisme,
contraste), mise en page vérifiée sur ordinateur et mobile. Le mock
calcule les mêmes chiffres et étale les inscriptions du jeu de démo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Lien d'inscription protégé par un jeton (FormTemplate.access_token,
/inscription/<token>), avec régénération depuis l'écran de configuration.
- Reçus fiscaux mécénat (cerfa 11580*05) : un PDF par bénévole ayant payé en
mécénat, donateur/montant (chiffres et toutes lettres en français,
app/mecenat/number_words.py)/date complétés par-dessus le modèle officiel.
- Jeu de démonstration : bénévoles avec cotisation classique ou mécénat payée.
- Infobulles (HelpTip et détail de cellule de la grille des besoins)
repositionnées en position: fixed via JS, bornées à l'écran.
- Revue de sécurité : logo des emails en pièce jointe inline au lieu d'une
URL (fuyait le jeton d'inscription), sujet retiré du log de remise des
emails (pouvait porter un nom de bénévole), photo du formulaire public
revalidée par décodage réel avant stockage, export Excel protégé contre
l'injection de formule, sondage du statut de paiement limité en
fréquence, pyjwt mis à jour.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Formulaire public — paiement en ligne :
- Le bénévole n'est plus compté en base tant qu'un paiement en ligne choisi
n'est pas confirmé (PendingFormSubmission, promu en Volunteer à la
confirmation, webhook ou vérification en direct).
- Type de paiement « mécénat » : étape d'adresse postale avant le paiement,
conservée sur le bénévole pour un reçu fiscal (cerfa) ultérieur.
- Page de confirmation au retour de SumUp, avec relance possible après
échec/expiration sans ressaisir le formulaire.
- « Je paierai sur place » remplace « je ne participe pas » ; tous les
champs activés par la coordination sont désormais obligatoires.
- La fiche bénévole n'affiche plus l'état des cases à cocher, seulement le
résultat réel du paiement (libellé, montant, statut).
Emails :
- Email de confirmation d'inscription à la création du bénévole (tient lieu
de reçu de paiement quand une cotisation est payée en ligne).
- Mise en forme HTML au thème de l'application (logo de l'édition en
en-tête) pour tous les emails envoyés, texte brut conservé en repli.
Export Excel des bénévoles (COORDINATION) : coordonnées, vœux résolus en
noms lisibles, cotisation et mécénat.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Remplace le simple lien Yapla statique par une vraie intégration API SumUp :
checkout hébergé créé à la soumission du formulaire, statut de paiement
(PENDING/PAID/FAILED/EXPIRED) tenu à jour par un webhook SumUp et visible sur
la fiche du bénévole. La cotisation n'est plus un montant unique : la
coordination configure autant de types de paiement qu'elle veut (libellé +
montant, ex. « Cotisation classique », « Mécénat »), et le bénévole en
choisit un à l'inscription — le libellé et le montant choisis sont figés sur
le bénévole, indépendamment d'une modification ultérieure de la config.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FormTemplate.yapla_url (lien vers la page de cotisation Yapla, doit
commencer par http(s)://, PATCH .../form comme description/is_open)
fait apparaître un encart « Payer ma cotisation » sous la case
WILLING_TO_PAY_FEE, sur le formulaire public. Configurable depuis le
panneau « Personnalisation » de l'écran /inscriptions. La case reste
une simple déclaration : rien ne vérifie que le paiement a eu lieu.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FormTemplate porte désormais un logo (image, PUT/GET .../form/logo — GET
public comme .../form/public, affiché sur /inscription) et un texte
d'introduction libre (PATCH .../form, description désormais optionnelle
comme is_open). Écran de configuration (/inscriptions) : nouveau panneau
« Personnalisation » pour importer le logo et rédiger le texte. Le
formulaire public affiche le logo en grand au-dessus du panneau et le
texte juste sous le titre.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retire « constructeur de formulaire » des exclusions du CLAUDE.md (§ 5) —
décision explicite prise en conversation.
Un formulaire par édition (COORDINATION choisit les champs actifs parmi
neuf : email, téléphone, vœux de poste — max 2, vœux d'artiste — un par
soir de festival, vœu de compagnon en texte libre, disponibilité/
cotisation déclaratives, photo, commentaire ; nom/prénom toujours
collectés) ouvert/fermé à volonté, avec un lien à partager
(/inscription, aucun compte requis).
Backend : FormTemplate/FormField (migration 0013), quatre nouveaux champs
sur Volunteer (photo, companion_wish_note, self_declared_available,
willing_to_pay_fee), app/api/forms.py — les deux seuls endpoints publics
de toute l'API (GET .../form/public, POST .../form/public/submit),
protégés par hCaptcha (app/forms/captcha.py, désactivé sans
HCAPTCHA_SECRET_KEY) et un filet de sécurité global par édition
(form_submission_limiter). resolve_owned_ids/set_artists/
set_wished_positions déplacés vers app/api/_common.py, partagés avec
volunteers.py.
Frontend : écran de configuration (/inscriptions) avec lien copiable et
bascule ouvert/fermé, formulaire public (/inscription, hors RequireAuth)
avec widget hCaptcha chargé dynamiquement, capture photo native
(<input capture>), vœux de poste/artiste avec les mêmes règles que le
back-end. Réponses du formulaire visibles et éditables depuis la fiche
bénévole existante (ConfigVolunteers.tsx).
Vérifié : pytest (273 passés), ruff, alembic check, tsc --noEmit, eslint,
vite build, smoke (rendu SSR de chaque écran + flux de bout en bout du
formulaire en mode démonstration) — tous verts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Le format du badge n'est plus fixé à CR80 : il est déduit des pixels de
l'image du recto à l'import (résolution supposée 300 dpi,
app/badges/pdf.py:image_size_mm), stocké sur BadgeTemplate (migration
0012). L'éditeur (BadgesPage.tsx) et la génération PDF s'alignent sur ce
format réel ; CR80 reste le repère par défaut tant qu'aucune image n'est
importée. Le verso est ajusté sans déformation (preserveAspectRatio).
- Le champ « Postes affectés » liste désormais une ligne par créneau
(triées chronologiquement) : nom du poste + horaire, et le référent du
poste s'il y en a un et que ce n'est pas le bénévole badgé lui-même.
Texte multi-lignes pris en charge par la génération PDF (découpe sur
`\n`, hauteur de ligne calculée en mm) et par l'aperçu de l'éditeur.
- Corrige au passage un bug d'unités dans le positionnement vertical du
texte (police en points confondue avec des millimètres).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Recto personnalisable (fond image + champs positionnés : nom, poste(s)
affectés, catégorie, rôle) via un éditeur visuel glisser-déposer ; verso
fixe, la même image pour tous les bénévoles. Nouvel écran « Badges »
(COORDINATION uniquement, /badges).
Backend : modèle BadgeTemplate/BadgeField (migration 0011), génération
PDF isolée (app/badges/pdf.py, ReportLab — planches recto suivies des
planches verso correspondantes pour une impression recto-verso), route
POST /editions/{id}/badges/generate, tests (module pur + API).
Frontend : client HTTP (upload multipart, réponses binaires images/PDF),
client mock (mode démonstration), éditeur de positionnement en
millimètres (coordonnées partagées avec le backend), écran de génération
avec sélection des bénévoles et téléchargement.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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
- 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
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
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
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