Formulaire public : logo et texte d'introduction personnalisables

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>
This commit is contained in:
AdamThomas
2026-09-23 20:46:52 +02:00
co-authored by Claude Sonnet 5
parent 48d3deec65
commit 957a004623
17 changed files with 415 additions and 44 deletions
+24 -17
View File
@@ -122,6 +122,14 @@ const fileToDataUrl = (file: File): Promise<string> =>
reader.readAsDataURL(file);
});
const formTemplateRead = (): FormTemplateRead => ({
edition_id: state.edition.id,
is_open: state.formTemplate.isOpen,
fields: state.formTemplate.fields,
has_logo: state.formTemplate.logo !== null,
description: state.formTemplate.description,
});
// PDF valide minimal (une page blanche) — remplace la vraie génération
// (réservée au back-end réel) pour que le téléchargement fonctionne en mode
// démonstration.
@@ -1467,27 +1475,24 @@ export function createMockApi(): PlanningApi {
},
forms: {
getTemplate: (): Promise<FormTemplateRead> =>
delay({
edition_id: state.edition.id,
is_open: state.formTemplate.isOpen,
fields: state.formTemplate.fields,
}),
getTemplate: (): Promise<FormTemplateRead> => delay(formTemplateRead()),
setFields: (_edition, body: FormFieldsSet) => {
state.formTemplate.fields = [...new Set(body.fields)];
return delay<FormTemplateRead>({
edition_id: state.edition.id,
is_open: state.formTemplate.isOpen,
fields: state.formTemplate.fields,
});
return delay<FormTemplateRead>(formTemplateRead());
},
setOpen: (_edition, body: FormTemplateUpdate) => {
state.formTemplate.isOpen = body.is_open;
return delay<FormTemplateRead>({
edition_id: state.edition.id,
is_open: state.formTemplate.isOpen,
fields: state.formTemplate.fields,
});
if (body.is_open !== undefined) state.formTemplate.isOpen = body.is_open;
if (body.description !== undefined) state.formTemplate.description = body.description;
return delay<FormTemplateRead>(formTemplateRead());
},
uploadLogo: async (_edition, file: File) => {
state.formTemplate.logo = await fileToDataUrl(file);
state.formTemplate.logoType = file.type;
return delay<FormTemplateRead>(formTemplateRead());
},
logoBlob: async () => {
if (!state.formTemplate.logo) return null;
return (await fetch(state.formTemplate.logo)).blob();
},
getPublicConfig: (): Promise<PublicFormConfig> => {
const fields = state.formTemplate.fields;
@@ -1504,6 +1509,8 @@ export function createMockApi(): PlanningApi {
performance_date: a.performance_date,
}))
: [],
has_logo: state.formTemplate.logo !== null,
description: state.formTemplate.description,
});
},
submitPublic: async (_edition, body: PublicFormSubmission) => {