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

15 KiB
Raw Blame History

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
03-wizard-ui 05 execute 4
03-03
03-04
src/components/wizard/DeploymentStep.tsx
src/components/wizard/StepIndicator.tsx
src/App.tsx
false
WIZD-02
WIZD-03
truths artifacts key_links
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
path provides exports
src/components/wizard/DeploymentStep.tsx Step 2 — deployment options (includeInstall, configPath, scriptTargets)
DeploymentStep
path provides exports
src/components/wizard/StepIndicator.tsx Breadcrumb navigation — 1.Backend > 2.Remote Config > 3.Deployment
StepIndicator
path provides
src/App.tsx Step router — renders correct step component by currentStep
from to via pattern
src/App.tsx src/components/wizard/BackendSelectionStep.tsx STEPS[0] — renders when currentStep === 0 BackendSelectionStep
from to via pattern
src/App.tsx src/components/wizard/RemoteConfigStep.tsx STEPS[1] — renders when currentStep === 1, keyed on backendType RemoteConfigStep
from to via pattern
src/components/wizard/StepIndicator.tsx src/store/context.tsx dispatches SET_STEP and conditionally SET_REMOTE_PARAMS on back-nav to step 0 dispatch.*SET_STEP|SET_REMOTE_PARAMS
from to via pattern
src/components/wizard/DeploymentStep.tsx src/store/context.tsx dispatches SET_DEPLOYMENT on every control change dispatch.*SET_DEPLOYMENT
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.

<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>

@.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

From src/store/types.ts:

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:

export function useWizard(): { state: WizardState; dispatch: React.Dispatch<WizardAction> }

Step routing pattern (from RESEARCH.md):

// 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):

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 });
};
Task 1: Implement DeploymentStep and StepIndicator src/components/wizard/DeploymentStep.tsx, src/components/wizard/StepIndicator.tsx 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
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: ` dispatch({ type: 'SET_DEPLOYMENT', payload: { includeInstall: e.target.checked } })} />` - `configPath` radios: two `` elements for 'machine-wide' and 'user-profile' - `scriptTargets` checkboxes: two `` 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
npx vitest run src/components/wizard/StepIndicator.test.tsx --reporter=verbose 2>&1 DeploymentStep.tsx and StepIndicator.tsx exist with named exports. All StepIndicator WIZD-03 tests GREEN. Task 2: Wire App.tsx step router and make App.test GREEN src/App.tsx - 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 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.
npx vitest run src/App.test.tsx --reporter=verbose 2>&1 All App.test.tsx WIZD-02 tests GREEN. App.tsx wires all three steps. StepIndicator renders above each step. Task 3: Human verification of complete wizard flow end-to-end 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. Complete wizard navigation: BackendSelectionStep to RemoteConfigStep to DeploymentStep, with StepIndicator breadcrumb and full back-navigation support. 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) npm run dev 2>&1 | head -5 User confirms the full wizard flow works end-to-end with correct navigation, validation, and data preservation. Type "approved" if navigation works correctly end-to-end, or describe any issues found. 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:

npm run dev 2>&1 | head -5

Server starts without errors.

<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>
After completion, create `.planning/phases/03-wizard-ui/03-05-SUMMARY.md`