feat: display missing books count badge on series covers
Show an orange badge with BookX icon on series covers when the Stripstream API reports missing books in the collection. Also display a warning status badge on the series detail page header. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Book, BookOpen, BookMarked, Star, StarOff, User } from "lucide-react";
|
import { Book, BookOpen, BookMarked, BookX, Star, StarOff, User } from "lucide-react";
|
||||||
import type { NormalizedSeries } from "@/lib/providers/types";
|
import type { NormalizedSeries } from "@/lib/providers/types";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
@@ -160,6 +160,11 @@ export const SeriesHeader = ({ series, refreshSeries, initialIsFavorite }: Serie
|
|||||||
? t("series.header.books", { count: series.bookCount })
|
? t("series.header.books", { count: series.bookCount })
|
||||||
: t("series.header.books_plural", { count: series.bookCount })}
|
: t("series.header.books_plural", { count: series.bookCount })}
|
||||||
</span>
|
</span>
|
||||||
|
{series.missingCount != null && series.missingCount > 0 && (
|
||||||
|
<StatusBadge status="warning" icon={BookX}>
|
||||||
|
{t("series.header.missing", { count: series.missingCount })}
|
||||||
|
</StatusBadge>
|
||||||
|
)}
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ProgressBar } from "./progress-bar";
|
import { ProgressBar } from "./progress-bar";
|
||||||
|
import { BookX } from "lucide-react";
|
||||||
import type { SeriesCoverProps } from "./cover-utils";
|
import type { SeriesCoverProps } from "./cover-utils";
|
||||||
|
|
||||||
export function SeriesCover({
|
export function SeriesCover({
|
||||||
@@ -12,6 +13,7 @@ export function SeriesCover({
|
|||||||
const readBooks = series.booksReadCount;
|
const readBooks = series.booksReadCount;
|
||||||
const totalBooks = series.bookCount;
|
const totalBooks = series.bookCount;
|
||||||
const showProgress = Boolean(showProgressUi && totalBooks > 0 && readBooks > 0 && !isCompleted);
|
const showProgress = Boolean(showProgressUi && totalBooks > 0 && readBooks > 0 && !isCompleted);
|
||||||
|
const missingCount = series.missingCount;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full">
|
<div className="relative w-full h-full">
|
||||||
@@ -27,6 +29,12 @@ export function SeriesCover({
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(" ")}
|
.join(" ")}
|
||||||
/>
|
/>
|
||||||
|
{showProgressUi && missingCount != null && missingCount > 0 && (
|
||||||
|
<div className="absolute top-1.5 right-1.5 flex items-center gap-0.5 rounded-full bg-orange-500/90 px-1.5 py-0.5 text-white shadow-md backdrop-blur-sm">
|
||||||
|
<BookX className="h-3 w-3" />
|
||||||
|
<span className="text-[10px] font-bold leading-none">{missingCount}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{showProgress ? <ProgressBar progress={readBooks} total={totalBooks} type="series" /> : null}
|
{showProgress ? <ProgressBar progress={readBooks} total={totalBooks} type="series" /> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -263,6 +263,7 @@
|
|||||||
},
|
},
|
||||||
"showMore": "Show more",
|
"showMore": "Show more",
|
||||||
"showLess": "Show less",
|
"showLess": "Show less",
|
||||||
|
"missing": "{{count}} missing",
|
||||||
"toggleSidebar": "Toggle sidebar",
|
"toggleSidebar": "Toggle sidebar",
|
||||||
"toggleTheme": "Toggle theme"
|
"toggleTheme": "Toggle theme"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,7 +262,8 @@
|
|||||||
"remove": "Retiré des favoris"
|
"remove": "Retiré des favoris"
|
||||||
},
|
},
|
||||||
"showMore": "Voir plus",
|
"showMore": "Voir plus",
|
||||||
"showLess": "Voir moins"
|
"showLess": "Voir moins",
|
||||||
|
"missing": "{{count}} manquant(s)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"books": {
|
"books": {
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export class StripstreamAdapter {
|
|||||||
genres: [],
|
genres: [],
|
||||||
tags: [],
|
tags: [],
|
||||||
createdAt: null,
|
createdAt: null,
|
||||||
|
missingCount: series.missing_count ?? null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export interface NormalizedSeries {
|
|||||||
genres?: string[];
|
genres?: string[];
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
createdAt?: string | null;
|
createdAt?: string | null;
|
||||||
|
missingCount?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NormalizedBook {
|
export interface NormalizedBook {
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export interface StripstreamSeriesItem {
|
|||||||
books_read_count: number;
|
books_read_count: number;
|
||||||
first_book_id: string;
|
first_book_id: string;
|
||||||
library_id: string;
|
library_id: string;
|
||||||
|
missing_count?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StripstreamSeriesPage {
|
export interface StripstreamSeriesPage {
|
||||||
|
|||||||
Reference in New Issue
Block a user