// aerOS — data display: DataTable + StatCard. Exports to window. const { useState: _dS, useMemo: _dM } = React; /* ----------------------------- StatCard ----------------------------- */ function StatCard({ label, value, unit, delta, icon, spark, tone = 'brand', sub }) { const up = delta != null && delta >= 0; return (
{label} {icon && {typeof icon === 'function' ? icon({ size: 17 }) : icon}}
{value}{unit && {unit}}
{(delta != null || sub) && (
{delta != null && ( {(up ? Icon.trending : Icon.trending)({ size: 13, style: { transform: up ? 'none' : 'scaleY(-1)' } })} {up ? '+' : ''}{delta}% )} {sub && {sub}}
)}
{spark &&
}
); } /* ----------------------------- DataTable ----------------------------- */ // columns: [{ key, label, align, sortable, render, width, sortValue }] function DataTable({ columns, rows, searchKeys, filters, pageSize = 8, onRowClick, empty, dense, rightTools, searchPlaceholder = 'Ara…', initialSort }) { const [q, setQ] = _dS(''); const [page, setPage] = _dS(1); const [sort, setSort] = _dS(initialSort || null); // {key, dir} const [filterVals, setFilterVals] = _dS({}); const filtered = _dM(() => { let r = rows; if (q && searchKeys) { const ql = q.toLowerCase(); r = r.filter(row => searchKeys.some(k => String(row[k] ?? '').toLowerCase().includes(ql))); } if (filters) { for (const f of filters) { const v = filterVals[f.key]; if (v != null && v !== '') r = r.filter(row => String(row[f.key]) === String(v)); } } if (sort) { const col = columns.find(c => c.key === sort.key); r = [...r].sort((a, b) => { const av = col?.sortValue ? col.sortValue(a) : a[sort.key]; const bv = col?.sortValue ? col.sortValue(b) : b[sort.key]; if (av == null) return 1; if (bv == null) return -1; if (typeof av === 'number' && typeof bv === 'number') return sort.dir === 'asc' ? av - bv : bv - av; return sort.dir === 'asc' ? String(av).localeCompare(String(bv), 'tr') : String(bv).localeCompare(String(av), 'tr'); }); } return r; }, [rows, q, sort, filterVals, columns, searchKeys, filters]); const pages = Math.max(1, Math.ceil(filtered.length / pageSize)); const curPage = Math.min(page, pages); const pageRows = filtered.slice((curPage - 1) * pageSize, curPage * pageSize); const toggleSort = (key) => { setSort(s => s?.key === key ? (s.dir === 'asc' ? { key, dir: 'desc' } : null) : { key, dir: 'asc' }); }; React.useEffect(() => { setPage(1); }, [q, filterVals]); const cellPad = dense ? '9px 14px' : '13px 16px'; return (
{(searchKeys || filters || rightTools) && (
{searchKeys && (
)} {filters && filters.map(f => (