- Table series + colonnes seriesId/volumeNumber/volumeLabel sur les livres, extraction souple des numéros (T03, 003, etc.) via extract-series-volume - Contrôleur et page de série, badge volume sur les cartes de livre, styles associés (fond/clarté série, ajustements globaux) - Statuts de scan/enrichissement et provenance des métadonnées en base Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import type { BookDto, JobDto, LibraryDto } from "@readabook/shared";
|
|
import { api } from "../api/client";
|
|
import { hasActiveCoverWork, isBookCoverUpdating } from "../api/types";
|
|
import { BookCard } from "../components/BookCard";
|
|
import { EmptyState, LoadingState, Panel } from "../components/ui";
|
|
|
|
export function LibraryPage({ libraryId }: { libraryId: number }) {
|
|
const [books, setBooks] = useState<BookDto[] | null>(null);
|
|
const [libraries, setLibraries] = useState<LibraryDto[]>([]);
|
|
const [jobs, setJobs] = useState<JobDto[]>([]);
|
|
|
|
useEffect(() => {
|
|
let alive = true;
|
|
Promise.all([api.books({ libraryId }), api.libraries(), api.jobs().catch(() => [])]).then(([nextBooks, nextLibraries, nextJobs]) => {
|
|
if (!alive) return;
|
|
setBooks(nextBooks);
|
|
setLibraries(nextLibraries);
|
|
setJobs(nextJobs);
|
|
});
|
|
return () => {
|
|
alive = false;
|
|
};
|
|
}, [libraryId]);
|
|
|
|
if (!books) return <LoadingState />;
|
|
const library = libraries.find((item) => item.id === libraryId);
|
|
const fallbackCoverLoading = hasActiveCoverWork(jobs);
|
|
|
|
return (
|
|
<div className="page-grid">
|
|
<Panel className="span-3">
|
|
<div className="section-heading">
|
|
<div>
|
|
<h1>{library?.name ?? "Bibliotheque"}</h1>
|
|
<p>{library?.path ?? "Rayonnage non identifie"}</p>
|
|
</div>
|
|
<span>{books.length} ouvrages</span>
|
|
</div>
|
|
</Panel>
|
|
{books.length ? (
|
|
<section className="book-grid span-3">
|
|
{books.map((book) => (
|
|
<BookCard key={book.id} book={book} coverLoading={isBookCoverUpdating(book, fallbackCoverLoading)} />
|
|
))}
|
|
</section>
|
|
) : (
|
|
<Panel className="span-3">
|
|
<EmptyState title="Rayon vide" detail="Lance un scan depuis l'administration." />
|
|
</Panel>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|