Files
Ready2Blob/.planning/phases/06-new-backends/06-03-PLAN.md
T
kawaandClaude Sonnet 4.6 b314e65536 docs(06-new-backends): create phase plan
4 plans across 3 waves: Wave 0 TDD stubs, Wave 1 data layer (parallel: OneDrive/GCS/B2 + SFTP/SftpAuthToggle), Wave 2 RemoteConfigStep wiring.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 13:38:46 +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
06-new-backends 03 execute 2
06-01
06-02
src/components/wizard/RemoteConfigStep.tsx
false
BACK-01
BACK-02
BACK-03
BACK-04
truths artifacts key_links
User can select OneDrive in BackendSelectionStep and see token, drive_id, drive_type fields in RemoteConfigStep
User can select SFTP and see host, user, and the Password/Private Key toggle in RemoteConfigStep
User can select GCS and see project_number and service_account_credentials fields
User can select Backblaze B2 and see Application Key ID and Application Key fields
All four new backends appear in BackendSelectionStep (automatic — driven by BACKEND_REGISTRY)
Full Vitest suite passes with zero failures and zero TypeScript errors
path provides contains
src/components/wizard/RemoteConfigStep.tsx Extended component handling all 7 backends; sftp branch with SftpAuthToggle SftpAuthToggle
from to via pattern
src/components/wizard/RemoteConfigStep.tsx src/components/wizard/SftpAuthToggle.tsx sftp branch import and render backendType === 'sftp'
from to via pattern
src/components/wizard/RemoteConfigStep.tsx src/schemas/index.ts BACKEND_SCHEMAS schema lookup — all 7 BackendType values must be keys in BACKEND_SCHEMAS BACKEND_SCHEMAS[backendType]
from to via pattern
src/components/wizard/RemoteConfigStep.tsx src/schemas/registry.ts BACKEND_REGISTRY backendLabel record must be exhaustive over all 7 BackendType values backendLabel.*onedrive|sftp|gcs|b2
Wire all four new backends into RemoteConfigStep — extend backendLabel, add the sftp branch with SftpAuthToggle, and ensure gcs/b2/onedrive render via the existing registry loop.

Purpose: The only UI wiring step. After Plans 01 and 02, the data layer is complete. This plan makes it visible and interactive in the wizard. Output: RemoteConfigStep.tsx updated; all RemoteConfigStep tests green; full suite 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/ROADMAP.md @.planning/STATE.md @.planning/phases/06-new-backends/06-RESEARCH.md @.planning/phases/06-new-backends/06-01-SUMMARY.md @.planning/phases/06-new-backends/06-02-SUMMARY.md

From src/components/wizard/RemoteConfigStep.tsx (current — pre-modification):

import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useWizard } from '../../store/context';
import { BACKEND_REGISTRY } from '../../schemas/registry';
import { BACKEND_SCHEMAS } from '../../schemas';
import { FieldRenderer } from '../ui/FieldRenderer';
import { AzureAuthToggle } from './AzureAuthToggle';
import type { FieldError } from 'react-hook-form';

export function RemoteConfigStep() {
  const { state, dispatch } = useWizard();
  const backendType = state.remote.backendType;

  useEffect(() => {
    if (!backendType) dispatch({ type: 'SET_STEP', payload: 0 });
  }, [backendType, dispatch]);

  const schema = backendType ? BACKEND_SCHEMAS[backendType] : BACKEND_SCHEMAS['azureblob'];
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema),
    mode: 'onSubmit',
    reValidateMode: 'onChange',
    defaultValues: state.remote.params,
  });

  if (!backendType) return null;

  const onNext = (values: Record<string, string>) => {
    dispatch({ type: 'SET_REMOTE_PARAMS', payload: values });
    dispatch({ type: 'SET_STEP', payload: 2 });
  };

  const backendLabel: Record<NonNullable<typeof backendType>, string> = {
    azureblob: 'Azure Blob Storage',
    s3: 'Amazon S3',
    's3-compatible': 'S3-Compatible Storage',
  };

  return (
    <div>
      <h2>Step 2: Configure {backendLabel[backendType]}</h2>
      <form onSubmit={handleSubmit(onNext)}>
        {backendType === 'azureblob' ? (
          <>
            <FieldRenderer field={BACKEND_REGISTRY.azureblob.fields.find(f => f.key === 'account')!} register={register} error={errors.account as FieldError | undefined} />
            <AzureAuthToggle register={register} errors={{ key: errors.key as FieldError | undefined, sas_url: errors.sas_url as FieldError | undefined }} />
          </>
        ) : (
          BACKEND_REGISTRY[backendType].fields.map(field => (
            <FieldRenderer key={field.key} field={field} register={register} error={errors[field.key] as FieldError | undefined} />
          ))
        )}
        <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>
  );
}

From src/components/wizard/SftpAuthToggle.tsx (created by Plan 02):

export function SftpAuthToggle({ register, errors }: SftpAuthToggleProps)
// Props: { register: UseFormRegister<any>; errors: { pass?: FieldError; key_pem?: FieldError } }
Task 1: Extend RemoteConfigStep.tsx — backendLabel + sftp branch + all four new backends src/components/wizard/RemoteConfigStep.tsx - Import SftpAuthToggle from './SftpAuthToggle' - backendLabel record covers all 7 BackendType values: adds onedrive/'OneDrive', sftp/'SFTP', gcs/'Google Cloud Storage', b2/'Backblaze B2' - The ternary in JSX becomes a three-branch chain: 1. backendType === 'azureblob' → existing AzureAuthToggle branch (unchanged) 2. backendType === 'sftp' → host field + user field via FieldRenderer, then SftpAuthToggle for pass/key_pem 3. all others (onedrive, gcs, b2, s3, s3-compatible) → registry loop (unchanged) - SFTP branch renders host and user fields from BACKEND_REGISTRY.sftp.fields using FieldRenderer (same as azureblob renders account), then SftpAuthToggle - onedrive, gcs, b2 render entirely via the registry loop — no custom branch needed - `npx vitest run src/components/wizard/RemoteConfigStep.test.tsx` fully green for all 7 backends - `npx vitest run` (full suite) fully green - `npx tsc --noEmit` zero errors Edit src/components/wizard/RemoteConfigStep.tsx:
1. Add import at top (after AzureAuthToggle import):
   `import { SftpAuthToggle } from './SftpAuthToggle';`

