/** * EntityMentionChunks — live chunk list from public.entity_mentions for this entity. * * Server component. Returns up to `limit` chunks where this entity appears * (after 31-populate-entity-mentions.py has run). Empty state hides gracefully * — the markdown `mentioned_in[]` panel above is the static fallback. */ import Image from "next/image"; import Link from "next/link"; import { findEntity } from "@/lib/retrieval/graph"; import { pgQuery } from "@/lib/retrieval/db"; interface ChunkRow { chunk_pk: number; doc_id: string; chunk_id: string; page: number; type: string; bbox: { x: number; y: number; w: number; h: number } | null; content_en: string | null; content_pt: string | null; classification: string | null; } export async function EntityMentionChunks({ entityClassSingular, entityId, limit = 30, }: { entityClassSingular: string; entityId: string; limit?: number; }) { let rows: ChunkRow[] = []; try { const ent = await findEntity(entityClassSingular, entityId); if (!ent) return null; rows = await pgQuery( `SELECT c.chunk_pk, c.doc_id, c.chunk_id, c.page, c.type, c.bbox, c.content_en, c.content_pt, c.classification FROM public.entity_mentions em JOIN public.chunks c ON c.chunk_pk = em.chunk_pk WHERE em.entity_pk = $1 ORDER BY c.doc_id, c.order_global LIMIT $2`, [ent.entity_pk, limit], ); } catch { return null; } if (rows.length === 0) return null; return (

Live chunk mentions · {rows.length}

{rows.map((r) => { const cropUrl = r.bbox ? `/api/crop?doc=${encodeURIComponent(r.doc_id)}&page=${r.page}` + `&x=${r.bbox.x}&y=${r.bbox.y}&w=${r.bbox.w}&h=${r.bbox.h}&w_px=240` : null; const text = r.content_pt || r.content_en || ""; return ( {cropUrl && ( )}
{r.chunk_id} p{r.page} {r.type} {r.classification && {r.classification}}
{text}
{r.doc_id}
); })}
); }