feat: refactor book page to use ClientBookPage component and enhance data fetching with next book information

This commit is contained in:
Julien Froidefond
2025-10-17 16:18:38 +02:00
parent 191f10b8d7
commit 592aadf4ab
4 changed files with 121 additions and 60 deletions

View File

@@ -14,7 +14,9 @@ export async function GET(
const bookId: string = (await params).bookId; const bookId: string = (await params).bookId;
const data: KomgaBookWithPages = await BookService.getBook(bookId); const data: KomgaBookWithPages = await BookService.getBook(bookId);
return NextResponse.json(data); const nextBook = await BookService.getNextBook(bookId, data.book.seriesId);
return NextResponse.json({ ...data, nextBook });
} catch (error) { } catch (error) {
console.error("API Books - Erreur:", error); console.error("API Books - Erreur:", error);
if (error instanceof AppError) { if (error instanceof AppError) {

View File

@@ -1,38 +1,16 @@
import { Suspense } from "react"; import { Suspense } from "react";
import { ClientBookWrapper } from "@/components/reader/ClientBookWrapper"; import { ClientBookPage } from "@/components/reader/ClientBookPage";
import { BookSkeleton } from "@/components/skeletons/BookSkeleton"; import { BookSkeleton } from "@/components/skeletons/BookSkeleton";
import { BookService } from "@/lib/services/book.service";
import { withPageTiming } from "@/lib/hoc/withPageTiming"; import { withPageTiming } from "@/lib/hoc/withPageTiming";
import type { KomgaBookWithPages } from "@/types/komga";
import { ErrorMessage } from "@/components/ui/ErrorMessage";
import { ERROR_CODES } from "@/constants/errorCodes";
import { AppError } from "@/utils/errors";
async function BookPage({ params }: { params: { bookId: string } }) { async function BookPage({ params }: { params: { bookId: string } }) {
try { const { bookId } = await params;
const { bookId } = await params;
const data: KomgaBookWithPages = await BookService.getBook(bookId); return (
const nextBook = await BookService.getNextBook(bookId, data.book.seriesId); <Suspense fallback={<BookSkeleton />}>
return ( <ClientBookPage bookId={bookId} />
<Suspense fallback={<BookSkeleton />}> </Suspense>
<ClientBookWrapper book={data.book} pages={data.pages} nextBook={nextBook} /> );
</Suspense>
);
} catch (error) {
console.error("Erreur:", error);
if (error instanceof AppError) {
return (
<div className="container py-8 space-y-8">
<ErrorMessage errorCode={error.code} />
</div>
);
}
return (
<div className="container py-8 space-y-8">
<ErrorMessage errorCode={ERROR_CODES.SERIES.FETCH_ERROR} />
</div>
);
}
} }
export default withPageTiming("BookPage", BookPage); export default withPageTiming("BookPage", BookPage);

View File

@@ -14,39 +14,40 @@ export function ClientHomePage() {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
useEffect(() => { const fetchData = async () => {
const fetchData = async () => { setLoading(true);
setLoading(true); setError(null);
setError(null);
try { try {
const response = await fetch("/api/komga/home"); const response = await fetch("/api/komga/home");
if (!response.ok) { if (!response.ok) {
const errorData = await response.json(); const errorData = await response.json();
const errorCode = errorData.error?.code || ERROR_CODES.KOMGA.SERVER_UNREACHABLE; const errorCode = errorData.error?.code || ERROR_CODES.KOMGA.SERVER_UNREACHABLE;
// Si la config Komga est manquante, rediriger vers les settings // Si la config Komga est manquante, rediriger vers les settings
if (errorCode === ERROR_CODES.KOMGA.MISSING_CONFIG) { if (errorCode === ERROR_CODES.KOMGA.MISSING_CONFIG) {
router.push("/settings"); router.push("/settings");
return; return;
}
throw new Error(errorCode);
} }
const homeData = await response.json(); throw new Error(errorCode);
setData(homeData);
} catch (err) {
console.error("Error fetching home data:", err);
setError(err instanceof Error ? err.message : ERROR_CODES.KOMGA.SERVER_UNREACHABLE);
} finally {
setLoading(false);
} }
};
const homeData = await response.json();
setData(homeData);
} catch (err) {
console.error("Error fetching home data:", err);
setError(err instanceof Error ? err.message : ERROR_CODES.KOMGA.SERVER_UNREACHABLE);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData(); fetchData();
}, [router]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleRefresh = async () => { const handleRefresh = async () => {
try { try {
@@ -80,10 +81,14 @@ export function ClientHomePage() {
return <HomePageSkeleton />; return <HomePageSkeleton />;
} }
const handleRetry = () => {
fetchData();
};
if (error) { if (error) {
return ( return (
<main className="container mx-auto px-4 py-8"> <main className="container mx-auto px-4 py-8">
<ErrorMessage errorCode={error} /> <ErrorMessage errorCode={error} onRetry={handleRetry} />
</main> </main>
); );
} }
@@ -91,7 +96,7 @@ export function ClientHomePage() {
if (!data) { if (!data) {
return ( return (
<main className="container mx-auto px-4 py-8"> <main className="container mx-auto px-4 py-8">
<ErrorMessage errorCode={ERROR_CODES.KOMGA.SERVER_UNREACHABLE} /> <ErrorMessage errorCode={ERROR_CODES.KOMGA.SERVER_UNREACHABLE} onRetry={handleRetry} />
</main> </main>
); );
} }

View File

@@ -0,0 +1,76 @@
"use client";
import { useEffect, useState } from "react";
import { ClientBookWrapper } from "./ClientBookWrapper";
import { BookSkeleton } from "@/components/skeletons/BookSkeleton";
import { ErrorMessage } from "@/components/ui/ErrorMessage";
import { ERROR_CODES } from "@/constants/errorCodes";
import type { KomgaBook } from "@/types/komga";
interface ClientBookPageProps {
bookId: string;
}
export function ClientBookPage({ bookId }: ClientBookPageProps) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [data, setData] = useState<{
book: KomgaBook;
pages: number[];
nextBook: KomgaBook | null;
} | null>(null);
const fetchBookData = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(`/api/komga/books/${bookId}`);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.code || ERROR_CODES.BOOK.PAGES_FETCH_ERROR);
}
const bookData = await response.json();
setData(bookData);
} catch (err) {
console.error("Error fetching book:", err);
setError(err instanceof Error ? err.message : ERROR_CODES.BOOK.PAGES_FETCH_ERROR);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchBookData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [bookId]);
const handleRetry = () => {
fetchBookData();
};
if (loading) {
return <BookSkeleton />;
}
if (error) {
return (
<div className="container py-8 space-y-8">
<ErrorMessage errorCode={error} onRetry={handleRetry} />
</div>
);
}
if (!data) {
return (
<div className="container py-8 space-y-8">
<ErrorMessage errorCode={ERROR_CODES.BOOK.PAGES_FETCH_ERROR} onRetry={handleRetry} />
</div>
);
}
return <ClientBookWrapper book={data.book} pages={data.pages} nextBook={data.nextBook} />;
}