120 lines
4.8 KiB
TypeScript
120 lines
4.8 KiB
TypeScript
import Link from "next/link";
|
|
import { notFound } from "next/navigation";
|
|
import { ArrowLeft, Trash2 } from "lucide-react";
|
|
import { getTagCategoryBySlug, listTagsInCategory, listAllTags } from "@/lib/db/queries";
|
|
import { deleteTagCategory, renameTagCategory } from "@/app/actions/tagCategories";
|
|
import { EntityRenameInline } from "@/components/entities/EntityRenameInline";
|
|
import { CategoryTagAssigner } from "@/components/categories/CategoryTagAssigner";
|
|
import { CategoryCoverPanel } from "@/components/categories/CategoryCoverPanel";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export default async function CategoryDetailPage({ params }: { params: Promise<{ slug: string }> }) {
|
|
const { slug } = await params;
|
|
const cat = getTagCategoryBySlug(decodeURIComponent(slug));
|
|
if (!cat) notFound();
|
|
const tags = listTagsInCategory(cat.id);
|
|
// All tags, with their current category, so the assigner can let the
|
|
// user reassign uncategorised or differently-categorised tags into
|
|
// this one.
|
|
const allTags = listAllTags("az");
|
|
|
|
const remove = async () => {
|
|
"use server";
|
|
await deleteTagCategory(cat.id);
|
|
};
|
|
const rename = async (name: string) => {
|
|
"use server";
|
|
return await renameTagCategory(cat.id, name);
|
|
};
|
|
|
|
return (
|
|
<div className="max-w-[1600px] mx-auto px-6 py-6 fade-in">
|
|
<Link href="/category" 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 categories
|
|
</Link>
|
|
<div className="flex items-start justify-between gap-6 mb-6">
|
|
<div className="min-w-0">
|
|
<div className="flex items-center gap-2">
|
|
<span
|
|
className="w-4 h-4 rounded-full shrink-0"
|
|
style={{ background: cat.color ?? "var(--color-fg-muted)" }}
|
|
/>
|
|
<h1 className="text-3xl font-semibold tracking-tight truncate">{cat.name}</h1>
|
|
<EntityRenameInline
|
|
initialName={cat.name}
|
|
onRename={rename}
|
|
redirectBase="/category/"
|
|
redirectKey="slug"
|
|
/>
|
|
<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>
|
|
{cat.description && <p className="text-[var(--color-fg-dim)] mt-2">{cat.description}</p>}
|
|
<p className="text-sm text-[var(--color-fg-muted)] mt-1">{tags.length} tag{tags.length === 1 ? "" : "s"} in this category</p>
|
|
</div>
|
|
</div>
|
|
|
|
<section className="mb-6">
|
|
<h2 className="text-sm uppercase tracking-wider font-mono text-[var(--color-fg-muted)] mb-2">Cover art</h2>
|
|
<CategoryCoverPanel
|
|
categoryId={cat.id}
|
|
categoryName={cat.name}
|
|
categoryColor={cat.color}
|
|
portrait={{
|
|
path: cat.coverPortraitPath,
|
|
zoom: cat.coverPortraitZoom,
|
|
offsetX: cat.coverPortraitOffsetX,
|
|
offsetY: cat.coverPortraitOffsetY,
|
|
}}
|
|
landscape={{
|
|
path: cat.coverLandscapePath,
|
|
zoom: cat.coverLandscapeZoom,
|
|
offsetX: cat.coverLandscapeOffsetX,
|
|
offsetY: cat.coverLandscapeOffsetY,
|
|
}}
|
|
/>
|
|
</section>
|
|
|
|
<section className="mb-6">
|
|
<h2 className="text-sm uppercase tracking-wider font-mono text-[var(--color-fg-muted)] mb-2">Member tags</h2>
|
|
{tags.length === 0 ? (
|
|
<div className="glass rounded-2xl p-8 text-center text-sm text-[var(--color-fg-dim)]">
|
|
No tags assigned yet. Use the picker below to add some.
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-wrap gap-2">
|
|
{tags.map((t) => (
|
|
<Link
|
|
key={t.id}
|
|
href={`/tag/${encodeURIComponent(t.name)}`}
|
|
className="flex items-center gap-2 px-3 py-1.5 rounded-full glass glass-hover text-sm"
|
|
>
|
|
<span className="text-[var(--color-violet)]">{t.name}</span>
|
|
<span className="text-xs font-mono text-[var(--color-fg-muted)]">{t.count}</span>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
|
|
<section>
|
|
<h2 className="text-sm uppercase tracking-wider font-mono text-[var(--color-fg-muted)] mb-2">Assign tags</h2>
|
|
<CategoryTagAssigner
|
|
categoryId={cat.id}
|
|
tags={allTags.map((t) => ({
|
|
id: t.id,
|
|
name: t.name,
|
|
count: t.count,
|
|
currentCategoryId: t.categoryId,
|
|
currentCategoryName: t.categoryName,
|
|
}))}
|
|
/>
|
|
</section>
|
|
</div>
|
|
);
|
|
}
|