export const dynamic = "force-dynamic"; export const revalidate = 0; import { prisma } from "@/lib/prisma"; import Link from "next/link"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; import { ArrowLeftIcon } from "lucide-react"; type SimpleArtwork = { id: string; name: string; sortKey: number | null; }; function sortBySortIndexName(a: T, b: T) { return a.sortIndex - b.sortIndex || a.name.localeCompare(b.name); } function sortArtworks(a: SimpleArtwork, b: SimpleArtwork) { const ak = a.sortKey ?? 999999; const bk = b.sortKey ?? 999999; return ak - bk || a.name.localeCompare(b.name) || a.id.localeCompare(b.id); } export default async function AnimalListPage() { /** * We fetch all "animal page" tags and only artworks that are: * - published * - in "Animal Studies" category * * This makes the list reflect exactly what the user sees on the Animal Studies page. */ const tags = await prisma.artTag.findMany({ where: { showOnAnimalPage: true }, select: { id: true, name: true, slug: true, sortIndex: true, parentId: true, // artworks tagged with THIS tag artworks: { where: { published: true, categories: { some: { name: "Animal Studies" } }, }, select: { id: true, name: true, sortKey: true }, orderBy: [{ sortKey: "asc" }, { name: "asc" }, { id: "asc" }], }, }, orderBy: [{ sortIndex: "asc" }, { name: "asc" }], }); // Build maps to render a robust parent/child hierarchy (no reliance on Prisma nested children) const byId = new Map(tags.map((t) => [t.id, t])); const childrenByParentId = new Map(); for (const t of tags) { if (!t.parentId) continue; const arr = childrenByParentId.get(t.parentId) ?? []; arr.push(t); childrenByParentId.set(t.parentId, arr); } for (const [pid, arr] of childrenByParentId) { childrenByParentId.set(pid, arr.slice().sort(sortBySortIndexName)); } const parents = tags .filter((t) => t.parentId === null) .slice() .sort(sortBySortIndexName); // Orphans: child references a parentId that isn't present (or isn't showOnAnimalPage) const orphans = tags .filter((t) => t.parentId !== null && !byId.has(t.parentId)) .slice() .sort(sortBySortIndexName); // Small helper to render artworks list (linked) const ArtworkList = ({ items }: { items: SimpleArtwork[] }) => { const list = items.slice().sort(sortArtworks); if (list.length === 0) { return

No artworks found.

; } return ( ); }; // Count helper for badges const countArtworks = (tagId: string) => { const t = byId.get(tagId); return t?.artworks?.length ?? 0; }; const countArtworksInChildren = (tagId: string) => { const children = childrenByParentId.get(tagId) ?? []; let sum = 0; for (const c of children) sum += c.artworks.length; return sum; }; return (

Animal index

{/*

Click to expand and browse linked artworks.

*/}
Grouped animals {/*

Parent tags expand into children; standalone tags appear as single entries.

*/}

Click to expand and browse linked artworks.

{parents.length === 0 ? (

No parent tags found.

) : ( {parents.map((p) => { const children = childrenByParentId.get(p.id) ?? []; const parentDirectCount = p.artworks.length; const childrenCount = countArtworksInChildren(p.id); // Standalone root tag: no children const isStandalone = children.length === 0; return (
{p.name} {isStandalone ? ( single ) : ( {children.length} sub )}
{parentDirectCount > 0 ? ( {parentDirectCount} direct ) : null} {!isStandalone ? ( {childrenCount} in sub ) : ( {parentDirectCount} artworks )}
{/* If standalone root: just list its artworks */} {isStandalone ? (
Artworks
{p.artworks.length}
) : (
{/* Optional: artworks directly on the parent */}
Directly tagged
{p.artworks.length}
{/* Children blocks */}
{children.map((c) => (
{c.name}
Sub-tag of {p.name}
{c.artworks.length}
))}
)}
); })}
)}
{orphans.length ? ( Ungrouped animals {/*

Tags whose parent is not visible (or not configured for the animal page).

*/}
{orphans.map((t) => (
{t.name}
{t.artworks.length}
))}
) : null}
); }