From c2cdb07f9a030e2aed2d2357b17c813411988074 Mon Sep 17 00:00:00 2001 From: Git Agent Date: Fri, 28 Aug 2026 23:29:03 +0200 Subject: [PATCH] =?UTF-8?q?feat(web):=20biblioth=C3=A8que=20et=20lecteur?= =?UTF-8?q?=20=E2=80=94=20grilles=20de=20livres=20born=C3=A9es=20=C3=A0=20?= =?UTF-8?q?5=20colonnes=20(max-width=20calc=20sur=20.book-grid=20et=20.hom?= =?UTF-8?q?e-book-grid),=20retrait=20du=20bandeau=20=C2=AB=20API=20absente?= =?UTF-8?q?=20ou=20incomplete=20=C2=BB=20de=20l'accueil,=20et=20v=C3=A9rit?= =?UTF-8?q?ables=20pages=20d'erreur=20=C2=AB=20Livre=20introuvable=20?= =?UTF-8?q?=C2=BB=20sur=20fiche=20et=20lecteur=20(suppression=20de=20getAp?= =?UTF-8?q?iFallback,=20retour=20=C3=A0=20l'accueil=20+=20r=C3=A9essayer?= =?UTF-8?q?=20=E2=80=94=20=C3=A9volution=20#46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/pages/BookErrorPages.test.ts | 23 +++++++++++ apps/web/src/pages/BookPage.tsx | 38 +++++++++++------- apps/web/src/pages/HomePage.tsx | 4 +- .../src/pages/HomePageProgressScroll.test.ts | 11 ++++++ apps/web/src/pages/ReaderPage.tsx | 39 ++++++++++++------- apps/web/src/styles/app.css | 25 ++++++++++-- 6 files changed, 106 insertions(+), 34 deletions(-) create mode 100644 apps/web/src/pages/BookErrorPages.test.ts diff --git a/apps/web/src/pages/BookErrorPages.test.ts b/apps/web/src/pages/BookErrorPages.test.ts new file mode 100644 index 0000000..5e0e65a --- /dev/null +++ b/apps/web/src/pages/BookErrorPages.test.ts @@ -0,0 +1,23 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +describe("book missing error pages", () => { + it("does not render fallback books on the book detail page", () => { + const source = readFileSync(new URL("./BookPage.tsx", import.meta.url), "utf8"); + + expect(source).not.toContain("getApiFallback"); + expect(source).toContain("setBook(null)"); + expect(source).toContain("Livre introuvable"); + expect(source).toContain("Ce livre n'existe pas dans le catalogue."); + }); + + it("does not open the reader with a fallback book", () => { + const source = readFileSync(new URL("./ReaderPage.tsx", import.meta.url), "utf8"); + + expect(source).not.toContain("getApiFallback"); + expect(source).toContain("setBook(null)"); + expect(source).toContain("reader-missing-page"); + expect(source).toContain("Livre introuvable"); + expect(source).toContain("Ce livre n'existe pas dans le catalogue."); + }); +}); diff --git a/apps/web/src/pages/BookPage.tsx b/apps/web/src/pages/BookPage.tsx index 2e607ce..5b41915 100644 --- a/apps/web/src/pages/BookPage.tsx +++ b/apps/web/src/pages/BookPage.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { BookOpen, LibraryBig, RotateCcw } from "lucide-react"; import type { BookDto, ProgressDto } from "@readabook/shared"; -import { api, getApiFallback } from "../api/client"; +import { api } from "../api/client"; import { cleanBookDescription } from "../book/description"; import { bookDisplayTitle, bookMetadataState, bookMetadataStateLabel, bookSeriesInfo, displayPublishedDate } from "../book/metadata"; import { EmptyState, ErrorRibbon, FormatPill, LoadingState, Meter, Panel } from "../components/ui"; @@ -17,14 +17,17 @@ export function BookPage({ bookId }: { bookId: number }) { setLoading(true); setError(undefined); try { - const [nextBook, nextProgress] = await Promise.all([api.book(bookId), api.progress(bookId)]); + const nextBook = await api.book(bookId); setBook(nextBook); - setProgress(nextProgress); - } catch (loadError) { - const fallback = getApiFallback(loadError); - setBook(fallback ?? null); + try { + setProgress(await api.progress(bookId)); + } catch { + setProgress(null); + } + } catch { + setBook(null); setProgress(null); - setError(fallback ? "Fiche chargee en mode secours." : "Fiche livre indisponible."); + setError("Ce livre n'existe pas dans le catalogue."); } finally { setLoading(false); } @@ -43,13 +46,20 @@ export function BookPage({ bookId }: { bookId: number }) { if (loading && !book) return ; if (!book) { return ( - - - - +
+ + +
+ + +
+
+
); } diff --git a/apps/web/src/pages/HomePage.tsx b/apps/web/src/pages/HomePage.tsx index a6f72c6..ba60c05 100644 --- a/apps/web/src/pages/HomePage.tsx +++ b/apps/web/src/pages/HomePage.tsx @@ -8,14 +8,12 @@ import { navigate } from "../router"; export function HomePage() { const [state, setState] = useState(null); - const [fallback, setFallback] = useState(false); useEffect(() => { let alive = true; Promise.all([api.books(), api.continueReading(), api.libraries(), api.jobs()]) .then(([books, continueReading, libraries, jobs]) => { if (!alive) return; - setFallback(books.some((book) => book.filePath.startsWith("/library/")) && jobs.length === 1); setState({ books, continueReading, libraries, jobs }); }) .catch(() => { @@ -50,7 +48,7 @@ export function HomePage() {

Bibliotheque personnelle

Reprendre la lecture.

- {fallback ? "API absente ou incomplete : specimens de demonstration actifs." : "Catalogue branche sur le serveur local."} + Catalogue branche sur le serveur local.
+ + + + + ); + } + return ( )} > - {!book ? ( -
- {error ?? "Chargement du livre."} - -
- ) : book.format === "pdf" ? ( + {book.format === "pdf" ? (