# DataTable

> Bảng dữ liệu generic có sort · paginate · row select · custom render. Generic over row type `T`.

**Archetype mapping:** A (Dashboard) · B (Tool) — bảng là primitive chính cho mọi admin/SaaS analytics.

## API

| Prop | Type | Default | Description |
|---|---|---|---|
| `columns` | `DataTableColumn<T>[]` | required | Định nghĩa cột |
| `data` | `T[]` | required | Mảng row data |
| `rowKey` | `(row, i) => string` | `row.id ?? i` | Function lấy unique key cho row |
| `selectedKeys` | `string[]` | — | Controlled — nếu pass, hiện checkbox cột đầu |
| `onSelect` | `(keys: string[]) => void` | — | Callback khi user select/deselect row |
| `onSort` | `(key, dir) => void` | — | Callback khi user click sort header (notify server-side) |
| `pageSize` | `number` | `10` | Số row/page (client-side pagination) |
| `empty` | `ReactNode` | `'No data'` | Render khi `data` rỗng |
| `className` | `string` | — | Override wrapper |

### `DataTableColumn<T>`

| Field | Type | Description |
|---|---|---|
| `key` | `string` | Key trong row data hoặc identifier khi render custom |
| `label` | `ReactNode` | Header text |
| `sortable` | `boolean?` | Bật sort cho cột này |
| `align` | `'left' \| 'right' \| 'center'` | Căn cell + header |
| `width` | `string?` | CSS width (vd `'120px'`, `'20%'`) |
| `render` | `(row: T) => ReactNode` | Custom render cell (override `row[key]`) |

## Variants

Không có variant style — single layout. Variant logic qua props:

- Without `selectedKeys` → không có checkbox column
- Without sortable columns → không có sort UI
- `data.length ≤ pageSize` → không render pagination footer

## States

- **Default**: bảng có data
- **Empty**: cell duy nhất span toàn cột với `empty` prop
- **Sorted ascending**: header có `↑`
- **Sorted descending**: header có `↓`
- **Unsorted sortable**: header có `↕`
- **Row selected**: bg-primary-soft
- **Page out of range**: clamp về totalPages

## Sort · Filter · Pagination integration

- **Sort**: client-side (in-memory `sorted` memo). Pass `onSort` để mirror sang server query nếu cần.
- **Pagination**: client-side, tự render footer `Prev / X / Y / Next` khi `data > pageSize`. Cho server-side pagination, set `pageSize` lớn (vd `Infinity`) và xử lý paging ở caller, dùng [[Pagination]] component bên ngoài.
- **Filter**: KHÔNG built-in. Caller filter `data` trước khi pass vào (vd kết hợp với [[SearchBar]] hoặc [[Dropdown]] filter).

## Usage

```tsx
import { DataTable } from '@cowork/ui';

type Order = { id: string; customer: string; total: number; status: string };

const columns: DataTableColumn<Order>[] = [
  { key: 'id', label: 'Mã đơn', width: '100px' },
  { key: 'customer', label: 'Khách', sortable: true },
  { key: 'total', label: 'Tổng', align: 'right', sortable: true,
    render: (r) => `${r.total.toLocaleString('vi-VN')} đ` },
  { key: 'status', label: 'Trạng thái',
    render: (r) => <Badge variant={statusVariant(r.status)}>{r.status}</Badge> },
];

const [selected, setSelected] = useState<string[]>([]);

<DataTable
  columns={columns}
  data={orders}
  rowKey={(r) => r.id}
  selectedKeys={selected}
  onSelect={setSelected}
  pageSize={20}
  empty="Chưa có đơn hàng nào"
/>
```

## Real-world example

- An Nhien admin: bảng đơn hàng với cột `Mã / Khách / SP / Tổng / Trạng thái / Action`. Render Action cột cuối là [[Dropdown]] menu. Select nhiều đơn → bulk export Excel.
- KHI CRM members: bảng members với cột `Tên / SĐT / Lifecycle / Gói / Last active`. Sort theo `last_active` desc.
- A-Kryphan analytics: bảng bài viết với cột `Title / Views / CTR / Published`. Sort theo Views desc.

## Accessibility

- `<table>` native — screen reader đọc đúng row/cell relation
- Sort button trong header có focus ring (`focus-visible:ring-2 focus-visible:ring-primary-ring`)
- Select-all checkbox có `aria-label="Select all"`
- Row checkbox có `aria-label="Select row N"`
- Pagination buttons có rõ label "Prev" / "Next"

## Responsive behavior

- `overflow-x-auto` trên wrapper → bảng tự scroll ngang khi mobile
- Cell padding `px-4 py-3` giữ constant
- Pagination footer wrap nếu hẹp
- Cân nhắc: với 6+ columns, mobile UX kém — caller nên switch sang Card list view ở breakpoint sm

## Related

- [[Pagination]] — dùng standalone khi muốn server-side paging
- [[SearchBar]] — filter `data` trước khi pass vào DataTable
- [[Dropdown]] — filter dropdown gần DataTable, row action menu
- [[Card]] — alternative layout cho mobile khi DataTable không fit

## Source

`react/src/organisms/DataTable.tsx`
