"use client"; import { PageHeader, PageSection, Stat, EmptyState, Loading, ErrorState } from "./page-shell"; import { useApi } from "./use-api"; import type { LucideIcon } from "lucide-react"; function isObject(v: unknown): v is Record { return typeof v === "object" && v !== null && !Array.isArray(v); } function renderValue(v: unknown): React.ReactNode { if (v === null || v === undefined) return ; if (typeof v === "boolean") return v ? "Yes" : "No"; if (typeof v === "string" || typeof v === "number") return String(v); if (Array.isArray(v)) { if (v.length === 0) return None; return ( ); } if (isObject(v)) return ; return String(v); } function ObjectTable({ obj }: { obj: Record }) { const entries = Object.entries(obj).filter(([, v]) => typeof v !== "function"); if (entries.length === 0) return Empty; return (
{entries.map(([k, v]) => ( ))}
{k.replace(/([A-Z])/g, " $1").replace(/_/g, " ")} {renderValue(v)}
); } export function RecordList({ items, keyFn, render, empty, }: { items: any[]; keyFn: (item: any) => string; render: (item: any) => React.ReactNode; empty?: React.ReactNode; }) { if (items.length === 0) return empty ||

No records found.

; return
{items.map((item) =>
{render(item)}
)}
; } export function GenericDataPage({ icon: Icon, title, subtitle, endpoint, listKey, renderItem, listItemKey, stats, renderOverview, hideOverview, }: { icon: LucideIcon; title: string; subtitle: string; endpoint: string; listKey?: string; listItemKey?: (item: any) => string; renderItem?: (item: any) => React.ReactNode; stats?: { label: string; path: string }[]; renderOverview?: (data: any) => React.ReactNode; hideOverview?: boolean; }) { const { data, loading, error, refetch } = useApi(endpoint); if (loading) return ; if (error) return ; const list = listKey && data ? data[listKey] : data; const values = stats ? stats.map((s: any) => { const v = s.path.split(".").reduce((acc: any, k: string) => acc?.[k], data); return { ...s, value: v }; }) : []; return (
{values.length > 0 && (
{values.map((s: any) => ( ))}
)} {listKey && Array.isArray(list) ? ( l.toUpperCase())} className="mt-6"> {list.length === 0 ? ( ) : (
{list.map((item: any) => (
{renderItem ? renderItem(item) : }
))}
)}
) : !hideOverview ? ( {renderOverview ? renderOverview(data) : } ) : null}
); }