Files
Ready2Blob/.planning/phases/03-wizard-ui/03-04-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

12 KiB

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 04 execute 3
03-01
03-02
src/components/wizard/RemoteConfigStep.tsx
true
BACK-01
BACK-02
BACK-03
truths artifacts key_links
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
path provides exports
src/components/wizard/RemoteConfigStep.tsx Step 1 — registry-driven backend configuration form
RemoteConfigStep
from to via pattern
src/components/wizard/RemoteConfigStep.tsx src/schemas/registry.ts BACKEND_REGISTRY[backendType] drives field rendering loop BACKEND_REGISTRY
from to via pattern
src/components/wizard/RemoteConfigStep.tsx src/schemas/index.ts BACKEND_SCHEMAS[backendType] provides Zod resolver BACKEND_SCHEMAS
from to via pattern
src/components/wizard/RemoteConfigStep.tsx src/store/context.tsx dispatches SET_REMOTE_PARAMS on Next, SET_STEP(2) 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.

<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-01-SUMMARY.md @.planning/phases/03-wizard-ui/03-02-SUMMARY.md

From src/schemas/registry.ts:

export type BackendType = 'azureblob' | 's3' | 's3-compatible';
export const BACKEND_REGISTRY: Record<BackendType, FieldDef[]>
// 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:

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:

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

export function FieldRenderer(props: {
  field: FieldDef;
  register: UseFormRegister<any>;
  error?: FieldError;
}): JSX.Element
// Handles text, password, select, hidden (provider single-option)

From src/components/wizard/AzureAuthToggle.tsx (Plan 02):

export function AzureAuthToggle(props: {
  register: UseFormRegister<any>;
  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):

const { register, handleSubmit, formState: { errors } } = useForm({
  resolver: zodResolver(schema),
  mode: 'onSubmit',
  reValidateMode: 'onChange',
  defaultValues: state.remote.params,
});
const onNext = (values: Record<string, string>) => {
  dispatch({ type: 'SET_REMOTE_PARAMS', payload: values });
  dispatch({ type: 'SET_STEP', payload: 2 });
};

Remount on backend change (from RESEARCH.md Pitfall 1):

// In App.tsx (or wherever RemoteConfigStep is rendered):
<RemoteConfigStep key={state.remote.backendType} />
// 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<string, string>) => {
    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 (
    <div>
      <h2>Step 2: Configure {backendLabel}</h2>
      <form onSubmit={handleSubmit(onNext)}>
        {backendType === 'azureblob' ? (
          <>
            {/* Account field via FieldRenderer */}
            <FieldRenderer
              field={BACKEND_REGISTRY.azureblob.find(f => f.key === 'account')!}
              register={register}
              error={errors.account}
            />
            {/* Auth toggle handles key + sas_url — both always registered */}
            <AzureAuthToggle
              register={register}
              errors={{ key: errors.key, sas_url: errors.sas_url }}
            />
          </>
        ) : (
          /* S3 and S3-compatible: full registry loop — FieldRenderer handles provider hiding */
          BACKEND_REGISTRY[backendType].map(field => (
            <FieldRenderer
              key={field.key}
              field={field}
              register={register}
              error={errors[field.key]}
            />
          ))
        )}
        <div className="flex gap-3 mt-6">
          <button
            type="button"
            onClick={() => dispatch({ type: 'SET_STEP', payload: 0 })}
            className="px-4 py-2 text-sm border border-gray-300 rounded-md hover:bg-gray-50"
          >
            Back
          </button>
          <button
            type="submit"
            className="px-4 py-2 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700"
          >
            Next
          </button>
        </div>
      </form>
    </div>
  );
}
```

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 <RemoteConfigStep key={backendType} />;
  }
  return render(<WizardProvider><Setup /></WizardProvider>);
}
```
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:

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

<success_criteria>

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