import { useMemo, useState } from "react"; import { AlertIcon, SearchIcon } from "../ui/Icons"; import { Spinner } from "../ui/Spinner"; import { Badge } from "../ui/Badge"; const COLUMNS = [ { key: "title", label: "Título" }, { key: "journal", label: "Revista / Fuente" }, { key: "publication_year", label: "Año" }, { key: "doi", label: "DOI" }, { key: "type", label: "Tipo" }, ]; function SortIcon({ active, direction }) { const path = direction === "asc" || !active ? "M6 8L3 5h6z" : "M6 4l3 3H3z"; return ( ); } function sortPublications(rows, key, direction) { const sorted = [...rows].sort((a, b) => { const va = a[key]; const vb = b[key]; const cmp = typeof va === "string" ? va.localeCompare(vb) : (va ?? 0) - (vb ?? 0); return direction === "asc" ? cmp : -cmp; }); return sorted; } /** * Publications table. Owns only UI-state (filter + sort). Data, loading and * error states are driven by the parent page so retries and toasts can be * handled in one place. */ export function PublicationsTable({ publications, loading = false, error = null, onRetry, }) { const [filter, setFilter] = useState(""); const [sortKey, setSortKey] = useState("publication_year"); const [sortDir, setSortDir] = useState("desc"); const filtered = useMemo(() => { const needle = filter.trim().toLowerCase(); const rows = needle ? publications.filter( (p) => p.title.toLowerCase().includes(needle) || p.journal.toLowerCase().includes(needle) || String(p.publication_year).includes(needle), ) : publications; return sortPublications(rows, sortKey, sortDir); }, [publications, filter, sortKey, sortDir]); function toggleSort(key) { if (sortKey === key) { setSortDir((d) => (d === "asc" ? "desc" : "asc")); } else { setSortKey(key); setSortDir("desc"); } } return (
{/* Toolbar */}

Publicaciones

{filtered.length} de {publications.length} resultados

setFilter(e.target.value)} className="w-[220px] rounded-lg border border-surface-border-strong bg-surface-secondary py-2 pl-9 pr-3.5 text-[13px] text-ink-primary outline-none focus:border-brand-accent" />
{/* Body */}
{error ? ( ) : loading ? ( ) : ( {COLUMNS.map((col) => ( ))} {filtered.length === 0 ? ( ) : ( filtered.map((pub, i) => ( )) )}
toggleSort(col.key)} className="select-none whitespace-nowrap border-b border-surface-border/60 px-4 py-2.5 text-left text-xs font-medium tracking-wide text-ink-secondary" > {col.label.toUpperCase()}
No se encontraron publicaciones con ese filtro.
{pub.title} {pub.journal} {pub.publication_year} {pub.doi}
)}
); } function LoadingState() { return (

Cargando publicaciones…

); } function ErrorState({ error, onRetry }) { return (

No se pudieron cargar las publicaciones

{error?.message ?? "Error desconocido."}

{onRetry && ( )}
); }