{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "form-wizard",
  "type": "registry:block",
  "title": "Form wizard",
  "description": "Validated steps, retained drafts, review and guarded asynchronous completion.",
  "dependencies": [],
  "registryDependencies": [
    "button",
    "https://shine-blond.vercel.app/r/form-panel.json"
  ],
  "files": [
    {
      "path": "blocks/form-wizard.tsx",
      "type": "registry:component",
      "target": "components/shine/form-wizard.tsx",
      "content": "\"use client\";\nimport { useRef,useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { FormPanel,type FormField } from \"@/components/shine/form-panel\";\n/** Each validated step saves its draft in the wizard; only final confirmation writes remotely. */\nexport function FormWizard({title,steps,initialValues,onComplete}:{title:string;steps:{id:string;title:string;description:string;fields:FormField[]}[];initialValues:Record<string,string>;onComplete:(values:Record<string,string>)=>Promise<void>}) {\n const [index,setIndex]=useState(0),[values,setValues]=useState({...initialValues}),[busy,setBusy]=useState(false),[error,setError]=useState(\"\"),[done,setDone]=useState(false);const lock=useRef(false);\n if(!steps.length)throw Error(\"A wizard requires at least one step\");\n return <section data-product-pattern=\"form-wizard\" aria-label={title} className=\"space-y-5\"><h2 className=\"text-xl font-semibold\">{title}</h2><ol aria-label=\"Progress\" className=\"flex flex-wrap gap-3 text-sm\">{steps.map((step,i)=><li key={step.id} aria-current={i===index?\"step\":undefined}>{i+1}. {step.title}{i<index?\" — complete\":\"\"}</li>)}<li aria-current={index===steps.length?\"step\":undefined}>Review</li></ol>{done?<p role=\"status\">Completed successfully.</p>:<>{steps.map((step,i)=><div key={step.id} hidden={i!==index}><FormPanel title={step.title} description={step.description} fields={step.fields} initialValues={initialValues} saveLabel=\"Continue\" onSave={async draft=>{setValues(current=>({...current,...Object.fromEntries(step.fields.map(field=>[field.name,draft[field.name]||\"\"]))}));setIndex(i+1);}}/></div>)}{index===steps.length&&<div className=\"space-y-4\"><h3 className=\"font-semibold\">Review your entries</h3><dl className=\"space-y-3\">{steps.flatMap(step=>step.fields).map(field=><div key={field.name}><dt className=\"text-sm text-muted-foreground\">{field.label}</dt><dd className=\"break-words\">{values[field.name]||\"Not provided\"}</dd></div>)}</dl><p role=\"alert\" className=\"text-sm text-destructive\">{error}</p><Button disabled={busy} onClick={async()=>{if(lock.current)return;lock.current=true;setBusy(true);setError(\"\");try{await onComplete(values);setDone(true);}catch(cause){setError(cause instanceof Error?cause.message:\"Could not complete. Your entries are preserved.\");}finally{lock.current=false;setBusy(false);}}}>{busy?\"Submitting…\":\"Confirm and submit\"}</Button></div>}{index>0&&<Button variant=\"outline\" disabled={busy} onClick={()=>setIndex(index-1)}>Back</Button>}</>}</section>;\n}\n"
    }
  ]
}
