Fiches de poste rapides, badges sans initiales, vue par bénévole sur l'axe des temps
ci-cd / build-backend (push) Successful in 34s
ci-cd / build-frontend (push) Successful in 19s
ci-cd / deploy (push) Successful in 11s

É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>
This commit is contained in:
AdamThomas
2026-10-28 19:01:48 +01:00
co-authored by Claude Opus 5.5
parent c57c70569c
commit c6ea7378c7
13 changed files with 424 additions and 296 deletions
+6 -16
View File
@@ -16,7 +16,6 @@ import {
import {
FACE_LABEL_KEY,
fmt,
initials,
isVerticallyCentered,
renderField,
SAFE_MARGIN_MM,
@@ -25,8 +24,6 @@ import {
type FieldState,
} from './model';
/** Fond d'une photo absente (initiales à la place), comme le PDF. */
export const PHOTO_PLACEHOLDER_BG = '#E7E0CE';
/** Pas d'accroche du glisser-déposer (mm). */
const DRAG_SNAP_MM = 0.5;
/** Plus petit cadre possible au redimensionnement (mm). */
@@ -301,7 +298,7 @@ function FieldBox({
}}
>
{rendered?.kind === 'photo' || (!volunteer && field.field_type === 'PHOTO') ? (
<PhotoContent field={field} volunteer={volunteer} photoUrl={photoUrl} scale={scale} />
<PhotoContent field={field} volunteer={volunteer} photoUrl={photoUrl} />
) : rendered?.kind === 'table' ? (
<TableContent field={field} layout={rendered.layout} scale={scale} />
) : rendered?.kind === 'text' ? (
@@ -450,12 +447,10 @@ function PhotoContent({
field,
volunteer,
photoUrl,
scale,
}: {
field: FieldState;
volunteer: VolunteerRead | null;
photoUrl: string | null;
scale: number;
}) {
const radius = field.photo_shape === 'OVAL' ? '50%' : 0;
if (volunteer?.has_photo && photoUrl) {
@@ -469,19 +464,14 @@ function PhotoContent({
/>
);
}
// Sans photo, rien n'est imprimé : juste un repère pointillé dans
// l'éditeur (pas sur le badge) pour savoir où est le cadre.
return (
<div
className="pointer-events-none flex h-full w-full items-center justify-center font-bold"
style={{
borderRadius: radius,
background: PHOTO_PLACEHOLDER_BG,
color: field.color,
fontFamily: FONT_STACK,
// Comme le PDF : 0,28 × la largeur du cadre, 4 pt au moins.
fontSize: Math.max(4 * MM_PER_PT, field.width_mm * 0.28) * scale,
}}
className="pointer-events-none flex h-full w-full items-center justify-center border border-dashed border-ink/40 text-center font-mono text-[9px] leading-tight text-ink/50"
style={{ borderRadius: radius }}
>
{volunteer ? initials(volunteer) : null}
{t('badges.preview.noPhoto')}
</div>
);
}