feat: suivi de la progression de lecture par livre
- API : nouvelle table book_reading_progress (migration 0016) et module reading_progress.rs avec GET/PATCH /books/:id/progress (token read) - API : GET /books/:id enrichi avec reading_status, reading_current_page, reading_last_read_at via LEFT JOIN - Backoffice : badge de statut (Non lu / En cours · p.N / Lu) sur la page de détail et overlay sur les BookCards - OpenSpec : change reading-progress avec proposal/design/specs/tasks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,10 +3,17 @@
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { BookDto } from "../../lib/api";
|
||||
import { BookDto, ReadingStatus } from "../../lib/api";
|
||||
|
||||
const readingStatusOverlay: Record<ReadingStatus, { label: string; className: string } | null> = {
|
||||
unread: null,
|
||||
reading: { label: "En cours", className: "bg-amber-500/90 text-white" },
|
||||
read: { label: "Lu", className: "bg-green-600/90 text-white" },
|
||||
};
|
||||
|
||||
interface BookCardProps {
|
||||
book: BookDto & { coverUrl?: string };
|
||||
readingStatus?: ReadingStatus;
|
||||
}
|
||||
|
||||
function BookImage({ src, alt }: { src: string; alt: string }) {
|
||||
@@ -37,18 +44,27 @@ function BookImage({ src, alt }: { src: string; alt: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function BookCard({ book }: BookCardProps) {
|
||||
export function BookCard({ book, readingStatus }: BookCardProps) {
|
||||
const coverUrl = book.coverUrl || `/api/books/${book.id}/thumbnail`;
|
||||
|
||||
const status = readingStatus ?? book.reading_status;
|
||||
const overlay = status ? readingStatusOverlay[status] : null;
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={`/books/${book.id}`}
|
||||
<Link
|
||||
href={`/books/${book.id}`}
|
||||
className="group block bg-card rounded-xl border border-border/60 shadow-sm hover:shadow-md hover:-translate-y-1 transition-all duration-200 overflow-hidden"
|
||||
>
|
||||
<BookImage
|
||||
src={coverUrl}
|
||||
alt={`Cover of ${book.title}`}
|
||||
/>
|
||||
<div className="relative">
|
||||
<BookImage
|
||||
src={coverUrl}
|
||||
alt={`Cover of ${book.title}`}
|
||||
/>
|
||||
{overlay && (
|
||||
<span className={`absolute bottom-2 left-2 px-2 py-0.5 rounded-full text-[10px] font-bold tracking-wide ${overlay.className}`}>
|
||||
{overlay.label}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Book Info */}
|
||||
<div className="p-4">
|
||||
|
||||
Reference in New Issue
Block a user