feat: Bedienelemente für Bibliothek und Episodenfortschritt
Server-Aktionen prüfen die Sitzung jeweils selbst — auf Angaben aus dem Formular ist beim Zugriffsschutz kein Verlass. Häkchen reagieren sofort und werden zurückgedreht, wenn der Server widerspricht: Abhaken soll sich anfühlen wie ein Schalter, nicht wie ein Formular. Jede Sichtungsaktion meldet sich unten kurz zurück und lässt sich von dort zurücknehmen — die Wirkung passiert weit weg vom Klick.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import Link from "next/link";
|
||||
import { Poster } from "@/components/Poster";
|
||||
import { ProgressBar } from "@/components/ProgressBar";
|
||||
import { STATUS_COLOR, STATUS_LABEL, type StatusKey } from "@/lib/labels";
|
||||
import type { LibraryItem } from "@/lib/queries";
|
||||
|
||||
/**
|
||||
* Posterkachel der Bibliothek.
|
||||
*
|
||||
* Alles Wesentliche steht ohne Klick da: Status, Fortschritt, Bewertung.
|
||||
* Die Karte ist ein Link — der ganze Bereich ist anklickbar, nicht nur der Titel.
|
||||
*/
|
||||
export function LibraryCard({ item }: { item: LibraryItem }) {
|
||||
const status = item.status as StatusKey;
|
||||
const showProgress = item.type === "SERIES" && item.total > 0 && item.status !== "WATCHLIST";
|
||||
|
||||
return (
|
||||
<Link href={`/titel/${item.titleId}`} className="fl-poster-card group block focus:outline-none">
|
||||
<div className="relative">
|
||||
<Poster path={item.posterPath} alt={item.title} size="w342" />
|
||||
|
||||
<span
|
||||
className="absolute left-2 top-2 rounded-full px-2 py-0.5 text-[0.65rem] font-semibold backdrop-blur-md"
|
||||
style={{
|
||||
color: STATUS_COLOR[status],
|
||||
backgroundColor: `color-mix(in oklab, var(--fl-bg) 72%, transparent)`,
|
||||
boxShadow: `inset 0 0 0 1px color-mix(in oklab, ${STATUS_COLOR[status]} 45%, transparent)`,
|
||||
}}
|
||||
>
|
||||
{STATUS_LABEL[status]}
|
||||
</span>
|
||||
|
||||
{item.rating != null && (
|
||||
<span
|
||||
className="absolute right-2 top-2 rounded-full px-2 py-0.5 text-[0.65rem] font-semibold text-[var(--fl-on-accent)]"
|
||||
style={{ backgroundImage: "var(--fl-gradient)" }}
|
||||
>
|
||||
{item.rating}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{item.currentRun > 0 && (
|
||||
<span className="absolute bottom-2 right-2 rounded-full bg-[color-mix(in_oklab,var(--fl-bg)_78%,transparent)] px-2 py-0.5 text-[0.65rem] font-medium text-[var(--fl-muted)] backdrop-blur-md">
|
||||
{item.currentRun + 1}. Durchlauf
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-2 space-y-1">
|
||||
<p className="line-clamp-2 text-sm font-medium leading-snug">{item.title}</p>
|
||||
<p className="text-xs text-[var(--fl-faint)]">
|
||||
{item.type === "SERIES" ? "Serie" : "Film"}
|
||||
{item.year ? ` · ${item.year}` : ""}
|
||||
{item.whereToWatch ? ` · ${item.whereToWatch}` : ""}
|
||||
</p>
|
||||
|
||||
{showProgress && (
|
||||
<div className="pt-0.5">
|
||||
<ProgressBar
|
||||
percent={item.percent}
|
||||
label={`${item.watched} von ${item.total} Episoden gesehen`}
|
||||
/>
|
||||
<p className="mt-1 text-xs text-[var(--fl-faint)]">
|
||||
{item.watched}/{item.total} Episoden
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user