diff --git a/src/app.css b/src/app.css index 92c5991..c67105e 100644 --- a/src/app.css +++ b/src/app.css @@ -229,3 +229,17 @@ body { align-items: flex-start; } } + +.json-input { + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 12px; + line-height: 1.5; + background: #f8fafc; +} + +.error-note { + margin-top: 8px; + font-size: 12px; + color: #b91c1c; + font-weight: 600; +} diff --git a/src/lib/runtime/types.ts b/src/lib/runtime/types.ts index 67900b9..22dc25d 100644 --- a/src/lib/runtime/types.ts +++ b/src/lib/runtime/types.ts @@ -13,13 +13,46 @@ export type RuntimeDashboardConfig = { widgets: Array<{ key: string; title: string; enabled: boolean }>; }; +export type RuntimeOnboardingFieldOption = { + label: string; + value: string; +}; + +export type RuntimeOnboardingValidation = { + minLength?: number; + maxLength?: number; + min?: number; + max?: number; + pattern?: string; +}; + +export type RuntimeOnboardingField = { + id: string; + label: string; + type: 'text' | 'textarea' | 'number' | 'email' | 'tel' | 'date' | 'select' | 'url' | 'file' | 'checkbox'; + required?: boolean; + placeholder?: string; + helperText?: string; + defaultValue?: string | number | boolean | string[]; + readOnly?: boolean; + multiple?: boolean; + options?: RuntimeOnboardingFieldOption[]; + accept?: string; + maxFiles?: number; + maxSizeMB?: number; + validation?: RuntimeOnboardingValidation; +}; + +export type RuntimeOnboardingStep = { + id: string; + title: string; + description?: string; + fields: RuntimeOnboardingField[]; +}; + export type RuntimeOnboardingConfig = { schemaId: string; roleKey: string; version: number; - steps: Array<{ - id: string; - title: string; - fields: Array<{ id: string; label: string; type: string; required?: boolean }>; - }>; + steps: RuntimeOnboardingStep[]; }; diff --git a/src/routes/admin/onboarding-schemas/[schemaId].tsx b/src/routes/admin/onboarding-schemas/[schemaId].tsx index 4ede867..b5c5de5 100644 --- a/src/routes/admin/onboarding-schemas/[schemaId].tsx +++ b/src/routes/admin/onboarding-schemas/[schemaId].tsx @@ -2,21 +2,60 @@ import { useParams } from '@solidjs/router'; import { createSignal, onMount } from 'solid-js'; import AdminShell from '~/components/AdminShell'; import { getRuntimeConfig, saveRuntimeConfig } from '~/lib/runtime/storage'; -import type { RuntimeOnboardingConfig } from '~/lib/runtime/types'; +import type { RuntimeOnboardingConfig, RuntimeOnboardingStep } from '~/lib/runtime/types'; + +function stringifySteps(steps: RuntimeOnboardingStep[]): string { + return JSON.stringify(steps, null, 2); +} export default function EditOnboardingPage() { const params = useParams(); const [config, setConfig] = createSignal(null); + const [stepsJson, setStepsJson] = createSignal('[]'); const [statusMessage, setStatusMessage] = createSignal(''); + const [stepsError, setStepsError] = createSignal(''); onMount(() => { const existing = getRuntimeConfig('onboarding', params.schemaId); - setConfig(existing?.payload || null); + if (!existing?.payload) return; + setConfig(existing.payload); + setStepsJson(stringifySteps(existing.payload.steps)); }); + const syncSteps = (raw: string) => { + setStepsJson(raw); + const current = config(); + if (!current) return; + + try { + const parsed = JSON.parse(raw) as RuntimeOnboardingStep[]; + if (!Array.isArray(parsed)) { + setStepsError('Steps JSON must be an array.'); + return; + } + setConfig({ ...current, steps: parsed }); + setStepsError(''); + } catch { + setStepsError('Invalid JSON. Fix syntax before saving.'); + } + }; + const persist = (status: 'draft' | 'published') => { const payload = config(); if (!payload) return; + if (!payload.schemaId.trim()) { + setStatusMessage('Schema ID is required before saving.'); + return; + } + if (stepsError()) { + setStatusMessage('Fix steps JSON errors before saving.'); + return; + } + if (payload.steps.length === 0) { + setStatusMessage('Add at least one onboarding step before saving.'); + return; + } + saveRuntimeConfig('onboarding', payload.schemaId, payload, status); setStatusMessage(status === 'draft' ? 'Draft saved in runtime storage.' : 'Onboarding schema published in runtime storage.'); }; @@ -24,7 +63,7 @@ export default function EditOnboardingPage() { return (

Edit Onboarding Flow

-

Update this runtime onboarding schema without changing source code.

+

All onboarding fields, validations, upload rules, and select behaviors are runtime schema-driven.

{!config() ? (

Onboarding schema not found in local runtime storage.

) : ( @@ -44,25 +83,9 @@ export default function EditOnboardingPage() { setConfig({ ...config()!, version: Number(e.currentTarget.value || 1) })} />
- -