Files
ReadMeABook/src/app/setup/steps/DownloadClientStep.tsx
T
kikootwo af0eaceb98 Add extensible notification providers + UI/API
Introduce a provider-based notification system and wire it through the API and admin UI. Added INotificationProvider + notification service implementation and providers (apprise, discord, ntfy, pushover), plus a GET /api/admin/notifications/providers endpoint to expose provider metadata. Refactored code to use provider type strings (removed enum coupling), updated masking/encryption calls, and simplified the test notification endpoint to accept backendId or type+config and call sendToBackend directly.

UI: NotificationsTab now fetches provider metadata and renders provider cards and dynamic config forms (fields driven by provider metadata). Added config field rendering, improved backend cards, and edit/delete actions.

APIs: New providers route, updated admin notification CRUD routes to validate provider types dynamically, updated test route schema. Added download-client categories POST API to fetch categories from clients and wired postImportCategory handling in download-client routes.

Other notable changes: BookDate now fetches Claude models dynamically from Anthropic's Models API; added paginated model fetch helper. Added ALLOW_WEAK_PASSWORD flag exposure to auth providers and password change logic. Doc updates and various tests added/updated. File-organization doc clarifies EPERM fix using stream-based copy.
2026-02-10 15:06:20 -05:00

102 lines
2.8 KiB
TypeScript

/**
* Component: Setup Wizard Download Client Step
* Documentation: documentation/setup-wizard.md
*/
'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/Button';
import { DownloadClientManagement } from '@/components/admin/download-clients/DownloadClientManagement';
import { DownloadClientType } from '@/lib/interfaces/download-client.interface';
interface DownloadClient {
id: string;
type: DownloadClientType;
name: string;
enabled: boolean;
url: string;
username?: string;
password: string;
disableSSLVerify: boolean;
remotePathMappingEnabled: boolean;
remotePath?: string;
localPath?: string;
category?: string;
customPath?: string;
postImportCategory?: string;
}
interface DownloadClientStepProps {
downloadClients: DownloadClient[];
downloadDir?: string;
onUpdate: (field: string, value: any) => void;
onNext: () => void;
onBack: () => void;
}
export function DownloadClientStep({
downloadClients,
downloadDir,
onUpdate,
onNext,
onBack,
}: DownloadClientStepProps) {
const [clients, setClients] = useState<DownloadClient[]>(downloadClients || []);
const [error, setError] = useState<string | null>(null);
// Update parent when clients change
const handleClientsChange = (updatedClients: DownloadClient[]) => {
setClients(updatedClients);
onUpdate('downloadClients', updatedClients);
};
const handleNext = () => {
// Validate: At least one enabled client required
const hasEnabledClient = clients.some(c => c.enabled);
if (!hasEnabledClient) {
setError('Please add at least one download client before proceeding');
return;
}
setError(null);
onNext();
};
return (
<div className="space-y-6">
<div>
<h2 className="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-2">
Configure Download Clients
</h2>
<p className="text-gray-600 dark:text-gray-400">
Add at least one download client. You can configure a torrent client (qBittorrent or Transmission) and/or a usenet client (SABnzbd or NZBGet) to search across all indexer types.
</p>
</div>
{error && (
<div className="p-4 bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-300 rounded-lg">
<p className="text-sm">{error}</p>
</div>
)}
<DownloadClientManagement
mode="wizard"
initialClients={clients}
onClientsChange={handleClientsChange}
downloadDir={downloadDir}
/>
<div className="flex justify-between pt-6 border-t border-gray-200 dark:border-gray-700">
<Button onClick={onBack} variant="secondary">
Back
</Button>
<Button onClick={handleNext} variant="primary">
Next
</Button>
</div>
</div>
);
}