--- phase: 06-new-backends plan: "03" type: execute wave: 2 depends_on: - "06-01" - "06-02" files_modified: - src/components/wizard/RemoteConfigStep.tsx autonomous: false requirements: - BACK-01 - BACK-02 - BACK-03 - BACK-04 must_haves: truths: - "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" artifacts: - path: "src/components/wizard/RemoteConfigStep.tsx" provides: "Extended component handling all 7 backends; sftp branch with SftpAuthToggle" contains: "SftpAuthToggle" key_links: - from: "src/components/wizard/RemoteConfigStep.tsx" to: "src/components/wizard/SftpAuthToggle.tsx" via: "sftp branch import and render" pattern: "backendType === 'sftp'" - from: "src/components/wizard/RemoteConfigStep.tsx" to: "src/schemas/index.ts BACKEND_SCHEMAS" via: "schema lookup — all 7 BackendType values must be keys in BACKEND_SCHEMAS" pattern: "BACKEND_SCHEMAS\\[backendType\\]" - from: "src/components/wizard/RemoteConfigStep.tsx" to: "src/schemas/registry.ts BACKEND_REGISTRY" via: "backendLabel record must be exhaustive over all 7 BackendType values" pattern: "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. @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/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): ```typescript 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) => { dispatch({ type: 'SET_REMOTE_PARAMS', payload: values }); dispatch({ type: 'SET_STEP', payload: 2 }); }; const backendLabel: Record, string> = { azureblob: 'Azure Blob Storage', s3: 'Amazon S3', 's3-compatible': 'S3-Compatible Storage', }; return (

Step 2: Configure {backendLabel[backendType]}

{backendType === 'azureblob' ? ( <> f.key === 'account')!} register={register} error={errors.account as FieldError | undefined} /> ) : ( BACKEND_REGISTRY[backendType].fields.map(field => ( )) )}
); } ``` From src/components/wizard/SftpAuthToggle.tsx (created by Plan 02): ```typescript export function SftpAuthToggle({ register, errors }: SftpAuthToggleProps) // Props: { register: UseFormRegister; 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, 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 */} f.key === 'account')!} register={register} error={errors.account as FieldError | undefined} /> {/* Auth toggle handles key + sas_url — both always registered */} ) : backendType === 'sftp' ? ( <> {/* host and user via FieldRenderer */} f.key === 'host')!} register={register} error={errors.host as FieldError | undefined} /> f.key === 'user')!} register={register} error={errors.user as FieldError | undefined} /> {/* Auth toggle handles pass + key_pem — both always registered */} ) : ( /* All others (onedrive, gcs, b2, s3, s3-compatible): full registry loop */ BACKEND_REGISTRY[backendType].fields.map(field => ( )) )} ``` 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 - 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 After completion, create `.planning/phases/06-new-backends/06-03-SUMMARY.md`