Files
Ready2Blob/.planning/phases/03-wizard-ui/03-05-PLAN.md
T
kawa 53bbd00533 docs(05-03): complete act()-warning elimination plan
- SUMMARY.md: userEvent v14 migration + vi.useFakeTimers() for ReviewStep
- STATE.md: advanced to completed 05-03, added patterns as decisions
- ROADMAP.md: phase 5 now 4/4 plans complete (Complete status)
- REQUIREMENTS.md: TECH-05 marked complete
2026-03-30 11:44:59 +02:00

322 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 03-wizard-ui
plan: "05"
type: execute
wave: 4
depends_on:
- "03-03"
- "03-04"
files_modified:
- src/components/wizard/DeploymentStep.tsx
- src/components/wizard/StepIndicator.tsx
- src/App.tsx
autonomous: false
requirements:
- WIZD-02
- WIZD-03
must_haves:
truths:
- "App.tsx renders BackendSelectionStep for currentStep 0, RemoteConfigStep for step 1, DeploymentStep for step 2"
- "StepIndicator shows 3 labeled steps with checkmarks on completed steps"
- "Clicking a completed step in StepIndicator dispatches SET_STEP — clicking step 0 also dispatches SET_REMOTE_PARAMS({})"
- "DeploymentStep renders includeInstall toggle, configPath radio group, and scriptTargets checkboxes"
- "DeploymentStep dispatches SET_DEPLOYMENT on each change, keeping deployment state in sync"
- "Full forward + backward wizard navigation works without losing entered data in any step"
artifacts:
- path: "src/components/wizard/DeploymentStep.tsx"
provides: "Step 2 — deployment options (includeInstall, configPath, scriptTargets)"
exports: ["DeploymentStep"]
- path: "src/components/wizard/StepIndicator.tsx"
provides: "Breadcrumb navigation — 1.Backend > 2.Remote Config > 3.Deployment"
exports: ["StepIndicator"]
- path: "src/App.tsx"
provides: "Step router — renders correct step component by currentStep"
key_links:
- from: "src/App.tsx"
to: "src/components/wizard/BackendSelectionStep.tsx"
via: "STEPS[0] — renders when currentStep === 0"
pattern: "BackendSelectionStep"
- from: "src/App.tsx"
to: "src/components/wizard/RemoteConfigStep.tsx"
via: "STEPS[1] — renders when currentStep === 1, keyed on backendType"
pattern: "RemoteConfigStep"
- from: "src/components/wizard/StepIndicator.tsx"
to: "src/store/context.tsx"
via: "dispatches SET_STEP and conditionally SET_REMOTE_PARAMS on back-nav to step 0"
pattern: "dispatch.*SET_STEP|SET_REMOTE_PARAMS"
- from: "src/components/wizard/DeploymentStep.tsx"
to: "src/store/context.tsx"
via: "dispatches SET_DEPLOYMENT on every control change"
pattern: "dispatch.*SET_DEPLOYMENT"
---
<objective>
Wire the complete wizard: implement DeploymentStep and StepIndicator, then update App.tsx to route between all three steps and render the breadcrumb navigation.
Purpose: Satisfies WIZD-02 (multi-step navigation shell) and WIZD-03 (back navigation without data loss). Closes the full wizard loop.
Output: DeploymentStep, StepIndicator, App.tsx wired — all tests GREEN.
</objective>
<execution_context>
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/phases/03-wizard-ui/03-CONTEXT.md
@.planning/phases/03-wizard-ui/03-RESEARCH.md
@.planning/phases/03-wizard-ui/03-03-SUMMARY.md
@.planning/phases/03-wizard-ui/03-04-SUMMARY.md
<interfaces>
<!-- Key types and contracts the executor needs. -->
From src/store/types.ts:
```typescript
export interface WizardState {
currentStep: number; // 0=Backend, 1=RemoteConfig, 2=Deployment
remote: {
name: string;
backendType: BackendType | null;
params: Record<string, string>;
};
deployment: {
includeInstall: boolean; // toggle — default: false
configPath: 'machine-wide' | 'user-profile'; // radio — default: 'machine-wide'
scriptTargets: ('intune' | 'rmm')[]; // checkboxes — default: both selected
};
}
// Deploy step actions:
dispatch({ type: 'SET_DEPLOYMENT', payload: { includeInstall: true } });
dispatch({ type: 'SET_DEPLOYMENT', payload: { configPath: 'user-profile' } });
dispatch({ type: 'SET_DEPLOYMENT', payload: { scriptTargets: ['intune'] } });
// Back-nav to step 0 (CRITICAL: clears params but NOT deployment):
dispatch({ type: 'SET_REMOTE_PARAMS', payload: {} });
dispatch({ type: 'SET_STEP', payload: 0 });
```
From src/store/context.tsx:
```typescript
export function useWizard(): { state: WizardState; dispatch: React.Dispatch<WizardAction> }
```
Step routing pattern (from RESEARCH.md):
```tsx
// App.tsx step routing
import { BackendSelectionStep } from './components/wizard/BackendSelectionStep';
import { RemoteConfigStep } from './components/wizard/RemoteConfigStep';
import { DeploymentStep } from './components/wizard/DeploymentStep';
import { StepIndicator } from './components/wizard/StepIndicator';
// Key RemoteConfigStep on backendType to force remount on backend change (Pitfall 1 fix)
const STEPS = [
() => <BackendSelectionStep />,
() => <RemoteConfigStep key={state.remote.backendType} />,
() => <DeploymentStep />,
];
```
StepIndicator back-nav pattern (from RESEARCH.md):
```tsx
const handleStepClick = (targetStep: number) => {
if (targetStep === 0 && state.currentStep > 0) {
dispatch({ type: 'SET_REMOTE_PARAMS', payload: {} }); // clear params (backend may change)
// Do NOT dispatch RESET — deployment options must be preserved
}
dispatch({ type: 'SET_STEP', payload: targetStep });
};
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Implement DeploymentStep and StepIndicator</name>
<files>src/components/wizard/DeploymentStep.tsx, src/components/wizard/StepIndicator.tsx</files>
<behavior>
DeploymentStep:
- Renders an "Include rclone installation" toggle (checkbox or switch); state.deployment.includeInstall default false
- Renders a "Config deployment path" radio group: 'machine-wide' (C:\ProgramData\rclone\) and 'user-profile' (%APPDATA%\rclone\); default machine-wide
- Renders a "Script targets" checkbox group: 'intune' and 'rmm' checkboxes; both checked by default
- Each control dispatches SET_DEPLOYMENT immediately on change (live sync, no Next button needed for deployment options)
- Renders a "Back" button that dispatches SET_STEP(1) and a "Next / Review" button that dispatches SET_STEP(3) to advance to Phase 4's review step
StepIndicator:
- Renders three labeled steps: "1. Backend", "2. Remote Config", "3. Deployment"
- Current step is bold/active
- Completed steps (step index < currentStep) show a checkmark prefix and are clickable buttons
- Future steps are not clickable (non-interactive)
- Clicking a completed step that is NOT step 0 dispatches only SET_STEP(targetStep)
- Clicking step 0 dispatches SET_REMOTE_PARAMS({}) THEN SET_STEP(0) — clears params for potential backend change
- Does NOT dispatch RESET on any click
</behavior>
<action>
Create `src/components/wizard/DeploymentStep.tsx`:
- Uses `useWizard()` to read `state.deployment` and `dispatch`
- Does NOT use react-hook-form — deployment fields dispatch directly via onChange handlers
- `includeInstall` toggle: `<input type="checkbox" checked={state.deployment.includeInstall} onChange={e => dispatch({ type: 'SET_DEPLOYMENT', payload: { includeInstall: e.target.checked } })} />`
- `configPath` radios: two `<input type="radio">` elements for 'machine-wide' and 'user-profile'
- `scriptTargets` checkboxes: two `<input type="checkbox">` for 'intune' and 'rmm' — on change, compute new array and dispatch SET_DEPLOYMENT
- Back button: `dispatch({ type: 'SET_STEP', payload: 1 })`
- Next button: `dispatch({ type: 'SET_STEP', payload: 3 })` — step 3 is Phase 4's review/download (placeholder for now)
Create `src/components/wizard/StepIndicator.tsx`:
- Uses `useWizard()` to read `state.currentStep`
- Renders a horizontal breadcrumb with separators ()
- Step labels: `['Backend', 'Remote Config', 'Deployment']`
- For each step index i:
- If i < currentStep: completed — render as `<button>` with "checkmark {label}"
- If i === currentStep: active — render as `<span>` bold with "{i+1}. {label}"
- If i > currentStep: future — render as `<span>` muted with "{i+1}. {label}"
- `handleStepClick(i)`: if i === 0 and currentStep > 0, dispatch SET_REMOTE_PARAMS({}) first, then dispatch SET_STEP(i)
Update `src/components/wizard/StepIndicator.test.tsx` to make all WIZD-03 tests GREEN:
- "clicking a completed step dispatches SET_STEP" — render with currentStep=2, click "Backend" step, verify SET_STEP(0) was dispatched (use spy or observe re-render)
- "clicking back to step 0 dispatches SET_REMOTE_PARAMS({})" — same scenario, verify params are cleared
- "clicking back to step 0 does NOT dispatch RESET" — verify no full RESET
- "step 0 shows as active when currentStep is 0" — render with step 0
- "completed steps are clickable" — render with step 2, verify step 0 and 1 have button role
</action>
<verify>
<automated>npx vitest run src/components/wizard/StepIndicator.test.tsx --reporter=verbose 2>&1</automated>
</verify>
<done>
DeploymentStep.tsx and StepIndicator.tsx exist with named exports. All StepIndicator WIZD-03 tests GREEN.
</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Wire App.tsx step router and make App.test GREEN</name>
<files>src/App.tsx</files>
<behavior>
- App renders BackendSelectionStep when state.currentStep === 0
- App renders RemoteConfigStep when state.currentStep === 1
- App renders DeploymentStep when state.currentStep === 2
- App always renders StepIndicator above the current step component
- RemoteConfigStep is keyed on state.remote.backendType to force remount on backend change
</behavior>
<action>
Replace the placeholder `src/App.tsx` with the step router.
```tsx
import { useWizard } from './store/context';
import { WizardProvider } from './store/context';
import { StepIndicator } from './components/wizard/StepIndicator';
import { BackendSelectionStep } from './components/wizard/BackendSelectionStep';
import { RemoteConfigStep } from './components/wizard/RemoteConfigStep';
import { DeploymentStep } from './components/wizard/DeploymentStep';
function WizardShell() {
const { state } = useWizard();
const steps = [
<BackendSelectionStep />,
<RemoteConfigStep key={state.remote.backendType ?? 'none'} />,
<DeploymentStep />,
];
// Guard: clamp to valid range (phase 4 adds step 3 later)
const stepIndex = Math.min(state.currentStep, steps.length - 1);
const CurrentStep = steps[stepIndex];
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center py-12 px-4">
<div className="w-full max-w-2xl">
<h1 className="text-3xl font-bold text-gray-900 mb-8 text-center">Ready2Blob</h1>
<StepIndicator />
<div className="mt-8">
{CurrentStep}
</div>
</div>
</div>
);
}
export default function App() {
return (
<WizardProvider>
<WizardShell />
</WizardProvider>
);
}
```
Then update `src/App.test.tsx` to make all WIZD-02 tests GREEN:
- "renders BackendSelectionStep when currentStep is 0" — render App, expect to find backend card grid (e.g., text "Azure Blob Storage")
- "renders RemoteConfigStep when currentStep is 1" — render App, dispatch SET_BACKEND_TYPE + SET_STEP(1), expect backend config form to appear
- "renders DeploymentStep when currentStep is 2" — render App, navigate to step 2, expect deployment options to appear
Testing approach: `render(<App />)`, then use a dispatch helper or find elements that are only present on the target step.
</action>
<verify>
<automated>npx vitest run src/App.test.tsx --reporter=verbose 2>&1</automated>
</verify>
<done>
All App.test.tsx WIZD-02 tests GREEN. App.tsx wires all three steps. StepIndicator renders above each step.
</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<name>Task 3: Human verification of complete wizard flow end-to-end</name>
<action>Human runs `npm run dev` and manually verifies the complete wizard flow in the browser. No code changes required — this task only requires visual inspection and interaction testing.</action>
<what-built>Complete wizard navigation: BackendSelectionStep to RemoteConfigStep to DeploymentStep, with StepIndicator breadcrumb and full back-navigation support.</what-built>
<how-to-verify>
1. Run `npm run dev` and open http://localhost:5173
2. Verify the breadcrumb shows "1. Backend 2. Remote Config 3. Deployment"
3. Step 1 (Backend Selection):
- Remote name field is at the top
- Try clicking a card without a name — inline error should appear below the name field
- Enter "my-remote" in the name field, click "Azure Blob Storage" — should advance to step 2
4. Step 2 (Remote Config) with Azure:
- Storage Account Name field visible
- "SAS URL" is the default active auth method
- Enter a SAS URL, toggle to "Access Key", enter a key — both values should be in the form
- Click "Back" — should return to step 1 with "my-remote" still in the name field
5. Navigate back to step 1, click "Amazon S3" — should advance to step 2 with S3 fields
6. Verify S3 form: access key ID, secret access key, region fields (no provider dropdown visible)
7. Fill S3 form and click "Next" — should advance to step 3
8. Step 3 (Deployment): verify include-install toggle, config path radios, script targets checkboxes
9. Click step "1. Backend" in breadcrumb from step 3 — should navigate back to step 1 without losing deployment options
10. Select a different backend — step 2 form should be blank (not showing old S3 values)
</how-to-verify>
<verify>
<automated>npm run dev 2>&1 | head -5</automated>
</verify>
<done>User confirms the full wizard flow works end-to-end with correct navigation, validation, and data preservation.</done>
<resume-signal>Type "approved" if navigation works correctly end-to-end, or describe any issues found.</resume-signal>
</task>
</tasks>
<verification>
Full automated suite:
```bash
npx vitest run --reporter=verbose 2>&1
```
All tests GREEN: App.test.tsx (WIZD-02), BackendSelectionStep.test.tsx (WIZD-01, WIZD-04), RemoteConfigStep.test.tsx (BACK-01, BACK-02, BACK-03), StepIndicator.test.tsx (WIZD-03).
Dev server check:
```bash
npm run dev 2>&1 | head -5
```
Server starts without errors.
</verification>
<success_criteria>
- All 7 requirement test suites GREEN (WIZD-01, WIZD-02, WIZD-03, WIZD-04, BACK-01, BACK-02, BACK-03)
- App.tsx routes to correct step component by currentStep
- RemoteConfigStep keyed on backendType (prevents stale form values on backend change)
- StepIndicator dispatches SET_REMOTE_PARAMS({}) when navigating back to step 0 (preserves deployment)
- DeploymentStep dispatches SET_DEPLOYMENT live on every control change
- Human verifies full wizard flow end-to-end: forward navigation, back navigation, data preservation
</success_criteria>
<output>
After completion, create `.planning/phases/03-wizard-ui/03-05-SUMMARY.md`
</output>