diff --git a/apps/mana/apps/web/src/lib/data/unlisted/resolvers.ts b/apps/mana/apps/web/src/lib/data/unlisted/resolvers.ts index 4d68ca1f0..864da1620 100644 --- a/apps/mana/apps/web/src/lib/data/unlisted/resolvers.ts +++ b/apps/mana/apps/web/src/lib/data/unlisted/resolvers.ts @@ -333,5 +333,11 @@ async function buildFormBlob(recordId: string): Promise> if (settings.recurrence?.frequency) { blob.recurrence = { frequency: settings.recurrence.frequency }; } + // M9 — `experience` controls the public render mode (classic vs + // conversation). Whitelisted so the public dispatcher can pick the + // right view; the value is just an enum, no PII. + if (settings.experience) { + blob.experience = settings.experience; + } return blob; } diff --git a/apps/mana/apps/web/src/lib/i18n/locales/forms/de.json b/apps/mana/apps/web/src/lib/i18n/locales/forms/de.json index cadcdadca..426f25604 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/forms/de.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/forms/de.json @@ -69,7 +69,10 @@ "successMessage": "Bestätigungstext nach Absenden", "requireEmail": "E-Mail-Adresse vom Absender abfragen", "allowMultiple": "Mehrere Antworten pro Person erlauben", - "anonymous": "Anonym — Submitter-Daten nicht speichern" + "anonymous": "Anonym — Submitter-Daten nicht speichern", + "experience": "Public-Render-Modus", + "experienceClassic": "Klassisch — alle Felder gleichzeitig", + "experienceConversation": "Conversation — Chat, eine Frage nach der anderen" }, "visibility": { "title": "Sichtbarkeit & Teilen", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/forms/en.json b/apps/mana/apps/web/src/lib/i18n/locales/forms/en.json index 769fb87cd..da50e815d 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/forms/en.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/forms/en.json @@ -69,7 +69,10 @@ "successMessage": "Confirmation text after submit", "requireEmail": "Ask submitter for email address", "allowMultiple": "Allow multiple submissions per person", - "anonymous": "Anonymous — don't store submitter data" + "anonymous": "Anonymous — don't store submitter data", + "experience": "Public render mode", + "experienceClassic": "Classic — all fields at once", + "experienceConversation": "Conversation — chat, one question at a time" }, "visibility": { "title": "Visibility & Sharing", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/forms/es.json b/apps/mana/apps/web/src/lib/i18n/locales/forms/es.json index 3260f52c9..627f1fa08 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/forms/es.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/forms/es.json @@ -69,7 +69,10 @@ "successMessage": "Mensaje de confirmación tras el envío", "requireEmail": "Pedir correo electrónico al remitente", "allowMultiple": "Permitir varias respuestas por persona", - "anonymous": "Anónimo — no guardar datos del remitente" + "anonymous": "Anónimo — no guardar datos del remitente", + "experience": "Modo de visualización pública", + "experienceClassic": "Clásico — todos los campos a la vez", + "experienceConversation": "Conversación — chat, una pregunta a la vez" }, "visibility": { "title": "Visibilidad y compartir", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/forms/fr.json b/apps/mana/apps/web/src/lib/i18n/locales/forms/fr.json index ff1b63cf4..c4135d678 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/forms/fr.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/forms/fr.json @@ -69,7 +69,10 @@ "successMessage": "Message de confirmation après envoi", "requireEmail": "Demander l'adresse e-mail au destinataire", "allowMultiple": "Autoriser plusieurs réponses par personne", - "anonymous": "Anonyme — ne pas conserver les données du destinataire" + "anonymous": "Anonyme — ne pas conserver les données du destinataire", + "experience": "Mode de rendu public", + "experienceClassic": "Classique — tous les champs à la fois", + "experienceConversation": "Conversation — chat, une question à la fois" }, "visibility": { "title": "Visibilité et partage", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/forms/it.json b/apps/mana/apps/web/src/lib/i18n/locales/forms/it.json index a012fc6b0..d8247b92b 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/forms/it.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/forms/it.json @@ -69,7 +69,10 @@ "successMessage": "Messaggio di conferma dopo l'invio", "requireEmail": "Chiedi l'indirizzo e-mail al mittente", "allowMultiple": "Permetti più risposte per persona", - "anonymous": "Anonimo — non memorizzare i dati del mittente" + "anonymous": "Anonimo — non memorizzare i dati del mittente", + "experience": "Modalità di visualizzazione pubblica", + "experienceClassic": "Classica — tutti i campi insieme", + "experienceConversation": "Conversazione — chat, una domanda alla volta" }, "visibility": { "title": "Visibilità e condivisione", diff --git a/apps/mana/apps/web/src/lib/modules/forms/ConversationFormView.svelte b/apps/mana/apps/web/src/lib/modules/forms/ConversationFormView.svelte new file mode 100644 index 000000000..65e051c70 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/forms/ConversationFormView.svelte @@ -0,0 +1,701 @@ + + + +
+
+

{form.title}

