import { fetchBooks, searchBooks, fetchLibraries, BookDto, LibraryDto, SeriesHitDto, getBookCoverUrl } from "../../lib/api"; import { BooksGrid, EmptyState } from "../components/BookCard"; import { LiveSearchForm } from "../components/LiveSearchForm"; import { Card, CardContent, OffsetPagination } from "../components/ui"; import Link from "next/link"; import Image from "next/image"; import { getServerTranslations } from "../../lib/i18n/server"; export const dynamic = "force-dynamic"; export default async function BooksPage({ searchParams }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }>; }) { const { t } = await getServerTranslations(); const searchParamsAwaited = await searchParams; const libraryId = typeof searchParamsAwaited.library === "string" ? searchParamsAwaited.library : undefined; const searchQuery = typeof searchParamsAwaited.q === "string" ? searchParamsAwaited.q : ""; const readingStatus = typeof searchParamsAwaited.status === "string" ? searchParamsAwaited.status : undefined; const format = typeof searchParamsAwaited.format === "string" ? searchParamsAwaited.format : undefined; const metadataProvider = typeof searchParamsAwaited.metadata === "string" ? searchParamsAwaited.metadata : undefined; const sort = typeof searchParamsAwaited.sort === "string" ? searchParamsAwaited.sort : undefined; const page = typeof searchParamsAwaited.page === "string" ? parseInt(searchParamsAwaited.page) : 1; const limit = typeof searchParamsAwaited.limit === "string" ? parseInt(searchParamsAwaited.limit) : 20; const [libraries] = await Promise.all([ fetchLibraries().catch(() => [] as LibraryDto[]) ]); let books: BookDto[] = []; let total = 0; let searchResults: BookDto[] | null = null; let seriesHits: SeriesHitDto[] = []; let totalHits: number | null = null; if (searchQuery) { const searchResponse = await searchBooks(searchQuery, libraryId, limit).catch(() => null); if (searchResponse) { seriesHits = searchResponse.series_hits ?? []; searchResults = searchResponse.hits.map(hit => ({ id: hit.id, library_id: hit.library_id, kind: hit.kind, title: hit.title, author: hit.authors?.[0] ?? null, authors: hit.authors ?? [], series: hit.series, volume: hit.volume, language: hit.language, page_count: null, format: null, file_path: null, file_format: null, file_parse_status: null, updated_at: "", reading_status: "unread" as const, reading_current_page: null, reading_last_read_at: null, summary: null, isbn: null, publish_date: null, })); totalHits = searchResponse.estimated_total_hits; } } else { const booksPage = await fetchBooks(libraryId, undefined, page, limit, readingStatus, sort, undefined, format, metadataProvider).catch(() => ({ items: [] as BookDto[], total: 0, page: 1, limit, })); books = booksPage.items; total = booksPage.total; } const displayBooks = (searchResults || books).map(book => ({ ...book, coverUrl: getBookCoverUrl(book.id) })); const totalPages = Math.ceil(total / limit); const libraryOptions = [ { value: "", label: t("books.allLibraries") }, ...libraries.map((lib) => ({ value: lib.id, label: lib.name })), ]; const statusOptions = [ { value: "", label: t("common.all") }, { value: "unread", label: t("status.unread") }, { value: "reading", label: t("status.reading") }, { value: "read", label: t("status.read") }, ]; const formatOptions = [ { value: "", label: t("books.allFormats") }, { value: "cbz", label: "CBZ" }, { value: "cbr", label: "CBR" }, { value: "pdf", label: "PDF" }, { value: "epub", label: "EPUB" }, ]; const metadataOptions = [ { value: "", label: t("series.metadataAll") }, { value: "linked", label: t("series.metadataLinked") }, { value: "unlinked", label: t("series.metadataUnlinked") }, ]; const sortOptions = [ { value: "", label: t("books.sortTitle") }, { value: "latest", label: t("books.sortLatest") }, ]; const hasFilters = searchQuery || libraryId || readingStatus || format || metadataProvider || sort; return ( <>

{t("books.title")}

{/* Résultats */} {searchQuery && totalHits !== null ? (

{t("books.resultCountFor", { count: String(totalHits), plural: totalHits !== 1 ? "s" : "", query: searchQuery })}

) : !searchQuery && (

{t("books.resultCount", { count: String(total), plural: total !== 1 ? "s" : "" })}

)} {/* Séries matchantes */} {seriesHits.length > 0 && (

{t("books.seriesHeading")}

{seriesHits.map((s) => (
{t("books.coverOf",

{s.name === "unclassified" ? t("books.unclassified") : s.name}

{t("books.bookCount", { count: String(s.book_count), plural: s.book_count !== 1 ? "s" : "" })}

))}
)} {/* Grille de livres */} {displayBooks.length > 0 ? ( <> {searchQuery &&

{t("books.title")}

} {!searchQuery && ( )} ) : ( )} ); }