feat(13-04): wire all new backends into RemoteConfigStep
- Replace manual backendLabel record with registry-derived Object.fromEntries - Import OAuthInstructions and GdriveAuthToggle components - Refactor 3-branch ternary into renderBackendFields() switch function - Add gdrive branch: GdriveAuthToggle + generic loop for non-auth fields - Add onedrive branch: OAuthInstructions + generic loop - Add dropbox/box/pcloud branches: OAuthInstructions + generic loop - Default branch covers all other backends (azure-files, swift, ftp, webdav, smb, http, seafile)
This commit is contained in:
@@ -6,13 +6,185 @@ import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useWizard } from '../../store/context';
|
||||
import { BACKEND_REGISTRY } from '../../schemas/registry';
|
||||
import type { BackendType } from '../../schemas/registry';
|
||||
import { BACKEND_SCHEMAS } from '../../schemas';
|
||||
import { FieldRenderer } from '../ui/FieldRenderer';
|
||||
import { AzureAuthToggle } from './AzureAuthToggle';
|
||||
import { SftpAuthToggle } from './SftpAuthToggle';
|
||||
import { OAuthInstructions } from './OAuthInstructions';
|
||||
import { GdriveAuthToggle } from './GdriveAuthToggle';
|
||||
import type { FieldError, FieldErrors } from 'react-hook-form';
|
||||
import { MD3_BTN_FILLED, MD3_BTN_OUTLINED } from '../../styles/md3-buttons';
|
||||
|
||||
// backendLabel derived from registry — no manual duplication
|
||||
const backendLabel = Object.fromEntries(
|
||||
Object.entries(BACKEND_REGISTRY).map(([k, v]) => [k, v.displayName])
|
||||
) as Record<BackendType, string>;
|
||||
|
||||
function renderBackendFields(
|
||||
backendType: BackendType,
|
||||
register: ReturnType<typeof useForm>['register'],
|
||||
errors: Record<string, unknown>
|
||||
) {
|
||||
switch (backendType) {
|
||||
case 'azureblob':
|
||||
return (
|
||||
<>
|
||||
{/* 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,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
case 'sftp':
|
||||
return (
|
||||
<>
|
||||
{/* 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,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
case 'gdrive':
|
||||
return (
|
||||
<>
|
||||
<GdriveAuthToggle
|
||||
register={register}
|
||||
errors={{
|
||||
token: errors.token as FieldError | undefined,
|
||||
service_account_credentials: errors.service_account_credentials as FieldError | undefined,
|
||||
}}
|
||||
/>
|
||||
{BACKEND_REGISTRY.gdrive.fields
|
||||
.filter(f => f.key !== 'token' && f.key !== 'service_account_credentials')
|
||||
.map(field => (
|
||||
<FieldRenderer
|
||||
key={field.key}
|
||||
field={field}
|
||||
register={register}
|
||||
error={errors[field.key] as FieldError | undefined}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
case 'onedrive':
|
||||
return (
|
||||
<>
|
||||
<OAuthInstructions
|
||||
backendName="OneDrive"
|
||||
authorizeCommand='rclone authorize "onedrive"'
|
||||
/>
|
||||
{BACKEND_REGISTRY.onedrive.fields.map(field => (
|
||||
<FieldRenderer
|
||||
key={field.key}
|
||||
field={field}
|
||||
register={register}
|
||||
error={errors[field.key] as FieldError | undefined}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
case 'dropbox':
|
||||
return (
|
||||
<>
|
||||
<OAuthInstructions
|
||||
backendName="Dropbox"
|
||||
authorizeCommand='rclone authorize "dropbox"'
|
||||
/>
|
||||
{BACKEND_REGISTRY.dropbox.fields.map(field => (
|
||||
<FieldRenderer
|
||||
key={field.key}
|
||||
field={field}
|
||||
register={register}
|
||||
error={errors[field.key] as FieldError | undefined}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
case 'box':
|
||||
return (
|
||||
<>
|
||||
<OAuthInstructions
|
||||
backendName="Box"
|
||||
authorizeCommand='rclone authorize "box"'
|
||||
/>
|
||||
{BACKEND_REGISTRY.box.fields.map(field => (
|
||||
<FieldRenderer
|
||||
key={field.key}
|
||||
field={field}
|
||||
register={register}
|
||||
error={errors[field.key] as FieldError | undefined}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
case 'pcloud':
|
||||
return (
|
||||
<>
|
||||
<OAuthInstructions
|
||||
backendName="pCloud"
|
||||
authorizeCommand='rclone authorize "pcloud"'
|
||||
/>
|
||||
{BACKEND_REGISTRY.pcloud.fields.map(field => (
|
||||
<FieldRenderer
|
||||
key={field.key}
|
||||
field={field}
|
||||
register={register}
|
||||
error={errors[field.key] as FieldError | undefined}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
default:
|
||||
// All other backends (azure-files, swift, ftp, webdav, smb, http, seafile, s3, s3-compatible, gcs, b2):
|
||||
// generic FieldRenderer loop driven by registry
|
||||
return (
|
||||
<>
|
||||
{BACKEND_REGISTRY[backendType].fields.map(field => (
|
||||
<FieldRenderer
|
||||
key={field.key}
|
||||
field={field}
|
||||
register={register}
|
||||
error={errors[field.key] as FieldError | undefined}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function RemoteConfigStep() {
|
||||
const { state, dispatch } = useWizard();
|
||||
const backendType = state.remote.backendType;
|
||||
@@ -50,16 +222,6 @@ export function RemoteConfigStep() {
|
||||
}
|
||||
}
|
||||
|
||||
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',
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-on-surface mb-2">Step 2: Configure {backendLabel[backendType]}</h2>
|
||||
@@ -68,56 +230,7 @@ export function RemoteConfigStep() {
|
||||
generated rclone.conf and are never sent to any server.
|
||||
</p>
|
||||
<form onSubmit={handleSubmit(onNext, onInvalidSubmit)}>
|
||||
{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}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
{renderBackendFields(backendType, register, errors as Record<string, unknown>)}
|
||||
<div className="flex flex-col sm:flex-row gap-3 mt-6">
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user