# AkiTable

It supports pagination, row selection, bulk actions, editable cells, and custom headers/footers.

### <mark style="color:red;">Installation</mark>

```bash
pnpm add @akinon/akitable
```

### <mark style="color:red;">Basic Usage</mark>

```tsx
import { Akitable, type AkitableColumn, type AkitableData } from '@akinon/akitable';
import { useAkilocale } from '@akinon/akilocale';

interface Product {
  id: number;
  name: string;
  price: number;
  status: string;
}

export const ProductTable = () => {
  const { locale } = useAkilocale();

  const columns: AkitableColumn[] = [
    {
      title: 'Name',
      dataIndex: 'name',
      key: 'name'
    },
    {
      title: 'Price',
      dataIndex: 'price',
      key: 'price',
      render: (value: number) => 
        new Intl.NumberFormat(locale.lng, { style: 'currency', currency: 'USD' }).format(value)
    },
  {
    title: 'Status',
    dataIndex: 'status',
    key: 'status'
  }
];

  const data: Product[] = [
    { id: 1, name: 'Product A', price: 29.99, status: 'Active' },
    { id: 2, name: 'Product B', price: 49.99, status: 'Inactive' },
    { id: 3, name: 'Product C', price: 19.99, status: 'Active' }
  ];

  return (
    <Akitable
      columns={columns}
      data={data}
      rowKey="id"
    />
  );
};
```

### <mark style="color:red;">Props</mark>

| Prop                  | Type                                                                 | Description                                                                       |
| --------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `columns`             | `AkitableColumn[]`                                                   | Column definitions for the table                                                  |
| `data`                | `AkitableData[] \| AkitablePaginatedData`                            | Data source (array or paginated response)                                         |
| `rowKey`              | `string`                                                             | Unique key field for each row                                                     |
| `actions`             | `AkitableAction[]`                                                   | Bulk actions for selected rows                                                    |
| `header`              | `AkitableHeaderProps`                                                | Header configuration                                                              |
| `footer`              | `AkitableFooterProps`                                                | Footer configuration                                                              |
| `isLoading`           | `boolean`                                                            | Loading state                                                                     |
| `maxColumnContent`    | `boolean`                                                            | Set max-content for horizontal scroll (default: `true`)                           |
| `pagination`          | `AkitablePaginationProps`                                            | Pagination configuration                                                          |
| `onPaginationChanged` | `(page: number, size: number) => void`                               | Pagination change callback                                                        |
| `onRowClick`          | `(record, event?, rowIndex?) => void`                                | Row click callback                                                                |
| `onRowEdit`           | `(modifiedRecord, payload) => void \| Promise<void>`                 | Row edit callback                                                                 |
| `onChangeSorter`      | `(sorter) => void`                                                   | Sorter change callback                                                            |
| `paginationShowTotal` | `(total: number, range: [number, number]) => ReactNode \| undefined` | Custom render function for the pagination total label (e.g. "1-20 of 100 items"). |

### <mark style="color:red;">Pagination</mark>

Akitable supports server-side pagination with a standardized paginated response format:

```tsx
import { useState, useEffect } from 'react';
import { 
  Akitable, 
  type AkitablePaginatedData, 
  type AkitablePaginationProps 
} from '@akinon/akitable';

export const PaginatedTable = () => {
  const [data, setData] = useState<AkitablePaginatedData>({
    count: 0,
    results: []
  });
  const [pagination, setPagination] = useState<AkitablePaginationProps>({
    page: 1,
    size: 20
  });

  useEffect(() => {
    fetchData(pagination.page, pagination.size);
  }, [pagination]);

  const fetchData = async (page: number, size: number) => {
    const response = await api.getProducts({ page, size });
    setData(response); // { count: 100, results: [...] }
  };

  const handlePaginationChange = (page: number, size: number) => {
    setPagination({ page, size });
  };

  return (
    <Akitable
      columns={columns}
      data={data}
      rowKey="id"
      pagination={pagination}
      onPaginationChanged={handlePaginationChange}
    />
  );
};
```

#### Paginated Data Format

```tsx
interface AkitablePaginatedData {
  count: number;           // Total number of items
  next?: string | null;    // URL for next page (optional)
  previous?: string | null;// URL for previous page (optional)
  results: AkitableData[]; // Current page items
}
```

#### Page Sizes

Available page sizes: `20`, `50`, `100`, `250`

### <mark style="color:red;">Column Configuration</mark>

Columns extend Ant Design's `TableColumnType` with additional properties:

```tsx
interface AkitableColumn extends TableColumnType<AkitableData> {
  copyable?: boolean;  // Enable copy to clipboard
  editable?: boolean;  // Enable inline editing
}
```

#### Basic Columns

