82 lines
3.2 KiB
TypeScript
82 lines
3.2 KiB
TypeScript
import Link from "next/link";
|
|
import { notFound } from "next/navigation";
|
|
import { ArrowLeft, Trash2 } from "lucide-react";
|
|
import { getStudioBySlug, 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 { deleteStudio, renameStudio } from "@/app/actions/entities";
|
|
import { EntityRenameInline } from "@/components/entities/EntityRenameInline";
|
|
import { parseFilterCriteria, statusToFlags } from "@/lib/filters";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export default async function StudioPage({
|
|
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 s = getStudioBySlug(decodeURIComponent(slug));
|
|
if (!s) notFound();
|
|
const items = listImages({
|
|
studioId: s.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,
|
|
});
|
|
|
|
const remove = async () => {
|
|
"use server";
|
|
await deleteStudio(s.id);
|
|
};
|
|
|
|
const rename = async (name: string) => {
|
|
"use server";
|
|
return await renameStudio(s.id, name);
|
|
};
|
|
|
|
return (
|
|
<div className="max-w-[1600px] mx-auto px-6 py-6 fade-in">
|
|
<Link href="/studios" 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 studios
|
|
</Link>
|
|
<div className="mb-4">
|
|
<div className="flex items-center gap-2">
|
|
<h1 className="text-3xl font-semibold tracking-tight mb-1">{s.name}</h1>
|
|
<EntityRenameInline initialName={s.name} onRename={rename} redirectBase="/studios/" />
|
|
<form action={remove}>
|
|
<button className="flex items-center gap-1.5 text-xs px-2 py-1 rounded-lg glass text-[var(--color-fg-muted)] hover:text-[var(--color-coral)] hover:border-[var(--color-coral)]/30">
|
|
<Trash2 className="w-3.5 h-3.5" /> Delete
|
|
</button>
|
|
</form>
|
|
</div>
|
|
<p className="text-[var(--color-fg-dim)]">{items.length} cover{items.length === 1 ? "" : "s"}</p>
|
|
</div>
|
|
<FilterBar current={{ kind: "studio", name: s.name }} criteria={criteria} sort={sort} />
|
|
<RegisterVisible ids={items.map((i) => i.id)} />
|
|
<MasonryGrid images={items} />
|
|
</div>
|
|
);
|
|
}
|