Render computed fields read-only and seed server prefill

lead_ref is now an id_gen, issued server-side and stripped from the
submission — so the form shows it as auto rather than as an empty box
somebody is expected to type a reference code into, and never sends it.

Also seeds the inputs from the prefill the form response carries. The
door's channel and the signed-in agent are stamped by a prefill pipeline,
so the form should show them already filled rather than ask.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yashas 2026-08-24 16:19:15 +05:30
parent 5ea627b064
commit e750eb8287
2 changed files with 32 additions and 2 deletions

View File

@ -41,3 +41,13 @@
}
.af__err strong { font-family: ui-monospace, monospace; margin-right: 6px; }
.af__err p { margin: 6px 0 0; font-size: .8rem; color: var(--zk-muted); }
.af__auto {
font-size: .88rem; padding: 8px 10px; border-radius: 4px; min-height: 37px;
border: 1px dashed var(--zk-line); background: var(--zk-tint); color: var(--zk-muted);
display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.af__auto span {
font-size: .6rem; letter-spacing: .07em; text-transform: uppercase;
color: var(--zk-grey); border: 1px solid var(--zk-line); border-radius: 3px; padding: 1px 5px;
}

View File

@ -22,7 +22,19 @@ export default function ActivityForm({ activityUid, instanceId, onDone, onCancel
let dead = false
setSchema(null); setError(null); setValues({})
client.formSchema(activityUid, instanceId)
.then((s) => { if (!dead) setSchema(s) })
.then((s) => {
if (dead) return
setSchema(s)
// The server resolved a prefill pipeline for this activity it stamps
// the channel from the door and the agent from the signed-in user, so
// the form never asks for either. Seed the inputs with what it sent.
const pre = s.prefill_data || s.prefillData || s.field_defaults || {}
if (pre && typeof pre === 'object') {
const seed = {}
for (const f of s.fields) if (pre[f.id] !== undefined) seed[f.id] = pre[f.id]
if (Object.keys(seed).length) setValues(seed)
}
})
.catch((e) => { if (!dead) setError(e) })
return () => { dead = true }
}, [client, activityUid, instanceId])
@ -36,6 +48,9 @@ export default function ActivityForm({ activityUid, instanceId, onDone, onCancel
// and an unknown field is fatal, so empties are dropped rather than sent.
const payload = {}
for (const f of schema.fields) {
// id_gen is issued server-side and stripped from the submission. Sending
// it would be forging a reference the platform owns.
if (f.data_type === 'id_gen') continue
const v = values[f.id]
if (v === undefined || v === '' || (Array.isArray(v) && v.length === 0)) continue
payload[f.id] = f.data_type === 'number' ? Number(v) : v
@ -65,7 +80,12 @@ export default function ActivityForm({ activityUid, instanceId, onDone, onCancel
<label key={f.uid} className={'af__field' + (f.data_type === 'longtext' ? ' af__field--wide' : '')}>
<span>{f.name}{f.mandatory ? <em className="af__req">required</em> : null}</span>
{f.data_type === 'select' ? (
{f.data_type === 'id_gen' ? (
<div className="af__auto">
{v || 'Issued on submit'}
<span>auto</span>
</div>
) : f.data_type === 'select' ? (
<select value={v} onChange={(e) => set(f.id, e.target.value)}>
<option value=""></option>
{opts.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}