import { useEffect, useState } from 'react' import { useZino } from '../api/provider.jsx' import './ActivityForm.css' /** * Renders whatever /view/form-screens returns for an activity — labels, types, * select options and which fields are mandatory — and submits it straight back. * * Nothing about this form is defined in the frontend. Add a field to the * activity in Studio, redeploy, and it appears here with no code change. That * is the point: the workflow is the source of truth, and a hardcoded form would * quietly drift from it. */ export default function ActivityForm({ activityUid, instanceId, onDone, onCancel }) { const { client } = useZino() const [schema, setSchema] = useState(null) const [values, setValues] = useState({}) const [error, setError] = useState(null) const [busy, setBusy] = useState(false) useEffect(() => { let dead = false setSchema(null); setError(null); setValues({}) client.formSchema(activityUid, instanceId) .then((s) => { if (!dead) setSchema(s) }) .catch((e) => { if (!dead) setError(e) }) return () => { dead = true } }, [client, activityUid, instanceId]) function set(id, v) { setValues((p) => ({ ...p, [id]: v })) } async function submit(e) { e.preventDefault() setBusy(true); setError(null) // Send only fields the activity defines. A submission is schema-validated // and an unknown field is fatal, so empties are dropped rather than sent. const payload = {} for (const f of schema.fields) { 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 } try { const res = instanceId ? await client.activity(instanceId, activityUid, payload) : await client.start(activityUid, payload) onDone?.(res) } catch (err) { setError(err) } finally { setBusy(false) } } if (error && !schema) return
Could not load the form — {error.status} {error.message}
if (!schema) return

Loading the form…

return (
{schema.fields.map((f) => { const opts = f.properties?.options || [] const v = values[f.id] ?? (f.data_type === 'multiselect' ? [] : '') return (