Refonte du créateur de badges et nombre de badges par bénévole
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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1a23881e81
commit
ced0855d47
@@ -89,7 +89,10 @@ les tables sauf les tables d'association.
|
||||
paiement SumUp plus bas). `badge_text` : texte du badge propre à ce bénévole
|
||||
(champ `FREE_TEXT`, voir `BadgeField`), saisi par la coordination sur la
|
||||
fiche (« Texte du badge ») — jamais par le formulaire public — et qui
|
||||
remplace pour lui le texte commun de l'éditeur.
|
||||
remplace pour lui le texte commun de l'éditeur. `badge_count` : nombre
|
||||
d'exemplaires de son badge imprimés sur les planches (1 par défaut, 1 à
|
||||
100 — `CHECK >= 1` en base, borne haute à la saisie contre une faute de
|
||||
frappe), « Nombre de badges » sur la fiche.
|
||||
- **`Artist`** : programmation de l'édition (`name`, `performance_date`,
|
||||
`start_time`, `end_time` — `end_time <= start_time` ⇒ le passage finit le
|
||||
lendemain). Unique (`edition_id`, `name`, `performance_date`). La
|
||||
@@ -162,15 +165,31 @@ les tables sauf les tables d'association.
|
||||
et la génération retombant alors sur le format CR80 par défaut (85,6 ×
|
||||
54 mm, `DEFAULT_BADGE_WIDTH_MM`/`HEIGHT_MM`). Le verso est ajusté (sans
|
||||
déformation, `preserveAspectRatio=True`) à ce même format.
|
||||
- **`BadgeField`** : un champ positionné sur le recto (`field_type` ∈
|
||||
`FULL_NAME` / `POSITIONS` / `CATEGORY` / `ROLE` / `PHOTO` / `FREE_TEXT`,
|
||||
au plus un par édition —
|
||||
`PUT .../badges/template/fields` remplace tout), coordonnées `x_mm`/`y_mm`/
|
||||
`width_mm`/`height_mm` en millimètres depuis le coin haut-gauche du badge,
|
||||
`font_size`, `align`, `bold`, `color`. Édité visuellement (glisser-déposer)
|
||||
depuis l'écran « Badges » du front-end (`frontend/src/pages/BadgesPage.tsx`),
|
||||
qui envoie ces mêmes millimètres et redimensionne son aperçu au format réel
|
||||
du badge (`GET .../badges/template` renvoie `badge_width_mm`/`height_mm`).
|
||||
- **`BadgeField`** : un champ positionné sur le **recto ou le verso**
|
||||
(`face` ∈ `FRONT` / `BACK` ; `field_type` ∈ `FULL_NAME` / `POSITIONS` /
|
||||
`CATEGORY` / `ROLE` / `PHOTO` / `FREE_TEXT`, au plus un par face et par
|
||||
édition — unique (`edition_id`, `face`, `field_type`), 422
|
||||
`badge.duplicate_field_type` sinon ; `PUT .../badges/template/fields`
|
||||
remplace tout, les deux faces dans la même liste), coordonnées `x_mm`/
|
||||
`y_mm`/`width_mm`/`height_mm` en millimètres depuis le coin haut-gauche du
|
||||
badge, `font_size`, `align`, `bold`, `color`, `bg_color` (fond plein de la
|
||||
zone, `NULL` = transparent, `#rrggbb` sinon 422 `badge.invalid_color`),
|
||||
`auto_fit` (taille réduite par pas de 0,5 pt, plancher 4 pt, jusqu'à ce que
|
||||
tout tienne — `app/badges/pdf.py:fit_text` ; sans, les lignes en trop ne
|
||||
sont pas imprimées), `photo_shape` (`RECT` / `OVAL`, champ `PHOTO`).
|
||||
`FREE_TEXT` et `ROLE` sont centrés verticalement dans leur cadre (règle
|
||||
dérivée du type), les autres en haut. `FULL_NAME` = prénom, puis nom à la
|
||||
ligne. Édité visuellement depuis l'écran « Badges »
|
||||
(`frontend/src/pages/badges/` — refonte d'après `design/Créateur de
|
||||
badges.dc.html` : liste des champs recto/verso, aperçu des deux faces avec
|
||||
les données d'un vrai bénévole, inspecteur, formats prédéfinis via
|
||||
`PUT .../template/size`, annuler, flèches, zoom, repères de découpe
|
||||
1,5 mm, contrôle de tous les badges avant génération — `issues.ts`).
|
||||
L'aperçu reprend la mise en page du PDF à l'identique
|
||||
(`badges/layout.ts`, avec les métriques Helvetica de ReportLab recopiées
|
||||
dans `helveticaMetrics.ts`, pas une mesure du navigateur) : toute
|
||||
règle de mise en page changée dans `app/badges/pdf.py` doit l'être aussi
|
||||
dans `layout.ts`, sinon le contrôle avant génération ment.
|
||||
`POSITIONS` est un **tableau** (`app/api/badges.py:_positions_table`,
|
||||
dessiné par `app/badges/pdf.py:_draw_table`) des créneaux affectés,
|
||||
**regroupés par jour de festival** (§ 3 — une soirée qui passe minuit
|
||||
@@ -188,23 +207,30 @@ les tables sauf les tables d'association.
|
||||
au ratio du cadre sans déformation, orientation EXIF appliquée et réduite
|
||||
à 300 dpi (`app/badges/pdf.py:_fit_photo` — une photo de téléphone de
|
||||
plusieurs Mo répétée sur 200 badges alourdirait le PDF) ; sans photo ou
|
||||
photo illisible, le cadre reste vide plutôt que de faire échouer la
|
||||
planche. Le texte d'un champ passe à la ligne pour tenir dans la largeur
|
||||
photo illisible, les **initiales** du bénévole sont imprimées à la
|
||||
place (fond `#E7E0CE`, même forme) plutôt que de faire échouer la
|
||||
planche ; `OVAL` découpe la photo en ellipse. Le texte d'un champ passe à la ligne pour tenir dans la largeur
|
||||
de son cadre (`app/badges/pdf.py:wrap_text` — entre deux mots, ou au
|
||||
milieu d'un mot trop long seul ; les `\n` restent des retours à la
|
||||
ligne), et ce qui dépasse la hauteur du cadre n'est pas imprimé — comme
|
||||
l'aperçu de l'éditeur, qui fait de même côté navigateur. Avant, chaque
|
||||
ligne s'imprimait d'un bloc et débordait du cadre. L'éditeur ramène tout champ à l'intérieur du badge
|
||||
(`BadgesPage.tsx:fitToBadge`) et met le champ sélectionné au premier
|
||||
(`badges/model.ts:fitToBadge`) et met le champ sélectionné au premier
|
||||
plan : les positions par défaut visent un CR80 paysage, et sur un badge
|
||||
plus étroit un champ pouvait tomber hors du badge, avec des bornes de
|
||||
glisser-déposer négatives qui le bloquaient — bug corrigé une fois (photo
|
||||
impossible à déplacer ni redimensionner).
|
||||
`POST /editions/{id}/badges/generate` (COORDINATION, `{volunteer_ids?}` —
|
||||
absent = tous) génère la planche PDF (`app/badges/pdf.py`, OR-Tools n'y est
|
||||
absent = tous ; chaque bénévole y figure `badge_count` fois, à la suite)
|
||||
génère la planche PDF (`app/badges/pdf.py`, OR-Tools n'y est
|
||||
pour rien : juste ReportLab) : pages recto personnalisées suivies, planche
|
||||
par planche, de la page de verso correspondante (même image répétée à
|
||||
chaque emplacement) — impression recto-verso classique.
|
||||
par planche, de la page de verso correspondante (image de verso puis
|
||||
champs `BACK` de chaque bénévole) — impression recto-verso classique
|
||||
(retournement bord long) : sur la page de verso, chaque badge est placé
|
||||
**en miroir horizontal** (colonne `cols-1-col`, `_cell_origin(mirrored=
|
||||
True)`) pour tomber derrière son recto. Indispensable depuis que le verso
|
||||
est personnalisé ; avant, un verso identique pour tous masquait le
|
||||
décalage.
|
||||
- **Feuilles de poste** (PDF, `app/sheets/pdf.py` + `app/api/position_sheets.py`) :
|
||||
aucune nouvelle entité — dérivées de `Position`/`Shift`/`Assignment`/
|
||||
`Volunteer` existants. Complément des badges (un badge identifie un
|
||||
@@ -953,6 +979,8 @@ Aucune dépendance locale requise hors Docker.
|
||||
| 48 | Badges : texte libre saisi dans l'éditeur (`BadgeField.text`, migration `0025`), commun à tous les badges, le « Texte du badge » de la fiche le remplaçant pour un bénévole ; champs ramenés dans le badge et champ sélectionné au premier plan (la photo restait bloquée hors d'un badge plus étroit que le CR80) | à valider |
|
||||
| 49 | Badges : le texte des champs (nom, postes et horaires…) passe à la ligne pour tenir dans la largeur de son cadre au lieu de déborder (`app/badges/pdf.py:wrap_text`) | à valider |
|
||||
| 50 | Badges : les créneaux affectés s'affichent en tableau — le jour de festival en en-tête, puis une colonne horaire et une colonne poste (le référent n'est plus imprimé sur le badge), filets à la couleur du champ, cellules repliées dans leur colonne ; même tableau dans l'aperçu de l'éditeur | à valider |
|
||||
| 51 | Refonte du créateur de badges d'après la maquette (`design/`) : champs sur le recto **et le verso** (`BadgeField.face`, verso en miroir dans le PDF), fond de zone, réduction automatique du texte, photo ovale, initiales sans photo, nom sur deux lignes (migration `0026`) ; éditeur en 3 colonnes (champs, aperçu recto/verso avec de vrais bénévoles, inspecteur), formats prédéfinis, annuler/flèches/zoom/repères, dépôt d'image sur l'aperçu, contrôle de tous les badges avant génération | à valider |
|
||||
| 52 | Nombre de badges par bénévole (`Volunteer.badge_count`, 1 par défaut, migration `0027`) : champ « Nombre de badges » sur la fiche, autant d'exemplaires sur les planches, total à imprimer affiché à la génération, colonne dans l'export Excel | à valider |
|
||||
|
||||
**Reste hors périmètre** (modèle prêt à les accueillir) : SMS, constructeur
|
||||
de formulaire, pointage sur site, IA. Frontend : squelette + 6 écrans +
|
||||
|
||||
Reference in New Issue
Block a user