import type { Metadata } from "next"; import { notFound } from "next/navigation"; import Link from "next/link"; import Image from "next/image"; import { getPayload } from "payload"; import config from "@payload-config"; import { RichText } from "@payloadcms/richtext-lexical/react"; import { GridOverlay, TechChip } from "@/components/site"; import type { Project, Media } from "@/payload-types"; export const dynamic = "force-dynamic"; type Params = { slug: string }; function isMedia(val: unknown): val is Media { return typeof val === "object" && val !== null && "url" in val; } const STATUS_PILL: Record = { active: "bg-primary/10 text-primary border border-primary/20", production: "bg-tertiary/10 text-tertiary border border-tertiary/20", prototype: "bg-secondary/10 text-secondary border border-secondary/20", archived: "bg-surface-container-highest text-on-surface-variant border border-outline-variant/15", }; const LINK_ICONS: Record = { live: "↗", repo: "⌥", docs: "⎕", writeup: "✎", }; export async function generateMetadata({ params, }: { params: Promise; }): Promise { const { slug } = await params; const payload = await getPayload({ config }); const { docs } = await payload.find({ collection: "projects", where: { slug: { equals: slug } }, depth: 0, limit: 1, }); const project = docs[0] as Project | undefined; if (!project) return { title: "Project Not Found" }; return { title: project.title, description: project.summary, }; } export default async function ProjectDetailPage({ params, }: { params: Promise; }) { const { slug } = await params; const payload = await getPayload({ config }); const { docs } = await payload.find({ collection: "projects", where: { slug: { equals: slug } }, depth: 2, limit: 1, }); const project = docs[0] as Project | undefined; if (!project) notFound(); const hero = isMedia(project.heroImage) ? project.heroImage : null; return (
{/* Hero */}
{hero?.url && (
{hero.alt}
)}
PROJECTS
{project.status} {project.year && ( {project.year} )}

{project.title}

{project.role && (

{project.role}

)}
{/* Body + Sidebar */}
{/* Main content — 70% */}

{project.summary}

{project.body && (
[0]["data"]} />
)} {/* Gallery */} {project.gallery && project.gallery.length > 0 && (
GALLERY
{project.gallery.map((entry, i) => { const img = isMedia(entry.image) ? entry.image : null; if (!img?.url) return null; return (
{img.alt}
{entry.caption && (
{entry.caption}
)}
); })}
)}
{/* Sidebar — 30% */}
{/* Footer nav */}
← All projects
); }