From fea840aa71f141a7640cd6e6e02f1165aa9a14e5 Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Wed, 19 Feb 2025 17:02:41 +0100 Subject: [PATCH] feat: mark as read --- src/components/series/BookGrid.tsx | 88 +++++++++++++++++------ src/components/ui/mark-as-read-button.tsx | 65 +++++++++++++++++ 2 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 src/components/ui/mark-as-read-button.tsx diff --git a/src/components/series/BookGrid.tsx b/src/components/series/BookGrid.tsx index 2914c18..c7ef6df 100644 --- a/src/components/series/BookGrid.tsx +++ b/src/components/series/BookGrid.tsx @@ -3,6 +3,8 @@ import { KomgaBook } from "@/types/komga"; import { formatDate } from "@/lib/utils"; import { Cover } from "@/components/ui/cover"; +import { MarkAsReadButton } from "@/components/ui/mark-as-read-button"; +import { useState } from "react"; interface BookGridProps { books: KomgaBook[]; @@ -40,7 +42,9 @@ const getReadingStatusInfo = (book: KomgaBook) => { }; export function BookGrid({ books, onBookClick }: BookGridProps) { - if (!books.length) { + const [localBooks, setLocalBooks] = useState(books); + + if (!localBooks.length) { return (

Aucun tome disponible

@@ -48,33 +52,77 @@ export function BookGrid({ books, onBookClick }: BookGridProps) { ); } + const handleMarkAsRead = (bookId: string) => { + setLocalBooks((prevBooks) => + prevBooks.map((book) => + book.id === bookId + ? { + ...book, + readProgress: { + ...(book.readProgress || {}), + completed: true, + readDate: new Date().toISOString(), + page: book.media.pagesCount, + created: book.readProgress?.created || new Date().toISOString(), + lastModified: new Date().toISOString(), + }, + } + : book + ) + ); + }; + return (
- {books.map((book) => { + {localBooks.map((book) => { const statusInfo = getReadingStatusInfo(book); + const isRead = book.readProgress?.completed || false; + return ( - + + {/* Overlay avec les contrôles */} +
+ {/* Bouton Marquer comme lu en haut à droite avec un petit décalage */} + {!isRead && ( +
+ handleMarkAsRead(book.id)} + className="bg-white/90 hover:bg-white text-black shadow-sm" + /> +
+ )} + + {/* Informations en bas - visible au survol uniquement */} +
+

+ {book.metadata.title || `Tome ${book.metadata.number}`} +

+
+ + {statusInfo.label} + +
- +
); })}
diff --git a/src/components/ui/mark-as-read-button.tsx b/src/components/ui/mark-as-read-button.tsx new file mode 100644 index 0000000..ae2d904 --- /dev/null +++ b/src/components/ui/mark-as-read-button.tsx @@ -0,0 +1,65 @@ +"use client"; + +import { CheckCircle2 } from "lucide-react"; +import { Button } from "./button"; +import { useToast } from "./use-toast"; + +interface MarkAsReadButtonProps { + bookId: string; + pagesCount: number; + isRead?: boolean; + onSuccess?: () => void; + className?: string; +} + +export function MarkAsReadButton({ + bookId, + pagesCount, + isRead = false, + onSuccess, + className, +}: MarkAsReadButtonProps) { + const { toast } = useToast(); + + const handleMarkAsRead = async (e: React.MouseEvent) => { + e.stopPropagation(); // Empêcher la propagation au parent + try { + const response = await fetch(`/api/komga/books/${bookId}/read-progress`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ page: pagesCount, completed: true }), + }); + + if (!response.ok) { + throw new Error("Erreur lors de la mise à jour"); + } + + toast({ + title: "Succès", + description: "Le tome a été marqué comme lu", + }); + onSuccess?.(); + } catch (error) { + toast({ + title: "Erreur", + description: "Impossible de marquer le tome comme lu", + variant: "destructive", + }); + } + }; + + return ( + + ); +}