import type { ReactNode } from 'react'; import { FilterBar } from '@/components/listing/filters'; import { EntityRow } from '@/components/ui/entity'; import { Pagination, withParams } from '@/components/ui/pagination'; import { Container, PageHeader } from '@/components/ui/section'; import { EmptyState, Unavailable } from '@/components/ui/unavailable'; import { api, safe } from '@/lib/api'; import { fmtInt } from '@/lib/format'; import type { EntitySummary, Page } from '@/lib/types'; const LIMIT = 40; /** * Generic paged listing for a type with light filters (q, org, sort). `fetch` defaults to /explore/; * pass a custom fetcher for typed endpoints (/papers, /hardware). Renders `EntityRow`s — dense, mobile-safe. */ export async function GenericListing({ type, basePath, title, eyebrow, lede, searchParams, fetch, sorts = [{ value: 'updated', label: 'Recently updated' }, { value: 'name', label: 'Name' }], extraFields = [], children, rowTrailing, headerAside }: { type: string; basePath: string; title: string; eyebrow: string; lede?: string; searchParams: Record; fetch?: (q: Record) => Promise>; sorts?: { value: string; label: string }[]; extraFields?: Parameters[0]['fields']; children?: ReactNode; /** Optional per-row trailing node (e.g. a CompareButton). */ rowTrailing?: (e: EntitySummary) => ReactNode; /** Extra content in the page header aside, next to the total. */ headerAside?: ReactNode }) { const current: Record = {}; for (const k of ['q', 'org', 'sort', 'category', 'kind', 'manufacturer', 'since', 'offset']) if (searchParams[k]) current[k] = searchParams[k]; const offset = Math.max(0, Number(current.offset) || 0); const query = { ...current, limit: LIMIT, offset }; const page = await safe(fetch ? fetch(query) : api.explore(type, query)); const href = (patch: Record) => withParams(basePath, current, patch); return ( {page &&

{fmtInt(page.total)} total

}{headerAside} : undefined}>
{children}
{!page ? ( ) : page.items.length === 0 ? ( Try another name or remove filters. ) : ( <>
    {page.items.map((e) => ( ))}
href({ offset: o || undefined })} className="mt-4" /> )}
); }