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
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
---
|
||||
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>
|
||||
Reference in New Issue
Block a user