/** * EntityListPage — shared shell used by /sightings, /witnesses, /objects, * /locations, /operations. * * Pulls entities of a given class, sorted by total_mentions, paginated. * Renders a magazine-style index page with a hero header + searchable * card grid. No detective branding. */ import Link from "next/link"; import { pgQuery } from "@/lib/retrieval/db"; import { SiteHeader } from "@/components/site-header"; import { BureauNav } from "@/components/bureau-nav"; import { getLocale } from "@/components/locale-toggle"; interface EntityRow { entity_class: string; entity_id: string; canonical_name: string; aliases: string[] | null; total_mentions: number; documents_count: number; summary_en: string | null; summary_pt_br: string | null; summary_status: string | null; } export interface EntityListPageProps { entityClass: "event" | "person" | "uap_object" | "location" | "organization"; /** URL folder under /e/ — kebab-case plural. */ folder: "events" | "people" | "uap-objects" | "locations" | "organizations"; title_en: string; title_pt: string; subtitle_en: string; subtitle_pt: string; /** Minimum total_mentions to surface — filters out doc-scoped noise. */ min_mentions?: number; /** Card variant. "magazine" = larger cards with year/icon; "compact" = tabular list. */ variant?: "magazine" | "compact"; } function parseEventId(id: string): { year: number | null } { const m = id.match(/^EV-(\d{4})-/i); return { year: m ? parseInt(m[1], 10) : null }; } export async function EntityListPage(props: EntityListPageProps) { const locale = (await getLocale()) === "en" ? "en" : "pt-br"; const rows = await pgQuery( `SELECT entity_class, entity_id, canonical_name, aliases, total_mentions, documents_count, summary_en, summary_pt_br, summary_status FROM public.entities WHERE entity_class = $1 AND total_mentions >= $2 AND canonical_name !~ '^(unspecified|unknown|n/a|—|UNKNOWN)$' ORDER BY (summary_status = 'ai_generated' OR summary_status = 'curated') DESC, total_mentions DESC, canonical_name ASC LIMIT 200`, [props.entityClass, props.min_mentions ?? 1], ).catch(() => [] as EntityRow[]); const title = locale === "en" ? props.title_en : props.title_pt; const subtitle = locale === "en" ? props.subtitle_en : props.subtitle_pt; const variant = props.variant ?? "magazine"; return (
{locale === "en" ? "// The archive" : "// O arquivo"}

{title}

{subtitle}

{rows.length} {locale === "en" ? "entries" : "entradas"}

{rows.length === 0 ? (

{locale === "en" ? "No entries yet — the corpus is still being indexed." : "Sem entradas ainda — o corpus está sendo indexado."}

) : variant === "magazine" ? ( ) : ( )}
{/* JSON-LD ItemList for GEO */}