// --------------------------------------------------------------------------- // DataTable — pre-built sortable, paginated table wired to AnyDB // --------------------------------------------------------------------------- // PRE-INSTALLED. CodeWriterAgent just uses: // import { DataTable } from "@/components/DataTable" // `$${(v/100).toFixed(2)}` }, // { key: 'status', label: 'Status', render: (v) => {v} }, // { key: 'created_at', label: 'Date', render: (v) => new Date(v).toLocaleDateString() }, // ]} // filter={{ status: 'active' }} // pageSize={10} // searchField="customer" // /> // --------------------------------------------------------------------------- import { useState, useMemo } from "react"; import { useCollection } from "@/hooks/use-anydb"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { ChevronUp, ChevronDown, Search, ChevronLeft, ChevronRight } from "lucide-react"; interface Column { key: string; label: string; sortable?: boolean; render?: (value: any, row: T) => React.ReactNode; className?: string; } interface DataTableProps { collection: string; columns: Column[]; filter?: Record; pageSize?: number; searchField?: string; searchPlaceholder?: string; onRowClick?: (row: T) => void; emptyMessage?: string; className?: string; } export function DataTable = any>({ collection, columns, filter = {}, pageSize = 10, searchField, searchPlaceholder = "Search...", onRowClick, emptyMessage = "No data found", className = "", }: DataTableProps) { const [page, setPage] = useState(0); const [sortKey, setSortKey] = useState(null); const [sortDir, setSortDir] = useState<1 | -1>(-1); const [search, setSearch] = useState(""); const queryFilter = useMemo(() => { const f = { ...filter }; if (searchField && search.trim()) { f[searchField] = { $regex: search.trim(), $options: "i" }; } return f; }, [filter, searchField, search]); const sort = sortKey ? { [sortKey]: sortDir } : { created_at: -1 as const }; const { data, total, loading, error } = useCollection(collection, { filter: queryFilter, sort, limit: pageSize, skip: page * pageSize, }); const totalPages = Math.ceil(total / pageSize); const handleSort = (key: string) => { if (sortKey === key) { setSortDir(d => (d === 1 ? -1 : 1)); } else { setSortKey(key); setSortDir(-1); } setPage(0); }; if (error) { return (

Failed to load data: {error}

); } return (
{/* Search bar */} {searchField && (
{ setSearch(e.target.value); setPage(0); }} className="pl-9 h-10" />
)} {/* Table */}
{columns.map((col) => ( ))} {loading ? ( Array.from({ length: pageSize }).map((_, i) => ( {columns.map((col) => ( ))} )) ) : data.length === 0 ? ( ) : ( data.map((row, i) => ( onRowClick?.(row)} > {columns.map((col) => ( ))} )) )}
col.sortable !== false && handleSort(col.key)} >
{col.label} {col.sortable !== false && sortKey === col.key && ( sortDir === 1 ? : )}
{emptyMessage}
{col.render ? col.render(row[col.key], row) : String(row[col.key] ?? "")}
{/* Pagination */} {totalPages > 1 && (

Showing {page * pageSize + 1}–{Math.min((page + 1) * pageSize, total)} of {total}

{page + 1} / {totalPages}
)}
); }