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
+37 -1
View File
@@ -615,7 +615,9 @@ export function useSetFormFields() {
});
}
export function useSetFormOpen() {
/** `PATCH /form` : `is_open` et/ou `description`, chacun optionnel
* (absent = inchangé — voir `FormTemplateUpdate`). */
export function useUpdateFormTemplate() {
const invalidate = useInvalidateFormTemplate();
return useMutation({
mutationFn: (body: FormTemplateUpdate) => api.forms.setOpen(edition, body),
@@ -623,6 +625,40 @@ export function useSetFormOpen() {
});
}
export function useUploadFormLogo() {
const invalidate = useInvalidateFormTemplate();
return useMutation({
mutationFn: (file: File) => api.forms.uploadLogo(edition, file),
onSuccess: invalidate,
});
}
/** URL objet (aperçu) du logo du formulaire public, ou `null` si absent.
* Utilisable sans compte connecté (`GET .../form/logo` n'est pas protégé —
* affiché sur `/inscription`). `refreshKey` (ex. `dataUpdatedAt` de
* `useFormTemplate`) force un nouveau fetch après un changement de logo. */
export function useFormLogoUrl(hasLogo: boolean, refreshKey: number | string) {
const [url, setUrl] = useState<string | null>(null);
useEffect(() => {
let objectUrl: string | null = null;
let cancelled = false;
if (!hasLogo) {
setUrl(null);
return;
}
api.forms.logoBlob(edition).then((blob) => {
if (cancelled || !blob) return;
objectUrl = URL.createObjectURL(blob);
setUrl(objectUrl);
});
return () => {
cancelled = true;
if (objectUrl) URL.revokeObjectURL(objectUrl);
};
}, [hasLogo, refreshKey]);
return url;
}
/** Configuration publique du formulaire — utilisable sans compte connecté
* (écran `/inscription`, hors de `RequireAuth`). */
export function usePublicFormConfig() {