mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-16 11:10:09 +00:00
2cda6decbe
Implements support for configuring both qBittorrent and SABnzbd simultaneously, including migration from legacy config, protocol-aware routing, and protocol filtering. Adds new CRUD API routes for download clients, new UI management components, and updates setup and settings flows to use the new multi-client architecture. Updates documentation to describe the new structure and usage.
286 lines
11 KiB
TypeScript
286 lines
11 KiB
TypeScript
/**
|
|
* Component: Setup Wizard Review Step
|
|
* Documentation: documentation/setup-wizard.md
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import { Button } from '@/components/ui/Button';
|
|
|
|
interface ReviewStepProps {
|
|
config: {
|
|
backendMode: 'plex' | 'audiobookshelf';
|
|
|
|
// Plex config
|
|
plexUrl: string;
|
|
plexLibraryId: string;
|
|
|
|
// Audiobookshelf config
|
|
absUrl: string;
|
|
absLibraryId: string;
|
|
|
|
// Auth config (ABS mode)
|
|
authMethod: 'oidc' | 'manual' | 'both';
|
|
oidcProviderName: string;
|
|
adminUsername: string;
|
|
|
|
// Common config
|
|
prowlarrUrl: string;
|
|
downloadClients: any[]; // Array of download client configs
|
|
downloadDir: string;
|
|
mediaDir: string;
|
|
|
|
// BookDate
|
|
bookdateConfigured: boolean;
|
|
bookdateProvider: string;
|
|
bookdateModel: string;
|
|
};
|
|
loading: boolean;
|
|
error: string | null;
|
|
onComplete: () => void;
|
|
onBack: () => void;
|
|
}
|
|
|
|
export function ReviewStep({ config, loading, error, onComplete, onBack }: ReviewStepProps) {
|
|
return (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<h2 className="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
|
Review Configuration
|
|
</h2>
|
|
<p className="text-gray-600 dark:text-gray-400 mt-2">
|
|
Please review your configuration before completing setup.
|
|
</p>
|
|
</div>
|
|
|
|
{error && (
|
|
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4">
|
|
<div className="flex gap-3">
|
|
<svg
|
|
className="w-6 h-6 text-red-600 dark:text-red-400 flex-shrink-0"
|
|
fill="currentColor"
|
|
viewBox="0 0 20 20"
|
|
>
|
|
<path
|
|
fillRule="evenodd"
|
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
|
clipRule="evenodd"
|
|
/>
|
|
</svg>
|
|
<div>
|
|
<h3 className="text-sm font-medium text-red-800 dark:text-red-200">Error</h3>
|
|
<p className="text-sm text-red-700 dark:text-red-300 mt-1">{error}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="space-y-4">
|
|
{/* Backend Configuration - Conditional based on mode */}
|
|
{config.backendMode === 'plex' ? (
|
|
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
|
|
Plex Media Server
|
|
</h3>
|
|
<dl className="space-y-2">
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Server URL:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{config.plexUrl}
|
|
</dd>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Library ID:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{config.plexLibraryId}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
) : (
|
|
<>
|
|
{/* Audiobookshelf Configuration */}
|
|
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
|
|
Audiobookshelf
|
|
</h3>
|
|
<dl className="space-y-2">
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Server URL:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{config.absUrl}
|
|
</dd>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Library ID:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{config.absLibraryId}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
|
|
{/* Authentication Configuration */}
|
|
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
|
|
Authentication
|
|
</h3>
|
|
<dl className="space-y-2">
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Auth Method:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100 capitalize">
|
|
{config.authMethod === 'both' ? 'OIDC + Manual Registration' : config.authMethod === 'oidc' ? 'OIDC' : 'Manual Registration'}
|
|
</dd>
|
|
</div>
|
|
{(config.authMethod === 'oidc' || config.authMethod === 'both') && (
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">OIDC Provider:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{config.oidcProviderName}
|
|
</dd>
|
|
</div>
|
|
)}
|
|
{(config.authMethod === 'manual' || config.authMethod === 'both') && (
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Admin Username:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{config.adminUsername}
|
|
</dd>
|
|
</div>
|
|
)}
|
|
</dl>
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
{/* Prowlarr Configuration */}
|
|
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
|
|
Prowlarr (Indexer)
|
|
</h3>
|
|
<dl className="space-y-2">
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Server URL:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{config.prowlarrUrl}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
|
|
{/* Download Client Configuration */}
|
|
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
|
|
Download Clients
|
|
</h3>
|
|
<dl className="space-y-3">
|
|
{config.downloadClients && config.downloadClients.length > 0 ? (
|
|
config.downloadClients.map((client: any, index: number) => (
|
|
<div key={index} className={index > 0 ? 'pt-3 border-t border-gray-200 dark:border-gray-700' : ''}>
|
|
<div className="flex justify-between mb-1">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Name:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{client.name}
|
|
</dd>
|
|
</div>
|
|
<div className="flex justify-between mb-1">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Type:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100 capitalize">
|
|
{client.type}
|
|
</dd>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">URL:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{client.url}
|
|
</dd>
|
|
</div>
|
|
</div>
|
|
))
|
|
) : (
|
|
<div className="text-sm text-gray-500 dark:text-gray-400">No download clients configured</div>
|
|
)}
|
|
</dl>
|
|
</div>
|
|
|
|
{/* Paths Configuration */}
|
|
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
|
|
Directory Paths
|
|
</h3>
|
|
<dl className="space-y-2">
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Download Directory:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100 font-mono">
|
|
{config.downloadDir}
|
|
</dd>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Media Directory:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100 font-mono">
|
|
{config.mediaDir}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
|
|
{/* BookDate Configuration (Optional) */}
|
|
{config.bookdateConfigured && (
|
|
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
|
|
BookDate AI Recommendations
|
|
</h3>
|
|
<dl className="space-y-2">
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Provider:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100 capitalize">
|
|
{config.bookdateProvider}
|
|
</dd>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<dt className="text-sm text-gray-600 dark:text-gray-400">Model:</dt>
|
|
<dd className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{config.bookdateModel}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4">
|
|
<div className="flex gap-3">
|
|
<svg
|
|
className="w-6 h-6 text-blue-600 dark:text-blue-400 flex-shrink-0"
|
|
fill="currentColor"
|
|
viewBox="0 0 20 20"
|
|
>
|
|
<path
|
|
fillRule="evenodd"
|
|
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
|
clipRule="evenodd"
|
|
/>
|
|
</svg>
|
|
<div>
|
|
<p className="text-sm font-medium text-blue-900 dark:text-blue-100">
|
|
Ready to complete setup
|
|
</p>
|
|
<p className="text-sm text-blue-700 dark:text-blue-300 mt-1">
|
|
Click "Complete Setup" to save your configuration and start using ReadMeABook.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex justify-between pt-4">
|
|
<Button onClick={onBack} variant="outline" disabled={loading}>
|
|
Back
|
|
</Button>
|
|
<Button onClick={onComplete} loading={loading} size="lg">
|
|
Complete Setup
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|