--- phase: 03-wizard-ui plan: "04" type: execute wave: 3 depends_on: - "03-01" - "03-02" files_modified: - src/components/wizard/RemoteConfigStep.tsx autonomous: true requirements: - BACK-01 - BACK-02 - BACK-03 must_haves: truths: - "RemoteConfigStep renders the correct fields for azureblob backend using BACKEND_REGISTRY iteration" - "RemoteConfigStep renders the correct fields for s3 backend using BACKEND_REGISTRY iteration" - "RemoteConfigStep renders the correct fields for s3-compatible backend including an endpoint field" - "AzureAuthToggle appears for azureblob backend — SAS URL shown by default, toggle switches to Access Key" - "Toggling Azure auth method does not clear the hidden field value (both registered)" - "Errors only show after first Next attempt, then update live (mode: onSubmit, reValidateMode: onChange)" - "RemoteConfigStep component is keyed on backendType to force remount on backend change" artifacts: - path: "src/components/wizard/RemoteConfigStep.tsx" provides: "Step 1 — registry-driven backend configuration form" exports: ["RemoteConfigStep"] key_links: - from: "src/components/wizard/RemoteConfigStep.tsx" to: "src/schemas/registry.ts" via: "BACKEND_REGISTRY[backendType] drives field rendering loop" pattern: "BACKEND_REGISTRY" - from: "src/components/wizard/RemoteConfigStep.tsx" to: "src/schemas/index.ts" via: "BACKEND_SCHEMAS[backendType] provides Zod resolver" pattern: "BACKEND_SCHEMAS" - from: "src/components/wizard/RemoteConfigStep.tsx" to: "src/store/context.tsx" via: "dispatches SET_REMOTE_PARAMS on Next, SET_STEP(2)" pattern: "dispatch.*SET_REMOTE_PARAMS|SET_STEP" --- Implement RemoteConfigStep — the second wizard step with a registry-driven form for configuring the selected backend. Purpose: Satisfies BACK-01 (Azure Blob config with auth toggle), BACK-02 (S3 config), BACK-03 (S3-compatible with endpoint). Output: src/components/wizard/RemoteConfigStep.tsx, RemoteConfigStep tests GREEN. @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md @.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-01-SUMMARY.md @.planning/phases/03-wizard-ui/03-02-SUMMARY.md From src/schemas/registry.ts: ```typescript export type BackendType = 'azureblob' | 's3' | 's3-compatible'; export const BACKEND_REGISTRY: Record // azureblob fields: account (text, required), key (password, optional), sas_url (password, optional) // s3 fields: provider (select/hidden), access_key_id (text), secret_access_key (password), region (text) // s3-compatible fields: provider (select/hidden), access_key_id, secret_access_key, endpoint (text, required), region (text, optional) ``` From src/schemas/index.ts: ```typescript export const BACKEND_SCHEMAS = { azureblob: ZodObject, s3: ZodObject, 's3-compatible': ZodObject, } as const; // All schemas built from BACKEND_REGISTRY — key/sas_url are optional (z.string().optional()) ``` From src/store/types.ts: ```typescript // On Next: dispatch SET_REMOTE_PARAMS with all form values // Then: dispatch SET_STEP(2) dispatch({ type: 'SET_REMOTE_PARAMS', payload: values }); dispatch({ type: 'SET_STEP', payload: 2 }); ``` From src/components/ui/FieldRenderer.tsx (Plan 02): ```typescript export function FieldRenderer(props: { field: FieldDef; register: UseFormRegister; error?: FieldError; }): JSX.Element // Handles text, password, select, hidden (provider single-option) ``` From src/components/wizard/AzureAuthToggle.tsx (Plan 02): ```typescript export function AzureAuthToggle(props: { register: UseFormRegister; errors: { key?: FieldError; sas_url?: FieldError }; }): JSX.Element // Renders segmented SAS/Key toggle with both fields always registered ``` react-hook-form registry-driven pattern (from RESEARCH.md): ```tsx const { register, handleSubmit, formState: { errors } } = useForm({ resolver: zodResolver(schema), mode: 'onSubmit', reValidateMode: 'onChange', defaultValues: state.remote.params, }); const onNext = (values: Record) => { dispatch({ type: 'SET_REMOTE_PARAMS', payload: values }); dispatch({ type: 'SET_STEP', payload: 2 }); }; ``` Remount on backend change (from RESEARCH.md Pitfall 1): ```tsx // In App.tsx (or wherever RemoteConfigStep is rendered): // This forces full remount when backend changes — prevents stale defaultValues ``` Task 1: Implement RemoteConfigStep and make its tests GREEN src/components/wizard/RemoteConfigStep.tsx - BACK-01: When backendType is 'azureblob', renders the 'account' text input - BACK-01: When backendType is 'azureblob', renders AzureAuthToggle (not separate key/sas_url FieldRenderer calls) - BACK-01: Azure form submits with both sas_url and key values in params (even if one is empty string) - BACK-01: Toggling auth method in AzureAuthToggle does not remove the hidden field from form state - BACK-02: When backendType is 's3', renders access_key_id, secret_access_key, and region inputs - BACK-02: S3 provider field is hidden (auto-registered with value 'AWS') — no visible dropdown - BACK-03: When backendType is 's3-compatible', renders endpoint field in addition to access_key_id, secret_access_key - BACK-03: S3-compatible provider field is hidden (auto-registered with value 'Other') - All backends: Errors do not show before first Next attempt (mode: 'onSubmit') - All backends: Errors show after first failed Next attempt and update live (reValidateMode: 'onChange') - All backends: Clicking Next with valid data dispatches SET_REMOTE_PARAMS then SET_STEP(2) Create `src/components/wizard/RemoteConfigStep.tsx`. The component reads `state.remote.backendType` and `state.remote.params` from `useWizard()`. It must NOT render if `backendType` is null (user somehow reached step 1 without selecting — guard with early return or redirect to step 0). For the Azure backend, the field rendering is SPECIAL — do not loop `BACKEND_REGISTRY['azureblob']` naively: - Render `account` field via `FieldRenderer` - Render `AzureAuthToggle` for `key` / `sas_url` (handles both fields internally) - Do NOT pass key/sas_url through the generic FieldRenderer loop for azureblob For S3 and S3-compatible, loop all `BACKEND_REGISTRY[backendType]` fields through `FieldRenderer`. The `provider` field with a single option is auto-hidden by `FieldRenderer` already. Implementation structure: ```tsx export function RemoteConfigStep() { const { state, dispatch } = useWizard(); const backendType = state.remote.backendType; // Guard — should never happen but prevents runtime errors if (!backendType) { dispatch({ type: 'SET_STEP', payload: 0 }); return null; } const schema = BACKEND_SCHEMAS[backendType]; const { register, handleSubmit, formState: { errors } } = useForm({ resolver: zodResolver(schema), mode: 'onSubmit', reValidateMode: 'onChange', defaultValues: state.remote.params, }); const onNext = (values: Record) => { dispatch({ type: 'SET_REMOTE_PARAMS', payload: values }); dispatch({ type: 'SET_STEP', payload: 2 }); }; const backendLabel = { azureblob: 'Azure Blob Storage', s3: 'Amazon S3', 's3-compatible': 'S3-Compatible Storage', }[backendType]; return (

Step 2: Configure {backendLabel}

{backendType === 'azureblob' ? ( <> {/* Account field via FieldRenderer */} f.key === 'account')!} register={register} error={errors.account} /> {/* Auth toggle handles key + sas_url — both always registered */} ) : ( /* S3 and S3-compatible: full registry loop — FieldRenderer handles provider hiding */ BACKEND_REGISTRY[backendType].map(field => ( )) )}
); } ``` Now update `src/components/wizard/RemoteConfigStep.test.tsx` to make all tests GREEN. Tests wrap component in `WizardProvider`. To set the backendType before rendering, create a helper that renders with a specific initial step/backend — dispatch actions in a test wrapper component, or initialize a custom context. Practical test approach: Create a `TestWrapper` helper inside the test file that wraps with `WizardProvider` and dispatches the desired initial state before rendering `RemoteConfigStep`: ```tsx function renderWithBackend(backendType: BackendType) { function Setup() { const { dispatch } = useWizard(); useEffect(() => { dispatch({ type: 'SET_BACKEND_TYPE', payload: backendType }); dispatch({ type: 'SET_STEP', payload: 1 }); }, []); return ; } return render(); } ```
npx vitest run src/components/wizard/RemoteConfigStep.test.tsx --reporter=verbose 2>&1 All RemoteConfigStep tests pass GREEN. BACK-01 (Azure form with toggle), BACK-02 (S3 fields), BACK-03 (S3-compatible with endpoint) all green. RemoteConfigStep.tsx exports the component correctly.
```bash npx vitest run src/components/wizard/RemoteConfigStep.test.tsx --reporter=verbose 2>&1 ``` All BACK-01, BACK-02, BACK-03 tests GREEN. Full suite check: ```bash npx vitest run 2>&1 | tail -10 ``` RemoteConfigStep tests GREEN. BackendSelectionStep tests GREEN (from Plan 03). App and StepIndicator stubs still RED (expected — Plan 05). - RemoteConfigStep.tsx exists with named export - Azure Blob form: account field + AzureAuthToggle renders (both key and sas_url registered, only one visible) - S3 form: access_key_id, secret_access_key, region fields render; provider field is hidden - S3-compatible form: same as S3 plus endpoint field renders; provider field is hidden - Touch-then-live validation: no errors on initial render, errors after first failed submit - On valid submit: dispatches SET_REMOTE_PARAMS with all field values (including hidden auth field), then SET_STEP(2) - All BACK-01, BACK-02, BACK-03 tests GREEN After completion, create `.planning/phases/03-wizard-ui/03-04-SUMMARY.md`