Initial commit

This commit is contained in:
admin
2026-05-26 22:46:00 +02:00
commit 7e2c2ff89c
256 changed files with 51523 additions and 0 deletions
+61
View File
@@ -0,0 +1,61 @@
import Link from "next/link";
import { notFound } from "next/navigation";
import { ArrowLeft } from "lucide-react";
import { getLabelBySlug, listImages } from "@/lib/db/queries";
import { MasonryGrid } from "@/components/grid/MasonryGrid";
import { RegisterVisible } from "@/components/select/RegisterVisible";
import { FilterBar } from "@/components/grid/FilterBar";
import { resolveSort } from "@/lib/sortServer";
import { parseFilterCriteria, statusToFlags } from "@/lib/filters";
export const dynamic = "force-dynamic";
export default async function LabelPage({
params,
searchParams,
}: {
params: Promise<{ slug: string }>;
searchParams: Promise<Record<string, string | string[] | undefined>>;
}) {
const { slug } = await params;
const sp = await searchParams;
const sort = await resolveSort(typeof sp.sort === "string" ? sp.sort : undefined);
const search = (typeof sp.q === "string" ? sp.q.trim() : "") || undefined;
const criteria = parseFilterCriteria(sp);
const label = getLabelBySlug(decodeURIComponent(slug));
if (!label) notFound();
const items = listImages({
labelId: label.id,
sort,
search,
...statusToFlags(criteria.status),
marks: criteria.marks,
actressIds: criteria.ids.actresses,
actressMode: criteria.mode.actresses,
studioIds: criteria.ids.studios,
seriesIds: criteria.ids.series,
genreIds: criteria.ids.genres,
genreMode: criteria.mode.genres,
collectionIds: criteria.ids.collections,
collectionMode: criteria.mode.collections,
tagIds: criteria.ids.tags,
tagMode: criteria.mode.tags,
categoryIds: criteria.ids.categories,
categoryMode: criteria.mode.categories,
});
return (
<div className="max-w-[1600px] mx-auto px-6 py-6 fade-in">
<Link href="/labels" className="inline-flex items-center gap-1 text-sm text-[var(--color-fg-dim)] hover:text-[var(--color-fg)] mb-4">
<ArrowLeft className="w-4 h-4" /> All labels
</Link>
<div className="mb-4">
<h1 className="text-3xl font-semibold tracking-tight mb-1">{label.name}</h1>
<p className="text-[var(--color-fg-dim)]">{items.length} cover{items.length === 1 ? "" : "s"}</p>
</div>
<FilterBar current={{ kind: "label", name: label.name }} criteria={criteria} sort={sort} />
<RegisterVisible ids={items.map((i) => i.id)} />
<MasonryGrid images={items} />
</div>
);
}
+37
View File
@@ -0,0 +1,37 @@
import Link from "next/link";
import { listAllLabels } from "@/lib/db/queries";
import { Tag } from "lucide-react";
export const dynamic = "force-dynamic";
export default function LabelsPage() {
const items = listAllLabels();
return (
<div className="max-w-[1600px] mx-auto px-6 py-6 fade-in">
<div className="mb-6">
<h1 className="text-3xl font-semibold tracking-tight">Labels</h1>
<p className="text-[var(--color-fg-dim)] mt-1">{items.length} total</p>
</div>
{items.length === 0 ? (
<div className="glass rounded-2xl p-card text-center">
<Tag className="w-8 h-8 mx-auto text-[var(--color-fg-dim)] mb-label" />
<p className="text-[var(--color-fg-dim)]">No labels yet.</p>
</div>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3">
{items.map((label) => (
<Link
key={label.id}
href={`/labels/${label.slug}`}
className="glass glass-hover rounded-xl p-4 flex items-center justify-between gap-3"
>
<span className="font-medium truncate">{label.name}</span>
<span className="text-xs font-mono text-[var(--color-fg-muted)] tabular-nums">{label.count}</span>
</Link>
))}
</div>
)}
</div>
);
}