feat: add batch metadata jobs, series filters, and translate backoffice to French

- Add metadata_batch job type with background processing via tokio::spawn
- Auto-apply metadata only when single result at 100% confidence
- Support primary + fallback provider per library, "none" to opt out
- Add batch report/results API endpoints and job detail UI
- Add series_status and has_missing filters to both series listing pages
- Add GET /series/statuses endpoint for dynamic filter options
- Normalize series_metadata status values (migration 0036)
- Hide ComicVine provider tab when no API key configured
- Translate entire backoffice UI from English to French

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-18 18:26:44 +01:00
parent 9a8c1577af
commit b955c2697c
46 changed files with 2161 additions and 379 deletions

View File

@@ -55,7 +55,7 @@ export default async function SeriesDetailPage({
const totalPages = Math.ceil(booksPage.total / limit);
const booksReadCount = booksPage.items.filter((b) => b.reading_status === "read").length;
const displayName = seriesName === "unclassified" ? "Non classifié" : seriesName;
const displayName = seriesName === "unclassified" ? "Non classé" : seriesName;
// Use first book cover as series cover
const coverBookId = booksPage.items[0]?.id;
@@ -68,7 +68,7 @@ export default async function SeriesDetailPage({
href="/libraries"
className="text-muted-foreground hover:text-primary transition-colors"
>
Libraries
Bibliothèques
</Link>
<span className="text-muted-foreground">/</span>
<Link
@@ -88,7 +88,7 @@ export default async function SeriesDetailPage({
<div className="w-40 aspect-[2/3] relative rounded-xl overflow-hidden shadow-card border border-border">
<Image
src={getBookCoverUrl(coverBookId)}
alt={`Cover of ${displayName}`}
alt={`Couverture de ${displayName}`}
fill
className="object-cover"
unoptimized