/** * /c/[slug] — Case report viewer. * * Reads /data/ufo/case/reports/.md, parses frontmatter for metadata, * renders the markdown body via MarkdownBody. The case-writer detective * writes these files; this page is the reader. */ import { notFound } from "next/navigation"; import Link from "next/link"; import { readFile } from "node:fs/promises"; import path from "node:path"; import { MarkdownBody } from "@/components/markdown-body"; import { AuthBar } from "@/components/auth-bar"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; const CASE_ROOT = process.env.CASE_ROOT || "/data/ufo/case"; interface Frontmatter { topic?: string; created_by?: string; created_at?: string; job_id?: string; n_evidence?: number; n_hypotheses?: number; n_contradictions?: number; n_witnesses?: number; n_outliers?: number; } function parseFrontmatter(md: string): { fm: Frontmatter; body: string } { const m = md.match(/^---\n([\s\S]+?)\n---\n([\s\S]*)$/); if (!m) return { fm: {}, body: md }; const fm: Frontmatter = {}; for (const line of m[1].split("\n")) { const kv = line.match(/^([a-z_]+):\s*(.+)$/); if (!kv) continue; const k = kv[1] as keyof Frontmatter; let v: string | number = kv[2].trim(); if (v.startsWith('"') && v.endsWith('"')) v = v.slice(1, -1); if (k === "n_evidence" || k === "n_hypotheses" || k === "n_contradictions" || k === "n_witnesses" || k === "n_outliers") { const n = Number(v); if (Number.isFinite(n)) (fm[k] as number) = n; } else { (fm[k] as string) = v as string; } } return { fm, body: m[2] }; } export default async function CaseReportPage({ params, }: { params: Promise<{ slug: string }> }) { const { slug } = await params; if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) notFound(); let md: string; try { md = await readFile(path.join(CASE_ROOT, "reports", `${slug}.md`), "utf-8"); } catch { notFound(); } const { fm, body } = parseFrontmatter(md); const stats: Array<{ label: string; value: number | undefined; color: string }> = [ { label: "evidence", value: fm.n_evidence, color: "text-[#06d6a0]" }, { label: "hypotheses", value: fm.n_hypotheses, color: "text-[#7fdbff]" }, { label: "contradictions", value: fm.n_contradictions, color: "text-[#ff8a4d]" }, { label: "witnesses", value: fm.n_witnesses, color: "text-[#9b5de5]" }, { label: "outliers", value: fm.n_outliers, color: "text-[#ffd23f]" }, ]; return (
disclosure.top / case-report / {slug}
Case report{fm.created_by && <> · written by {fm.created_by}} {fm.created_at && <> · {fm.created_at}}
{fm.topic && (

{fm.topic}

)}
{stats.filter((s) => typeof s.value === "number").map((s) => ( {s.value} {s.label} ))}
{body}
); }