Badges : permettre d'agrandir le badge proportionnellement à l'image
Le format déduit de l'image importée (300 dpi supposés, étape
précédente) donne un badge minuscule pour une image à résolution web —
ajout d'un contrôle « largeur du badge (mm) » dans l'écran de
personnalisation : la hauteur suit proportionnellement (ratio de
l'image). PUT /editions/{id}/badges/template/size écrase la taille
déduite de l'image ; la réimporter la recalcule à nouveau.
Vérifié : pytest (250 passés), ruff, tsc --noEmit, eslint, vite build,
smoke — tous verts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bc165757ca
commit
55c5d539a8
@@ -5,6 +5,7 @@ import {
|
||||
useBadgeTemplate,
|
||||
useGenerateBadges,
|
||||
useSetBadgeFields,
|
||||
useSetBadgeSize,
|
||||
useUploadBadgeBackImage,
|
||||
useUploadBadgeFrontImage,
|
||||
useVolunteers,
|
||||
@@ -116,6 +117,7 @@ function BadgeCustomizeTab() {
|
||||
const uploadFront = useUploadBadgeFrontImage();
|
||||
const uploadBack = useUploadBadgeBackImage();
|
||||
const setFields = useSetBadgeFields();
|
||||
const setSize = useSetBadgeSize();
|
||||
|
||||
const [fields, setFieldsState] = useState<Record<BadgeFieldType, FieldState> | null>(null);
|
||||
const [selected, setSelected] = useState<BadgeFieldType | null>(null);
|
||||
@@ -169,6 +171,14 @@ function BadgeCustomizeTab() {
|
||||
error={uploadFront.error}
|
||||
onPick={(file) => uploadFront.mutate(file)}
|
||||
/>
|
||||
<BadgeSizeControl
|
||||
badgeW={badgeW}
|
||||
badgeH={badgeH}
|
||||
pending={setSize.isPending}
|
||||
onApply={(width_mm, height_mm) =>
|
||||
setSize.mutate({ badge_width_mm: width_mm, badge_height_mm: height_mm })
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="relative mt-3 overflow-hidden border-2 border-ink bg-sand-dark bg-cover bg-center select-none"
|
||||
style={{
|
||||
@@ -286,6 +296,59 @@ function ImageUploadRow({
|
||||
);
|
||||
}
|
||||
|
||||
/** Largeur du badge en mm — la hauteur suit proportionnellement (ratio de
|
||||
* l'image importée). Sert à agrandir un badge devenu minuscule parce que
|
||||
* l'image de fond n'était pas à une résolution d'impression (300 dpi
|
||||
* supposés à l'import, voir `useBadgeTemplate`). */
|
||||
function BadgeSizeControl({
|
||||
badgeW,
|
||||
badgeH,
|
||||
pending,
|
||||
onApply,
|
||||
}: {
|
||||
badgeW: number;
|
||||
badgeH: number;
|
||||
pending: boolean;
|
||||
onApply: (width_mm: number, height_mm: number) => void;
|
||||
}) {
|
||||
const rounded = Math.round(badgeW * 10) / 10;
|
||||
const [width, setWidth] = useState(String(rounded));
|
||||
|
||||
useEffect(() => setWidth(String(rounded)), [rounded]);
|
||||
|
||||
const apply = () => {
|
||||
const w = Number(width);
|
||||
if (!Number.isFinite(w) || w <= 0) {
|
||||
setWidth(String(rounded));
|
||||
return;
|
||||
}
|
||||
const ratio = badgeH / badgeW;
|
||||
onApply(w, Math.round(w * ratio * 10) / 10);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-2 flex items-center gap-[8px]">
|
||||
<FieldLabel>{t('badges.size.width')}</FieldLabel>
|
||||
<input
|
||||
type="number"
|
||||
min={5}
|
||||
step={1}
|
||||
value={width}
|
||||
disabled={pending}
|
||||
onChange={(e) => setWidth(e.target.value)}
|
||||
onBlur={apply}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') apply();
|
||||
}}
|
||||
className="w-[64px] border-2 border-ink bg-cream px-[6px] py-[3px] font-mono text-[11px]"
|
||||
/>
|
||||
<span className="font-mono text-[9.5px] opacity-60">
|
||||
{t('badges.size.height', { height: Math.round(badgeH * 10) / 10 })}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FieldBox({
|
||||
field,
|
||||
badgeW,
|
||||
|
||||
Reference in New Issue
Block a user