2. Extend backendLabel record:
```typescript
const backendLabel: Record<NonNullable<typeof backendType>, string> = {
  azureblob:       'Azure Blob Storage',
  s3:              'Amazon S3',
  's3-compatible': 'S3-Compatible Storage',
  onedrive:        'OneDrive',
  sftp:            'SFTP',
  gcs:             'Google Cloud Storage',
  b2:              'Backblaze B2',
};
```

3. Replace the ternary block in JSX (inside the form, before the button div):
```tsx
{backendType === 'azureblob' ? (
  <>
    {/* Account field via FieldRenderer */}
    <FieldRenderer
      field={BACKEND_REGISTRY.azureblob.fields.find(f => f.key === 'account')!}
      register={register}
      error={errors.account as FieldError | undefined}
    />
    {/* Auth toggle handles key + sas_url — both always registered */}
    <AzureAuthToggle
      register={register}
      errors={{
        key: errors.key as FieldError | undefined,
        sas_url: errors.sas_url as FieldError | undefined,
      }}
    />
  </>
) : backendType === 'sftp' ? (
  <>
    {/* host and user via FieldRenderer */}
    <FieldRenderer
      field={BACKEND_REGISTRY.sftp.fields.find(f => f.key === 'host')!}
      register={register}
      error={errors.host as FieldError | undefined}
    />
    <FieldRenderer
      field={BACKEND_REGISTRY.sftp.fields.find(f => f.key === 'user')!}
      register={register}
      error={errors.user as FieldError | undefined}
    />
    {/* Auth toggle handles pass + key_pem — both always registered */}
    <SftpAuthToggle
      register={register}
      errors={{
        pass: errors.pass as FieldError | undefined,
        key_pem: errors.key_pem as FieldError | undefined,
      }}
    />
  </>
) : (
  /* All others (onedrive, gcs, b2, s3, s3-compatible): full registry loop */
  BACKEND_REGISTRY[backendType].fields.map(field => (
    <FieldRenderer
      key={field.key}
      field={field}
      register={register}
      error={errors[field.key] as FieldError | undefined}
    />
  ))
)}
```

After editing, run the full suite immediately. Confirm all 7 backend describe blocks in RemoteConfigStep.test.tsx are green.

Note on OneDrive drive_type field: BACKEND_REGISTRY.onedrive has drive_type with inputType: 'select'. FieldRenderer already handles 'select' type — no changes to FieldRenderer needed. The test asserts getByLabelText(/drive type/i) — ensure FieldRenderer renders a label matching that text.
npx vitest run 2>&1 | tail -20 RemoteConfigStep handles all 7 backends; SFTP branch uses SftpAuthToggle; onedrive/gcs/b2 render via registry loop; full Vitest suite green; zero TypeScript errors from `npx tsc --noEmit` Task 2: Human verify all four new backends in the wizard UI src/components/wizard/RemoteConfigStep.tsx Run `npm run dev` and manually test each new backend through the wizard UI flow. npx vitest run 2>&1 | tail -5 All four backends visually verified in the running app by the user All four new backends fully wired into the wizard: - OneDrive: token (password field), Drive ID, Drive Type (select: Personal/Business/SharePoint) - SFTP: Host, Username, Password/Private Key toggle (CSS-hidden, preserves values on switch) - Google Cloud Storage: Project Number, Service Account JSON (password field) - Backblaze B2: Application Key ID, Application Key (password field) All four backends appear as cards in BackendSelectionStep. Automated tests are fully green. 1. `npm run dev` — open http://localhost:5173 2. Verify BackendSelectionStep shows 7 backend cards including OneDrive, SFTP, Google Cloud Storage, Backblaze B2 3. Select OneDrive → confirm OAuth Token (JSON), Drive ID, Drive Type fields appear 4. Go back, select SFTP → confirm Host, Username fields + Password/Private Key tab buttons - Type a value in Password field, switch to Private Key, switch back — value must still be there 5. Go back, select GCS → confirm Project Number and Service Account JSON fields appear 6. Go back, select B2 → confirm Application Key ID and Application Key fields appear 7. For any backend: fill all required fields, click Next — wizard must advance without errors Type "approved" to complete Phase 6, or describe any visual issues found `npx vitest run` — all tests green including all 4 new backend describe blocks in RemoteConfigStep.test.tsx `npx tsc --noEmit` — zero TypeScript errors Manual: all 4 new backends visible and functional in the wizard UI

<success_criteria>

  • RemoteConfigStep handles all 7 BackendType values without TypeScript errors
  • SFTP branch uses SftpAuthToggle with CSS-hidden pattern
  • onedrive, gcs, b2 render entirely via the registry loop
  • All 4 new backends visible in BackendSelectionStep UI (automatic via registry)
  • Full Vitest suite green
  • Zero TypeScript errors
  • Human verification: all backends visually correct and functional </success_criteria>
After completion, create `.planning/phases/06-new-backends/06-03-SUMMARY.md`