import { Search } from "lucide-react"; import { useMemo, useState } from "react"; type Row = Record; type FilterKey = "all" | "aptos" | "no_aptos"; interface Props { rows: Row[]; /** Resumen opcional del backend; si falta se calcula desde filas. */ summary?: { total?: number; paso_bot?: number; con_error?: number; duplicados?: number; }; } const PREFERRED_COLUMNS = [ "radicado", "total_cargos", "autorizacion", "ingreso", "tipo_documento", "documento", "tipo_servicio", "copago", "cuota_moderadora", "OBSERVACION", "archivo_origen", ]; const COLUMN_LABELS: Record = { radicado: "Radicado", total_cargos: "Bruto", autorizacion: "Autorización", ingreso: "Fecha", tipo_documento: "Tipo ID", documento: "Documento", tipo_servicio: "Servicio", copago: "Copago", cuota_moderadora: "Moderadora", OBSERVACION: "Observación", archivo_origen: "Origen", tipo_archivo: "Tipo archivo", }; const HIDDEN_COLUMNS = new Set(["cleaning_job_id", "tipo_archivo"]); function cellValue(row: Row, col: string): string { const v = row[col]; if (v === null || v === undefined) return ""; return String(v); } function observacionOf(row: Row): string { return cellValue(row, "OBSERVACION").trim(); } function isApto(row: Row): boolean { return observacionOf(row) === "PASO_BOT"; } function isDuplicado(row: Row): boolean { return observacionOf(row).includes("RADICADO_DUPLICADO"); } function orderColumns(rows: Row[]): string[] { const present = new Set(rows.flatMap((r) => Object.keys(r))); const ordered: string[] = []; for (const c of PREFERRED_COLUMNS) { if (present.has(c) && !HIDDEN_COLUMNS.has(c)) ordered.push(c); } for (const c of present) { if (!ordered.includes(c) && !HIDDEN_COLUMNS.has(c)) ordered.push(c); } return ordered; } function ObsBadge({ value }: { value: string }) { if (!value) return —; const apto = value === "PASO_BOT"; const dup = value.includes("RADICADO_DUPLICADO"); const cls = apto ? "pa-obs-badge pa-obs-badge--apto" : dup ? "pa-obs-badge pa-obs-badge--dup" : "pa-obs-badge pa-obs-badge--error"; return {value}; } export function PoblacionAptaResultsTable({ rows, summary: summaryProp }: Props) { const [filter, setFilter] = useState("all"); const [search, setSearch] = useState(""); const columns = useMemo(() => orderColumns(rows), [rows]); const summary = useMemo(() => { const aptos = rows.filter(isApto).length; const dups = rows.filter(isDuplicado).length; return { total: summaryProp?.total ?? rows.length, aptos: summaryProp?.paso_bot ?? aptos, noAptos: summaryProp?.con_error ?? rows.length - aptos, duplicados: summaryProp?.duplicados ?? dups, }; }, [rows, summaryProp]); const filtered = useMemo(() => { let list = rows; if (filter === "aptos") list = list.filter(isApto); else if (filter === "no_aptos") list = list.filter((r) => !isApto(r)); const q = search.trim().toLowerCase(); if (q) { list = list.filter((r) => columns.some((c) => cellValue(r, c).toLowerCase().includes(q)), ); } return list; }, [rows, columns, filter, search]); const FILTERS: { key: FilterKey; label: string; count: number }[] = [ { key: "all", label: "Todos", count: summary.total }, { key: "aptos", label: "Aptos", count: summary.aptos }, { key: "no_aptos", label: "No aptos", count: summary.noAptos }, ]; return (
{summary.total} Total
{summary.aptos} Aptos
{summary.noAptos} No aptos
{summary.duplicados > 0 && (
{summary.duplicados} Duplicados
)}
{FILTERS.map(({ key, label, count }) => ( ))}
setSearch(e.target.value)} />
{columns.map((c) => ( ))} {filtered.length === 0 ? ( ) : ( filtered.map((row, i) => { const apto = isApto(row); return ( {columns.map((c) => ( ))} ); }) )}
{COLUMN_LABELS[c] ?? c}
{search ? "No hay filas que coincidan con la búsqueda." : "No hay filas en esta categoría."}
{c === "OBSERVACION" ? ( ) : cellValue(row, c).trim() === "" ? ( — ) : ( cellValue(row, c) )}
{filtered.length !== rows.length && (

Mostrando {filtered.length} de {rows.length} filas

)}
); }