Étape 62 : les fiches de poste intégraient la photo d'origine de chaque
bénévole pour chaque créneau (CPU à 300 %, puis erreur) — photos réduites
à la vignette une fois par PDF ; badges : photo recadrée une fois par
génération et décodée à taille réduite.
Étape 61 : sans photo, le cadre de la photo reste vide sur le badge.
Étape 60 : vue par bénévole en tranches de 30 min, affectations étirées
sur toute leur durée, postes simultanés empilés, ligne de l'heure actuelle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Le chevauchement de créneaux (SHIFT_OVERLAP) n'est plus bloquant : il
remonte en avertissement et l'affectation est enregistrée. Le solveur ne
l'exclut plus mais applique un fort malus au bénévole déjà affecté
ailleurs au même moment (proposé seulement sous l'effectif minimal, faute
de mieux) ; il n'est jamais reproposé sur un créneau où il est déjà.
Même malus dans le classement par défaut du panneau et dans le mock.
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>
La « journée de festival » commence désormais au début du plus long creux
laissé par les créneaux types sur 24 h (l'heure où le dernier se termine),
et non plus au début de celui de plus petit `order` : un créneau type
d'ordre 0 déplacé à 21:00 faisait commencer chaque journée des grilles à
21:00, jusqu'à 03:00 le lendemain. Même règle côté backend
(validation.engine.day_boundary, réutilisée par la génération de créneaux)
et frontend (dayBoundaryFromDayParts).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Un créneau saisi « 00:00–01:00 » sur la carte du samedi était enregistré
le samedi à 00:00 (nuit de vendredi à samedi), donc affiché la veille dans
la grille des besoins. Une heure de début avant le début de journée
(premier créneau type) désigne désormais la nuit qui suit : dimanche
00:00, à la suite de la soirée du samedi.
Même « journée de festival » partout (validation/engine.py:day_boundary,
festival_day ; miroir front lib/planning.ts:festivalDay) : écran Postes &
créneaux, feuilles de poste (une seule page pour une soirée qui passe
minuit) et chaînage du solveur (00h–01h enchaîne 23h–00h).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Un poste ne génère plus de créneaux à sa création : la coordination crée
elle-même ceux dont elle a besoin, avec ses propres horaires de début et
de fin (ConfigGrid.tsx). La génération depuis les créneaux types reste
disponible en option (bouton « Générer les créneaux »).
La grille des besoins devient un vrai axe des temps : une colonne = 30 min,
bornée par jour à la plage réellement utilisée, avec les cases fusionnées
pour correspondre aux horaires de chaque créneau (buildTimeGrid /
placeInTimeGrid). La « Vue bénévoles » garde son ancien modèle (une colonne
par créneau distinct), plus adapté à son tableau bénévole × créneau.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Un créneau type commençant avant la frontière du jour (le plus petit
`order`, ex. « Nuit » 00 h–03 h après « Soirée » 19 h–00 h) était généré
sur la même date calendaire que la soirée elle-même au lieu du lendemain,
et s'affichait donc avant 19 h/22 h/23 h dans la grille des besoins au
lieu d'après 23 h. Corrigé dans la génération des créneaux
(_shiftgen.py, même frontière que validation.engine._day_boundary) et
dans le regroupement/tri de la grille (planning.ts : `dayBoundary`
calculé depuis les créneaux types).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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
Le vœu reste à sens unique en base (A choisit B sur sa fiche, B n'a rien à
faire) mais si A souhaite B, B doit désormais profiter aussi de la présence
de A — dans les deux sens : affecter A d'abord fait remonter B comme
candidat, et affecter B d'abord fait remonter A, même si B n'a rien demandé.
- Solveur (`loader.py`) : le vœu (A → B) alimente maintenant l'ensemble
effectif des deux bénévoles (A et B), pas seulement celui du demandeur.
Aucun changement dans `engine.py` : il ne fait que lire un ensemble déjà
symétrisé.
- Mock (`buildEffectiveCompanions`) et classement par défaut
(`buildEffectiveCompanionIds` dans `lib/planning.ts`, utilisé par
`AssignmentPanel.tsx`) : même symétrisation, pour rester cohérents avec le
vrai solveur.
- La fiche bénévole (vœux affichés/édités) n'est pas touchée : c'est le seul
endroit qui reste à sens unique, par construction (la réciprocité n'est
qu'une lecture, jamais une donnée enregistrée — sinon éditer sa propre
liste risquerait d'effacer le vœu de quelqu'un d'autre).
- Nouveau test d'intégration : le bénévole qui n'a rien demandé profite quand
même de la présence de celui qui l'a choisi.
- CLAUDE.md à jour.
Vérifié : 193 tests backend pertinents (dont le nouveau), ruff, alembic
check ; typecheck/lint/build/smoke frontend.
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
- `buildLattice` (frontend/src/lib/planning.ts) identifie une colonne par
(date, heure de début, heure de fin) et non plus par la seule heure de
début : deux créneaux qui démarrent en même temps mais durent
différemment ont désormais chacun leur colonne, avec un en-tête « début
→ fin » au lieu du seul début.
- Largeur adaptative par jour (`columnWidthPx`) : peu de créneaux dans une
journée ⇒ colonnes larges, beaucoup de créneaux ⇒ largeur compacte
(défilement horizontal) — fini les colonnes étroites par défaut.
- `VolunteerViewPage` mis à jour pour rester synchronisé avec la nouvelle
clé de colonne (fonction `buildLattice` partagée).
- CLAUDE.md à jour.
Corrige au passage `bg-hatch-mauve` (case en sureffectif) : la classe était
utilisée depuis le commit précédent mais l'utilitaire Tailwind
correspondant n'avait jamais été ajouté à `backgroundImage` — la case
retombait silencieusement sur un fond uni.
typecheck + lint + build + smoke OK.
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
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
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