+ {#if form.description} +

{form.description}

+ {/if} +
+ + + +
+ {#each history as bubble, i (i)} +
{bubble.text}
+ {/each} +
+ + {#if submitted} +
{form.settings.successMessage}
+ {:else if isAtEnd} +
+

+ Fast fertig. Magst du noch deinen Namen / deine E-Mail dalassen? (optional) +

+ + + {#if submitError} +

{submitError}

+ {/if} + + +
+ {:else if isAtSubmitter} + +

+ {:else if currentField} +
+ {#if currentField.helpText} +

{currentField.helpText}

+ {/if} + + {#if currentField.type === 'short_text' || currentField.type === 'long_text' || currentField.type === 'email'} +
+ {#if currentField.type === 'long_text'} + + {:else} + { + if (e.key === 'Enter') { + e.preventDefault(); + handleTextSubmit(); + } + }} + /> + {/if} + +
+ {:else if currentField.type === 'number'} +
+ { + if (e.key === 'Enter') { + e.preventDefault(); + handleTextSubmit(); + } + }} + /> + +
+ {:else if currentField.type === 'date'} +
+ { + if (e.key === 'Enter') { + e.preventDefault(); + handleTextSubmit(); + } + }} + /> + +
+ {:else if currentField.type === 'yes_no'} +
+ + +
+ {:else if currentField.type === 'rating'} +
+ {#each ratingScale(currentField) as n} + + {/each} +
+ {:else if currentField.type === 'single_choice'} +
+ {#each currentField.options ?? [] as opt (opt.id)} + + {/each} +
+ {:else if currentField.type === 'multi_choice'} +
+ {#each currentField.options ?? [] as opt (opt.id)} + {@const checked = multiDraft.includes(opt.id)} + + {/each} + +
+ {:else if currentField.type === 'section'} + + {:else if currentField.type === 'consent'} +
+ + {#if !currentField.required} + + {/if} +
+ {/if} + + {#if stepIndex > 0 && currentField.type !== 'section'} + + {/if} +
+ {/if} + + {#if expiresAt && !submitted} +

Dieser Link läuft am {fmtExpiry(expiresAt)} ab.

+ {/if} + + +
+ + diff --git a/apps/mana/apps/web/src/lib/modules/forms/components/SettingsPanel.svelte b/apps/mana/apps/web/src/lib/modules/forms/components/SettingsPanel.svelte index 4e2f69df1..d52480a72 100644 --- a/apps/mana/apps/web/src/lib/modules/forms/components/SettingsPanel.svelte +++ b/apps/mana/apps/web/src/lib/modules/forms/components/SettingsPanel.svelte @@ -269,6 +269,30 @@ + +

{$_('forms.builder.recurrence.title', { default: 'Wiederkehr — Antworten in Wellen' })} diff --git a/apps/mana/apps/web/src/lib/modules/forms/types.ts b/apps/mana/apps/web/src/lib/modules/forms/types.ts index b115bab41..6a82f59b5 100644 --- a/apps/mana/apps/web/src/lib/modules/forms/types.ts +++ b/apps/mana/apps/web/src/lib/modules/forms/types.ts @@ -114,6 +114,12 @@ export interface FormSettings { autoSync?: AutoSyncConfig; responsesPublic?: boolean; recurrence?: RecurrenceConfig; + /** + * M9 — render the public form as a Typeform-style chat conversation + * (one question at a time, free-text + LLM-extraction) instead of + * the classic side-by-side fields layout. Default 'classic'. + */ + experience?: 'classic' | 'conversation'; } export type FormStatus = 'draft' | 'published' | 'closed'; diff --git a/apps/mana/apps/web/src/routes/share/[token]/+page.svelte b/apps/mana/apps/web/src/routes/share/[token]/+page.svelte index 91c88e679..ef7517ef2 100644 --- a/apps/mana/apps/web/src/routes/share/[token]/+page.svelte +++ b/apps/mana/apps/web/src/routes/share/[token]/+page.svelte @@ -10,9 +10,21 @@ import SharedAugurEntryView from '$lib/modules/augur/SharedAugurEntryView.svelte'; import SharedLastView from '$lib/modules/lasts/SharedLastView.svelte'; import SharedFormView from '$lib/modules/forms/SharedFormView.svelte'; + import ConversationFormView from '$lib/modules/forms/ConversationFormView.svelte'; import type { PageData } from './$types'; let { data }: { data: PageData } = $props(); + + // M9 — pick the form-render variant from the snapshot's experience + // flag. Default 'classic' so existing forms keep their look. + const formExperience = $derived( + data.collection === 'forms' + ? (((data.blob as Record | undefined)?.experience as + | 'classic' + | 'conversation' + | undefined) ?? 'classic') + : 'classic' + ); {#if data.collection === 'events'} @@ -26,7 +38,11 @@ {:else if data.collection === 'lasts'} {:else if data.collection === 'forms'} - + {#if formExperience === 'conversation'} + + {:else} + + {/if} {:else}

Unbekannter Link-Typ