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>
12 KiB
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 |
|
|
false |
|
|
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.mdFrom 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 } }
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.
<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>