```tsx
const columns: AkitableColumn[] = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
    width: 200
  },
  {
    title: 'Email',
    dataIndex: 'email',
    key: 'email',
    copyable: true  // Show copy button
  },
  {
    title: 'Created At',
    dataIndex: 'createdAt',
    key: 'createdAt',
    render: (date: string) => new Date(date).toLocaleDateString()
  }
];
```

#### Sortable Columns

```tsx
const columns: AkitableColumn[] = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
    sorter: true  // Enable sorting
  }
];

const handleSorterChange = (sorter: SorterResult | SorterResult[]) => {
  // sorter.field, sorter.order ('ascend' | 'descend' | null)
  console.log('Sort changed:', sorter);
};

<Akitable
  columns={columns}
  data={data}
  rowKey="id"
  onChangeSorter={handleSorterChange}
/>
```

#### Editable Columns

```tsx
const columns: AkitableColumn[] = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
    editable: true  // Enable inline editing
  },
  {
    title: 'Price',
    dataIndex: 'price',
    key: 'price',
    editable: true
  }
];

const handleRowEdit = async (record: AkitableData, payload: AkitableData) => {
  // record: original row data
  // payload: edited field values
  await api.updateProduct(record.id, payload);
};

<Akitable
  columns={columns}
  data={data}
  rowKey="id"
  onRowEdit={handleRowEdit}
/>
```

### <mark style="color:red;">Bulk Actions</mark>

Add bulk actions for selected rows:

```tsx
import type { AkitableAction } from '@akinon/akitable';

const actions: AkitableAction[] = [
  {
    label: 'Delete Selected',
    onSelect: (selectedRowKeys: React.Key[]) => {
      console.log('Deleting:', selectedRowKeys);
      // Handle bulk delete
    }
  },
  {
    label: 'Export Selected',
    onSelect: (selectedRowKeys: React.Key[]) => {
      console.log('Exporting:', selectedRowKeys);
      // Handle bulk export
    }
  }
];

<Akitable
  columns={columns}
  data={data}
  rowKey="id"
  actions={actions}
/>
```

When actions are provided, row selection checkboxes appear automatically.

### <mark style="color:red;">Header Configuration</mark>

Customize the table header with title, extra content, and download actions:

```tsx
import { Button } from '@akinon/ui-button';

<Akitable
  columns={columns}
  data={data}
  rowKey="id"
  pagination={pagination}
  header={{
    title: 'Products',
    extra: (
      <Button type="primary" onClick={handleAddProduct}>
        Add Product
      </Button>
    ),
    downloadActions: (
      <>
        <Button onClick={handleExportCsv}>CSV</Button>
        <Button onClick={handleExportXls}>Excel</Button>
      </>
    ),
    downloadActionsSlotWidth: 120,
    reserveDownloadActionsSpace: true
  }}
/>
```

#### Header Props

<table><thead><tr><th width="299.84765625">Prop</th><th width="137.19921875">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>title</code></td><td><code>string</code></td><td>Table title (displays with total count)</td></tr><tr><td><code>extra</code></td><td><code>ReactNode</code></td><td>Additional content (buttons, filters, etc.)</td></tr><tr><td><code>downloadActions</code></td><td><code>ReactNode</code></td><td>Download/export action buttons</td></tr><tr><td><code>downloadActionsSlotWidth</code></td><td><code>number</code></td><td>Reserved width for download actions slot</td></tr><tr><td><code>reserveDownloadActionsSpace</code></td><td><code>boolean</code></td><td>Keep slot width when empty (default: <code>true</code>)</td></tr></tbody></table>

### <mark style="color:red;">Footer Configuration</mark>

Add custom content to the table footer:

```tsx
import { useAkilocale } from '@akinon/akilocale';

const TableWithFooter = () => {
  const { locale } = useAkilocale();
  const totalAmount = 1234.56;

  return (
    <Akitable
      columns={columns}
      data={data}
      rowKey="id"
      footer={{
        extra: (
          <div>
            <span>
              Total: {
                new Intl.NumberFormat(locale.lng, { style: 'currency', currency: 'USD' })
                  .format(totalAmount)
                }
            </span>
          </div>
        )
      }}
    />
  );
};
```

### <mark style="color:red;">Row Events</mark>

#### Row Click

```tsx
const handleRowClick = (
  record: AkitableData,
  event?: React.MouseEvent<HTMLElement>,
  rowIndex?: number
) => {
  console.log('Row clicked:', record);
  // Navigate to detail page, open modal, etc.
};

<Akitable
  columns={columns}
  data={data}
  rowKey="id"
  onRowClick={handleRowClick}
/>
```

### <mark style="color:red;">Row Status</mark>

Rows can have visual status indicators:

```tsx
import { AkitableRowStatus } from '@akinon/akitable';

const data = [
  { id: 1, name: 'Processing...', rowStatus: AkitableRowStatus.PENDING },
  { id: 2, name: 'Failed item', rowStatus: AkitableRowStatus.ERROR },
  { id: 3, name: 'Normal item' }
];
```

