import { FormEvent, useEffect, useState } from "react"; import { Play, Plus, Trash2 } from "lucide-react"; import type { JobDto, LibraryDto, UserDto } from "@readabook/shared"; import { api, getApiFallback } from "../api/client"; import { jobDigestSummary } from "../book/metadata"; import { EmptyState, ErrorRibbon, LoadingState, Panel } from "../components/ui"; function formatJobTime(value: string) { return new Intl.DateTimeFormat(undefined, { hour: "2-digit", minute: "2-digit" }).format(new Date(value)); } export function AdminPage() { const [libraries, setLibraries] = useState([]); const [jobs, setJobs] = useState([]); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [name, setName] = useState("Bibliotheque locale"); const [path, setPath] = useState("/library"); const [error, setError] = useState(); const [success, setSuccess] = useState(); const [scanRetryLibrary, setScanRetryLibrary] = useState(); async function refresh() { setLoading(true); setError(undefined); const [libraryResult, jobResult, userResult] = await Promise.allSettled([api.libraries(), api.jobs(), api.users()]); const errors: string[] = []; if (libraryResult.status === "fulfilled") { setLibraries(libraryResult.value); } else { setLibraries(getApiFallback(libraryResult.reason) ?? []); errors.push("bibliotheques"); } if (jobResult.status === "fulfilled") { setJobs(jobResult.value); } else { setJobs(getApiFallback(jobResult.reason) ?? []); errors.push("travaux"); } if (userResult.status === "fulfilled") { setUsers(userResult.value); } else { setUsers(getApiFallback(userResult.reason) ?? []); errors.push("comptes"); } setError(errors.length ? `Donnees admin degradees : ${errors.join(", ")}.` : undefined); setLoading(false); } useEffect(() => { void refresh(); }, []); async function createLibrary(event: FormEvent) { event.preventDefault(); setError(undefined); setSuccess(undefined); setScanRetryLibrary(undefined); try { const created = await api.createLibrary({ name, path, enabled: true }); await refresh(); setName("Bibliotheque locale"); setPath("/library"); try { await api.scanLibrary(created.id); await refresh(); setSuccess(`Bibliothèque "${created.name}" ajoutée. Scan initial demandé.`); } catch (scanError) { setScanRetryLibrary(created); setSuccess( `Bibliothèque "${created.name}" ajoutée, mais le scan initial n'a pas pu être demandé. Tu peux réessayer le scan.` ); setError(scanError instanceof Error ? `Scan initial impossible : ${scanError.message}` : "Scan initial impossible."); } } catch (createError) { setError(createError instanceof Error ? `Création impossible : ${createError.message}` : "Création impossible."); } } async function scan(id: number) { setError(undefined); setSuccess(undefined); setScanRetryLibrary(undefined); try { await api.scanLibrary(id); await refresh(); setSuccess("Scan demandé."); } catch (scanError) { setError(scanError instanceof Error ? `Scan impossible : ${scanError.message}` : "Scan impossible."); } } async function deleteLibrary(library: LibraryDto) { const confirmed = window.confirm( `Supprimer la bibliothèque "${library.name}" ?\n\nLes livres restent sur le disque. ReadaBook supprimera seulement cette bibliothèque du catalogue.` ); if (!confirmed) return; setError(undefined); setSuccess(undefined); setScanRetryLibrary(undefined); try { await api.deleteLibrary(library.id); setLibraries((current) => current.filter((item) => item.id !== library.id)); setSuccess(`Bibliothèque "${library.name}" supprimée. Les fichiers disque n'ont pas été supprimés.`); } catch (deleteError) { setError(deleteError instanceof Error ? deleteError.message : "Suppression impossible"); } } return (

Administration

{loading ? "chargement" : `${users.length} comptes`}
{success &&
{success}
} {scanRetryLibrary ? (
La bibliothèque est conservée dans la liste.
) : error ? (
Les formulaires restent disponibles.
) : null}

Travaux

{jobs.length}
{loading && !jobs.length ? ( ) : jobs.length ? (
{jobs.map((job) => (
{job.type} {jobDigestSummary(job)}
{job.status}
))}
) : ( )}
{loading && !libraries.length ? ( ) : libraries.length ? (
{libraries.map((library) => (
{library.name} Chemin : {library.path}
{library.enabled ? "actif" : "pause"}
))}
) : ( )}
); }