feat: Oberfläche für Universen
Umsortieren geht per Ziehen und über Pfeilknöpfe: reines Drag & Drop wäre mit Tastatur nicht bedienbar und auf dem Handy fummelig — die Knöpfe sind der verlässliche Weg, das Ziehen der bequeme. Die Erscheinungsreihenfolge lässt sich gar nicht erst anfassen, weil sie sich aus den Daten ergibt.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
import { requireUser } from "@/lib/auth";
|
||||
import { getSettings } from "@/lib/settings";
|
||||
import { listUniverses } from "@/lib/universes";
|
||||
import { AppShell } from "@/components/AppShell";
|
||||
import { Poster } from "@/components/Poster";
|
||||
import { ProgressBar } from "@/components/ProgressBar";
|
||||
|
||||
export const metadata: Metadata = { title: "Universen" };
|
||||
|
||||
export default async function UniversesPage() {
|
||||
const user = await requireUser("/universen");
|
||||
const settings = await getSettings();
|
||||
const universes = await listUniverses(user.id);
|
||||
|
||||
return (
|
||||
<AppShell user={user} instanceName={settings.instanceName}>
|
||||
<header className="mb-6 flex flex-wrap items-end justify-between gap-3">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold tracking-tight sm:text-3xl">Universen</h1>
|
||||
<p className="mt-1 max-w-2xl text-sm text-[var(--fl-muted)]">
|
||||
Filme und Serien, die zusammengehören — mit mehreren Reihenfolgen und einem
|
||||
Gesamtfortschritt.
|
||||
</p>
|
||||
</div>
|
||||
<Link href="/universen/neu" className="fl-btn fl-btn-primary">
|
||||
Neues Universum
|
||||
</Link>
|
||||
</header>
|
||||
|
||||
{universes.length === 0 ? (
|
||||
<div className="fl-card p-8 text-center">
|
||||
<p className="mx-auto max-w-md text-[var(--fl-muted)]">
|
||||
Noch kein Universum angelegt. Ein Universum bündelt zusammengehörige Titel — etwa alle
|
||||
MCU-Filme samt Serien — und zeigt dir, was als Nächstes dran ist.
|
||||
</p>
|
||||
<Link href="/universen/neu" className="fl-btn fl-btn-primary mt-4">
|
||||
Erstes Universum anlegen
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<ul className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{universes.map((universe) => (
|
||||
<li key={universe.id}>
|
||||
<Link
|
||||
href={`/universen/${universe.id}`}
|
||||
className="fl-card fl-poster-card block overflow-hidden p-4"
|
||||
>
|
||||
<div className="mb-3 flex gap-1.5">
|
||||
{universe.posters.length > 0 ? (
|
||||
universe.posters.map((path, index) => (
|
||||
<div key={`${path}-${index}`} className="w-1/5">
|
||||
<Poster path={path} alt="" size="w154" />
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="w-1/5">
|
||||
<Poster path={null} alt={universe.name} size="w154" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h2 className="font-semibold">{universe.name}</h2>
|
||||
{universe.description && (
|
||||
<p className="mt-1 line-clamp-2 text-sm text-[var(--fl-muted)]">
|
||||
{universe.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<p className="mt-2 text-xs text-[var(--fl-faint)]">
|
||||
{universe.itemCount} {universe.itemCount === 1 ? "Titel" : "Titel"}
|
||||
{!universe.isOwner ? ` · geteilt von ${universe.ownerName}` : ""}
|
||||
</p>
|
||||
|
||||
<div className="mt-2">
|
||||
<ProgressBar
|
||||
percent={universe.percent}
|
||||
label={`${universe.completed} von ${universe.itemCount} abgeschlossen`}
|
||||
showValue
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user