Shared create wizard step primitive
## Original requirement (verbatim, epic body)
> "Break long pages into subtabs across the entire system."
> "Important and first hand stuff needs to be on the first page(s), then less important, logs, runs, etc"
Operator decisions: D3 "When creating, make a wizard-like". D5 "Only long create forms". D6 New automation: "Steps + review".
## Scope (Architecture K2 — contract C2, pinned)
New `apps/web/src/components/wizard/` — NOT a tab component (do not fork `Tabs`); a stepper for create flows only. Two consumer children (New automation, New project) build against this exact API:
```ts
// components/wizard/useWizard.ts
export type WizardStep<Id extends string> = { id: Id; label: string; valid: boolean; invalidField?: { inputId: string; label: string } }
export function useWizard<Id extends string>(steps: readonly WizardStep<Id>[]): {
step: Id; index: number; isFirst: boolean; isLast: boolean
next: () => void // blocked while steps[index].valid === false: focuses invalidField.inputId and exposes blockedMessage "<label> is required" (or the step's own message)
back: () => void
goTo: (id: Id) => void // allowed only to a step index <= furthest step reached
blockedMessage: string | null
furthest: number
}
// components/wizard/WizardSteps.tsx
export function WizardSteps<Id extends string>(p: { steps: readonly WizardStep<Id>[]; wizard: ReturnType<typeof useWizard<Id>>; label: string; testId: string }): JSX.Element
// components/wizard/WizardReview.tsx
export function WizardReview(p: { rows: { stepId: string; label: string; value: ReactNode }[]; onEdit: (stepId: string) => void; testId: string }): JSX.Element
```
- Step state is component state, NOT the URL (no `?step=`).
- Consumers keep every step's fields MOUNTED (hidden) across steps, like `TabPanel`; provide whatever small helper makes that the easy path (e.g. a `WizardStepPanel` with `hidden`), and document it in the module header.
- `WizardSteps` ≥ 600 px: MUI `Stepper` (horizontal, every step label in full, completed steps clickable). < 600 px: one line "Step 2 of 5 · When" plus a thin progress bar — never a truncated label row.
- Footer reuses `ui/FormActionBar`: Back (hidden on step 1), Next (all but last), Create (last). Nothing is POSTed before Create.
- `WizardReview` shows each value read-only with an Edit link per step (calls `onEdit(stepId)` → consumer calls `goTo`).
- If the step list changes length (a step omitted by a predicate) the hook keeps `index`/`furthest` consistent.
## Acceptance criteria (verbatim, PRD R5 — the mechanics this child owns)
- **AC5.1** (mechanics) A step indicator shows every step name and the current step. Back and Next move between steps. Next is blocked while the current step is invalid, and the invalid field is named. Steps already done can be revisited from the indicator. Nothing is saved until Create. Review shows every chosen value read-only, with an Edit link per step.
- **AC5.4 (quality)** At 390 px the step indicator stays legible (it may collapse to "Step 2 of 5 · When") and never truncates a step name to fragments.
Credentials & reach: none — every oracle here is a unit/component/Playwright-local test inside the worktree (verified, no credential needed).
Out of scope: Do NOT wire the wizard into any page (the New automation and New project children do that). Do NOT modify or fork `components/Tabs.tsx`, `hooks/useUrlTab.ts`, `components/addNode/AddNodeDialog.tsx` (already a stepper — unchanged). Do NOT touch `apps/api`, `apps/jaahd`, migrations. No URL step state. No feature flag.
Done means:
- Next blocked — unit test: invalid step → `next()` does not advance, `blockedMessage` names the field, focus moves to `invalidField.inputId`.
- Revisit — test: `goTo` a completed step works; `goTo` beyond `furthest` is refused; indicator's completed steps are clickable, future ones are not.
- Mounted steps — test: a value typed in step 1 survives Next → Back.
- AC5.4 — component test at <600 px renders "Step 2 of 5 · When" + progress bar; Playwright screenshot at 390 px and 1440 px of a test harness/story shows full labels, no ellipsis.
- Review — test: rows render read-only, Edit link calls `onEdit` with its step id.
- `./dc check quick` green; PR merged to develop.
Outcome
Shipped components/wizard (useWizard, WizardSteps, StepPanel, Footer, Review) per C2; 15 tests, quick gate green; PR #5069 merged, Jaah Deploy success. Added optional invalidMessage + validateAll. Not wired into pages (siblings do).
Artifacts
Activity
- Draft
- Ready
- Runner started
- Runner ended (done)