diff --git a/src/routes/admin/feedback/new/+page.svelte b/src/routes/admin/feedback/new/+page.svelte index 1fa5d2a..5a85aaa 100644 --- a/src/routes/admin/feedback/new/+page.svelte +++ b/src/routes/admin/feedback/new/+page.svelte @@ -2,6 +2,8 @@ import '$lib/styles/feedback.css'; import { goto } from '$app/navigation'; import Icon from '$lib/components/Icon.svelte'; + import FormBuilder from '$lib/components/FormBuilder.svelte'; + import { FeedbackFormDefinitionSchema, type FeedbackFormDefinition } from '$lib/schemas'; let creating = $state(false); let createError = $state(null); @@ -9,7 +11,13 @@ let title = $state(''); let description = $state(''); let chatEnabled = $state(true); - let formJson = $state(''); + + // Question authoring — mirrors the detail-page Edit tab. editForm is the + // canonical structured form; editFormJson is its textarea-friendly + // serialization. The two stay in sync on mode switches. + let editMode = $state<'visual' | 'json'>('visual'); + let editForm = $state(null); + let editFormJson = $state(''); const SAMPLE_FORM = JSON.stringify( { @@ -26,8 +34,44 @@ 2, ); + function syncJsonFromVisual(): void { + editFormJson = editForm ? JSON.stringify(editForm, null, 2) : ''; + } + + function syncVisualFromJson(): boolean { + const trimmed = editFormJson.trim(); + if (!trimmed) { + editForm = null; + return true; + } + try { + const parsed = JSON.parse(trimmed); + editForm = FeedbackFormDefinitionSchema.parse(parsed); + return true; + } catch (err) { + createError = `Invalid JSON: ${err instanceof Error ? err.message : 'parse error'}`; + return false; + } + } + + function switchEditMode(next: 'visual' | 'json'): void { + if (next === editMode) return; + createError = null; + if (next === 'json') syncJsonFromVisual(); + else if (!syncVisualFromJson()) return; + editMode = next; + } + + function ensureBuilderForm(): void { + if (!editForm) { + editForm = { + questions: [{ id: 'q1', label: 'Question 1', type: 'short_text', required: false }], + }; + } + } + function pasteSample(): void { - formJson = SAMPLE_FORM; + editFormJson = SAMPLE_FORM; } async function createInstance(e: SubmitEvent): Promise { @@ -35,15 +79,30 @@ creating = true; createError = null; - let parsedForm: unknown = null; - const trimmed = formJson.trim(); - if (trimmed) { - try { - parsedForm = JSON.parse(trimmed); - } catch (err) { - createError = `Invalid JSON: ${err instanceof Error ? err.message : 'parse error'}`; - creating = false; - return; + // Resolve form_definition from whichever mode the user is in. Mirrors + // detail-page saveEdits(). + let parsedForm: FeedbackFormDefinition | null = null; + if (editMode === 'json') { + const trimmed = editFormJson.trim(); + if (trimmed) { + try { + parsedForm = FeedbackFormDefinitionSchema.parse(JSON.parse(trimmed)); + } catch (err) { + createError = `Invalid JSON: ${err instanceof Error ? err.message : 'parse error'}`; + creating = false; + return; + } + } + } else { + parsedForm = editForm; + if (parsedForm) { + try { + parsedForm = FeedbackFormDefinitionSchema.parse(parsedForm); + } catch (err) { + createError = `Invalid questions: ${err instanceof Error ? err.message : 'parse error'}`; + creating = false; + return; + } } } @@ -122,22 +181,54 @@
Add questions now (advanced) -

- You can also edit questions visually after the form is created. -

-
- + +
+
+ Questions +
+ + +
+
+ + {#if editMode === 'visual'} + {#if editForm} + + {:else} +

No questions yet.

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

+ You can also edit questions visually after the form is created. +

+ {/if}
-
{#if createError} @@ -166,4 +257,15 @@ .fb-new-advanced[open] > summary { color: var(--color-text-primary); } + .fb-new-questions { + margin-top: var(--space-3); + } + .fb-new-questions__head { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-2); + margin-bottom: var(--space-3); + flex-wrap: wrap; + }