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:
2026-03-10 21:53:52 +01:00
parent 278f422206
commit 648d86970f
16 changed files with 516 additions and 11 deletions

View File

@@ -46,6 +46,14 @@ export type FolderItem = {
has_children: boolean;
};
export type ReadingStatus = "unread" | "reading" | "read";
export type ReadingProgressDto = {
status: ReadingStatus;
current_page: number | null;
last_read_at: string | null;
};
export type BookDto = {
id: string;
library_id: string;
@@ -60,6 +68,10 @@ export type BookDto = {
file_format: string | null;
file_parse_status: string | null;
updated_at: string;
// Présents uniquement sur GET /books/:id (pas dans la liste)
reading_status?: ReadingStatus;
reading_current_page?: number | null;
reading_last_read_at?: string | null;
};
export type BooksPageDto = {
@@ -353,3 +365,18 @@ export async function getThumbnailStats() {
export async function convertBook(bookId: string) {
return apiFetch<IndexJobDto>(`/books/${bookId}/convert`, { method: "POST" });
}
export async function fetchReadingProgress(bookId: string) {
return apiFetch<ReadingProgressDto>(`/books/${bookId}/progress`);
}
export async function updateReadingProgress(
bookId: string,
status: ReadingStatus,
currentPage?: number,
) {
return apiFetch<ReadingProgressDto>(`/books/${bookId}/progress`, {
method: "PATCH",
body: JSON.stringify({ status, current_page: currentPage ?? null }),
});
}