nxtgauge-admin-solid/src/routes/admin/onboarding-schemas/[schemaId].tsx

104 lines
4.1 KiB
TypeScript

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, 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<RuntimeOnboardingConfig | null>(null);
const [stepsJson, setStepsJson] = createSignal('[]');
const [statusMessage, setStatusMessage] = createSignal('');
const [stepsError, setStepsError] = createSignal('');
onMount(() => {
const existing = getRuntimeConfig<RuntimeOnboardingConfig>('onboarding', params.schemaId);
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.');
};
return (
<AdminShell>
<h1 class="page-title">Edit Onboarding Flow</h1>
<p class="page-subtitle">All onboarding fields, validations, upload rules, and select behaviors are runtime schema-driven.</p>
{!config() ? (
<section class="card"><p class="notice">Onboarding schema not found in local runtime storage.</p></section>
) : (
<div class="grid">
<section class="card">
<h2>Onboarding Builder</h2>
<div class="field">
<label>Schema ID</label>
<input value={config()!.schemaId} onInput={(e) => setConfig({ ...config()!, schemaId: e.currentTarget.value })} />
</div>
<div class="field">
<label>Role Key</label>
<input value={config()!.roleKey} onInput={(e) => setConfig({ ...config()!, roleKey: e.currentTarget.value.toUpperCase() })} />
</div>
<div class="field">
<label>Version</label>
<input type="number" value={config()!.version} onInput={(e) => setConfig({ ...config()!, version: Number(e.currentTarget.value || 1) })} />
</div>
<div class="field">
<label>Steps JSON (full runtime schema)</label>
<textarea class="json-input" rows={18} value={stepsJson()} onInput={(e) => syncSteps(e.currentTarget.value)} />
{stepsError() && <p class="error-note">{stepsError()}</p>}
</div>
<div class="actions">
<button class="btn" onClick={() => persist('draft')}>Save Draft</button>
<button class="btn primary" onClick={() => persist('published')}>Publish</button>
</div>
{statusMessage() && <p class="inline-note">{statusMessage()}</p>}
</section>
<section class="card">
<h2>Runtime Config Preview</h2>
<pre class="json">{JSON.stringify(config(), null, 2)}</pre>
</section>
</div>
)}
</AdminShell>
);
}