Available statuses:

* `AkitableRowStatus.PENDING` - Shows pending/loading state
* `AkitableRowStatus.ERROR` - Shows error state

### <mark style="color:red;">Loading State</mark>

```tsx
const [isLoading, setIsLoading] = useState(false);

<Akitable
  columns={columns}
  data={data}
  rowKey="id"
  isLoading={isLoading}
/>
```

### <mark style="color:red;">Complete Example</mark>

```tsx
import { useState, useEffect } from 'react';
import { 
  Akitable, 
  type AkitableColumn, 
  type AkitablePaginatedData,
  type AkitablePaginationProps,
  type AkitableAction,
  type SorterResult
} from '@akinon/akitable';
import { Button } from '@akinon/ui-button';
import { useAkilocale } from '@akinon/akilocale';

interface Order {
  id: number;
  orderNumber: string;
  customer: string;
  total: number;
  status: string;
  createdAt: string;
}

export const OrdersTable = () => {
  const { locale } = useAkilocale();

  const columns: AkitableColumn[] = [
    {
      title: 'Order Number',
      dataIndex: 'orderNumber',
      key: 'orderNumber',
      copyable: true
    },
    {
      title: 'Customer',
      dataIndex: 'customer',
      key: 'customer',
      sorter: true
    },
    {
      title: 'Total',
      dataIndex: 'total',
      key: 'total',
      render: (value: number) => 
        new Intl.NumberFormat(locale.lng, { style: 'currency', currency: 'USD' }).format(value),
      sorter: true
    },
  {
    title: 'Status',
    dataIndex: 'status',
    key: 'status'
  },
  {
    title: 'Created At',
    dataIndex: 'createdAt',
    key: 'createdAt',
    render: (date: string) => new Date(date).toLocaleDateString(),
    sorter: true
  }
  ];

  const [data, setData] = useState<AkitablePaginatedData>({
    count: 0,
    results: []
  });
  const [pagination, setPagination] = useState<AkitablePaginationProps>({
    page: 1,
    size: 20
  });
  const [isLoading, setIsLoading] = useState(false);
  const [sorter, setSorter] = useState<SorterResult | null>(null);

  useEffect(() => {
    fetchOrders();
  }, [pagination, sorter]);

  const fetchOrders = async () => {
    setIsLoading(true);
    try {
      const response = await api.getOrders({
        page: pagination.page,
        size: pagination.size,
        sortField: sorter?.field,
        sortOrder: sorter?.order
      });
      setData(response);
    } finally {
      setIsLoading(false);
    }
  };

  const handlePaginationChange = (page: number, size: number) => {
    setPagination({ page, size });
  };

  const handleSorterChange = (newSorter: SorterResult | SorterResult[]) => {
    const result = Array.isArray(newSorter) ? newSorter[0] : newSorter;
    setSorter(result);
  };

  const handleRowClick = (record: Order) => {
    // Navigate to order detail
    router.push(`/orders/${record.id}`);
  };

  const actions: AkitableAction[] = [
    {
      label: 'Mark as Shipped',
      onSelect: async (selectedKeys) => {
        await api.bulkUpdateStatus(selectedKeys, 'shipped');
        fetchOrders();
      }
    },
    {
      label: 'Delete',
      onSelect: async (selectedKeys) => {
        await api.bulkDelete(selectedKeys);
        fetchOrders();
      }
    }
  ];

  return (
    <Akitable
      columns={columns}
      data={data}
      rowKey="id"
      isLoading={isLoading}
      pagination={pagination}
      onPaginationChanged={handlePaginationChange}
      onChangeSorter={handleSorterChange}
      onRowClick={handleRowClick}
      actions={actions}
      header={{
        title: 'Orders',
        extra: (
          <Button type="primary" onClick={() => router.push('/orders/new')}>
            Create Order
          </Button>
        ),
        downloadActions: (
          <Button onClick={handleExportOrders}>Export</Button>
        )
      }}
    />
  );
};
```

### <mark style="color:red;">Internationalization</mark>

Akitable uses `@akinon/akilocale` for built-in translations. Default labels (pagination, actions, etc.) are provided in English and Turkish.

### <mark style="color:red;">Best Practices</mark>

1. **Always provide rowKey** - Use a unique identifier field for proper row tracking
2. **Use server-side pagination** - For large datasets, implement pagination with `AkitablePaginatedData`
3. **Handle loading states** - Show loading indicator during data fetches
4. **Define column widths** - Prevent layout shifts with fixed column widths
5. **Use bulk actions wisely** - Provide clear, actionable labels for bulk operations


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.akinon.com/akinon-ui/ui-kit/components/akitable.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
