mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 04:40:09 +00:00
af0eaceb98
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.
102 lines
2.8 KiB
TypeScript
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>
|
|
);
|
|
}
|