{"id":27457672,"url":"https://github.com/jacksonkasi1/tnks-data-table","last_synced_at":"2025-04-15T19:48:11.775Z","repository":{"id":286752195,"uuid":"962410667","full_name":"jacksonkasi1/tnks-data-table","owner":"jacksonkasi1","description":"Advanced data table component with server-side operations using Hono.js, Drizzle ORM, and PostgreSQL","archived":false,"fork":false,"pushed_at":"2025-04-15T05:33:57.000Z","size":563,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-15T06:28:40.124Z","etag":null,"topics":["data-table","drizzle","filtering","hono","nextjs","postgresql","react","server-side-pagination","shadcn-ui","sorting","tailwindcss","tailwindcss-v4","typescript","ui-component","zod"],"latest_commit_sha":null,"homepage":"https://tnks-data-table.vercel.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jacksonkasi1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-08T05:51:43.000Z","updated_at":"2025-04-15T05:34:00.000Z","dependencies_parsed_at":"2025-04-15T06:28:54.765Z","dependency_job_id":null,"html_url":"https://github.com/jacksonkasi1/tnks-data-table","commit_stats":null,"previous_names":["jacksonkasi1/data-table","jacksonkasi1/tnks-data-table"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacksonkasi1%2Ftnks-data-table","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacksonkasi1%2Ftnks-data-table/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacksonkasi1%2Ftnks-data-table/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacksonkasi1%2Ftnks-data-table/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jacksonkasi1","download_url":"https://codeload.github.com/jacksonkasi1/tnks-data-table/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249144063,"owners_count":21219960,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-table","drizzle","filtering","hono","nextjs","postgresql","react","server-side-pagination","shadcn-ui","sorting","tailwindcss","tailwindcss-v4","typescript","ui-component","zod"],"created_at":"2025-04-15T19:48:07.840Z","updated_at":"2025-04-15T19:48:11.763Z","avatar_url":"https://github.com/jacksonkasi1.png","language":"TypeScript","funding_links":[],"categories":["Libs and Components","Components \u0026 Libraries"],"sub_categories":[],"readme":"# Advanced Data Table Component Documentation\n\n**Version:** 1.0.0  \n**Updated:** 2025-04-14  \n**Author:** Jackson Kasi\n\n## Table of Contents\n\n1. [Introduction](#introduction)\n2. [Features Overview](#features-overview)\n3. [File Structure](#file-structure)\n4. [Installation \u0026 Setup](#installation--setup)\n5. [Basic Usage](#basic-usage)\n6. [Core Components](#core-components)\n7. [API Integration](#api-integration)\n8. [Advanced Configuration](#advanced-configuration)\n   - [Column Configuration](#column-configuration)\n   - [Row Actions](#row-actions)\n   - [Filtering \u0026 Sorting](#filtering--sorting)\n   - [Pagination](#pagination)\n   - [Date Range Filtering](#date-range-filtering)\n   - [Row Selection](#row-selection)\n   - [Toolbar Customization](#toolbar-customization)\n   - [Export Options](#export-options)\n9. [Server Implementation](#server-implementation)\n   - [API Endpoints](#api-endpoints)\n   - [Request \u0026 Response Formats](#request--response-formats)\n   - [Error Handling](#error-handling)\n10. [Popups \u0026 Modals](#popups--modals)\n11. [Customization](#customization)\n12. [Performance Optimization](#performance-optimization)\n13. [Best Practices](#best-practices)\n14. [Troubleshooting](#troubleshooting)\n15. [Complete API Reference](#complete-api-reference)\n16. [Example Implementations](#example-implementations)\n\n---\n\n## Introduction\n\nThe Advanced Data Table component is a highly configurable and feature-rich table implementation built on top of Shadcn UI components and TanStack Table (React Table v8). It's designed to handle enterprise-level requirements including complex data operations, server-side processing, and customizable UI elements.\n\nThis documentation provides comprehensive guidance on how to implement, configure, and extend the data table for your specific needs.\n\n## Server\n\nCheck out the API development document to understand the default configuration for this table. [👉 Click here](./src/SERVER.md)\n\n### Key Benefits\n\n- **TypeScript Support**: Fully typed components for better developer experience\n- **Modular Architecture**: Easily extendable and customizable\n- **Server Integration**: Built-in support for server-side operations\n- **Accessibility**: Follows WCAG guidelines for accessible tables\n- **Performance Optimized**: Efficient rendering even with large datasets\n- **Responsive Design**: Works across various screen sizes\n- **Theming Support**: Customizable appearance with Tailwind CSS\n\n---\n\n## Features Overview\n\nThe Data Table includes the following features:\n\n### Data Management\n\n- ✅ Server-side pagination\n- ✅ Server-side sorting\n- ✅ Server-side filtering\n- ✅ Single \u0026 multi-row selection\n- ✅ Optimistic UI updates\n\n### UI Features\n\n- ✅ Responsive layout\n- ✅ Column resizing\n- ✅ Column visibility toggle\n- ✅ Date range filtering\n- ✅ Search functionality\n- ✅ Customizable toolbar\n- ✅ Row actions menu\n- ✅ Bulk action support\n\n### Operations\n\n- ✅ Add new records\n- ✅ Edit existing records\n- ✅ Delete single records\n- ✅ Bulk delete operations\n- ✅ Data export (CSV/Excel)\n\n### Integration\n\n- ✅ React Query data fetching\n- ✅ Zod validation\n- ✅ Form handling with React Hook Form\n- ✅ Toast notifications\n- ✅ URL state persistence\n\n---\n\n## File Structure\n\nThe data table implementation follows a modular structure to separate concerns and improve maintainability. Below is the recommended file structure for implementing the data table in your project:\n\n```sh\nsrc/\n├── api/                       # API integration layer\n│   └── entity/                # Entity-specific API functions\n│       ├── add-entity.ts      # Create operation\n│       ├── delete-entity.ts   # Delete operation\n│       ├── fetch-entities.ts  # List operation with filters\n│       └── fetch-entity-by-ids.ts # Fetch specific entities\n│\n├── components/                # Shared UI components\n└── 📁data-table               # Core data table components\n    └── 📁hooks                # Custom React hooks for data-table\n        └── use-table-column-resize.ts  # Hook for managing column resize state and persistence\n    └── 📁utils                # Utility functions and helpers\n        └── column-sizing.ts   # Functions for calculating and managing column widths\n        └── conditional-state.ts # Logic for conditional rendering and state transitions\n        └── date-format.ts     # Date formatting and manipulation utilities\n        └── deep-utils.ts      # Deep object comparison and manipulation\n        └── export-utils.ts    # Utilities for data export (CSV/Excel)\n        └── index.ts           # Export barrel file for utilities\n        └── keyboard-navigation.ts # Keyboard navigation and accessibility\n        └── search.ts          # Search functionality and text matching\n        └── table-config.ts    # Table configuration types and defaults\n        └── table-state-handlers.ts # Handlers for table state changes\n        └── url-state.ts       # URL state persistence utilities\n    └── column-header.tsx      # Sortable column header component\n    └── data-export.tsx        # Component for export functionality UI\n    └── data-table-resizer.tsx # Column resize handler component\n    └── data-table.tsx         # Main data table component\n    └── pagination.tsx         # Pagination controls component\n    └── toolbar.tsx            # Table toolbar with search and filtering\n    └── view-options.tsx       # Column visibility and display options\n│\n├── app/                       # Application routes and pages\n│   └── (section)/             # Section grouping\n│       └── entity-table/      # Entity-specific implementation\n│           ├── components/    # Entity table components\n│           │   ├── columns.tsx                # Column definitions\n│           │   ├── row-actions.tsx            # Row action menu\n│           │   ├── toolbar-options.tsx        # Toolbar customizations\n│           │   └── actions/                   # Action components\n│           │       ├── add-entity-popup.tsx   # Add modal\n│           │       ├── delete-entity-popup.tsx # Delete confirmation\n│           │       └── bulk-delete-popup.tsx  # Bulk delete confirmation\n│           ├── schema/        # Data schemas\n│           │   ├── entity-schema.ts           # Entity type definitions\n│           │   └── index.ts                   # Schema exports\n│           ├── utils/         # Utility functions\n│           │   ├── config.ts                  # Table configuration\n│           │   └── data-fetching.ts           # Data fetching hooks\n│           └── index.tsx      # Table component entry\n```\n\n### Understanding the Structure\n\nThis file structure follows a clear separation of concerns:\n\n1. **API Layer**: Handles all communication with the backend\n2. **Core Components**: Reusable data table building blocks\n3. **Implementation**: Entity-specific configuration and customization\n4. **Schema**: Type definitions and validation\n5. **Utils**: Helper functions for specific implementations\n\nBy following this structure, you can easily maintain and extend your data tables while keeping each part focused on its specific responsibility.\n\n---\n\n## Installation \u0026 Setup\n\n### Prerequisites\n\n- Next.js 13+ with App Router\n- React 18+\n- TypeScript 5+\n- Tailwind CSS\n- Shadcn UI components\n\n### Installation Steps\n\n1. **Install required dependencies**:\n\n```bash\n# Installing dependencies with Bun\nbun add @tanstack/react-table @tanstack/react-query zod @hookform/resolvers sonner date-fns\n```\n\n2. **Copy the core data table components** to your project:\n\nCreate a `/components/data-table` directory in your project and copy the following core components:\n\n- `data-table.tsx`: Main component\n- `column-header.tsx`: Sortable column headers\n- `filters.tsx`: Filter components\n- `utils.ts`: Helper functions\n\n3. **Set up the API layer**:\n\nCreate an API directory structure as shown in the file structure section above. Implement the necessary API functions for your entity.\n\n4. **Create Schema Definitions**:\n\nDefine your entity schema using Zod for type validation.\n\n5. **Implement the Data Table**:\n\nCreate your entity-specific table implementation following the structure outlined above.\n\n---\n\n## Basic Usage\n\nHere's a basic example of how to implement a data table for your entity:\n\n### 1. Define your entity schema\n\n```typescript\n// src/app/(section)/entity-table/schema/entity-schema.ts\nimport { z } from \"zod\";\n\nexport const entitySchema = z.object({\n  id: z.number(),\n  name: z.string(),\n  email: z.string(),\n  created_at: z.string(),\n  // Add other fields as needed\n});\n\nexport type Entity = z.infer\u003ctypeof entitySchema\u003e;\n\nexport const entitiesResponseSchema = z.object({\n  success: z.boolean(),\n  data: z.array(entitySchema),\n  pagination: z.object({\n    page: z.number(),\n    limit: z.number(),\n    total_pages: z.number(),\n    total_items: z.number(),\n  }),\n});\n```\n\n### 2. Create API functions\n\n```typescript\n// src/api/entity/fetch-entities.ts\nimport { z } from \"zod\";\nimport { entitiesResponseSchema } from \"@/app/(section)/entity-table/schema\";\n\nconst API_BASE_URL = \"/api\";\n\nexport async function fetchEntities({\n  search = \"\",\n  from_date = \"\",\n  to_date = \"\",\n  sort_by = \"created_at\",\n  sort_order = \"desc\",\n  page = 1,\n  limit = 10,\n}) {\n  // Build query parameters\n  const params = new URLSearchParams();\n  if (search) params.append(\"search\", search);\n  if (from_date) params.append(\"from_date\", from_date);\n  if (to_date) params.append(\"to_date\", to_date);\n  params.append(\"sort_by\", sort_by);\n  params.append(\"sort_order\", sort_order);\n  params.append(\"page\", page.toString());\n  params.append(\"limit\", limit.toString());\n\n  // Fetch data\n  const response = await fetch(`${API_BASE_URL}/entities?${params.toString()}`);\n\n  if (!response.ok) {\n    throw new Error(`Failed to fetch entities: ${response.statusText}`);\n  }\n\n  const data = await response.json();\n  return entitiesResponseSchema.parse(data);\n}\n```\n\n### 3. Create a data fetching hook\n\n```typescript\n// src/app/(section)/entity-table/utils/data-fetching.ts\nimport { useQuery, keepPreviousData } from \"@tanstack/react-query\";\nimport { fetchEntities } from \"@/api/entity/fetch-entities\";\n\nexport function useEntitiesData(\n  page: number,\n  pageSize: number,\n  search: string,\n  dateRange: { from_date: string; to_date: string },\n  sortBy: string,\n  sortOrder: string\n) {\n  return useQuery({\n    queryKey: [\n      \"entities\",\n      page,\n      pageSize,\n      search,\n      dateRange,\n      sortBy,\n      sortOrder,\n    ],\n    queryFn: () =\u003e\n      fetchEntities({\n        page,\n        limit: pageSize,\n        search,\n        from_date: dateRange.from_date,\n        to_date: dateRange.to_date,\n        sort_by: sortBy,\n        sort_order: sortOrder,\n      }),\n    placeholderData: keepPreviousData,\n  });\n}\n\n// Add this property for the DataTable component\nuseEntitiesData.isQueryHook = true;\n```\n\n### 4. Define your columns\n\n```typescript\n// src/app/(section)/entity-table/components/columns.tsx\n\"use client\";\n\nimport { format } from \"date-fns\";\nimport { ColumnDef } from \"@tanstack/react-table\";\n\n// Import components\nimport { DataTableColumnHeader } from \"@/components/data-table/column-header\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\n\n// Import schema and actions\nimport { Entity } from \"../schema\";\nimport { DataTableRowActions } from \"./row-actions\";\n\nexport const getColumns = (\n  handleRowDeselection: ((rowId: string) =\u003e void) | null | undefined\n): ColumnDef\u003cEntity\u003e[] =\u003e {\n  const baseColumns: ColumnDef\u003cEntity\u003e[] = [\n    {\n      accessorKey: \"name\",\n      header: ({ column }) =\u003e (\n        \u003cDataTableColumnHeader column={column} title=\"Name\" /\u003e\n      ),\n      cell: ({ row }) =\u003e (\n        \u003cdiv className=\"font-medium\"\u003e{row.getValue(\"name\")}\u003c/div\u003e\n      ),\n      size: 200,\n    },\n    {\n      accessorKey: \"email\",\n      header: ({ column }) =\u003e (\n        \u003cDataTableColumnHeader column={column} title=\"Email\" /\u003e\n      ),\n      cell: ({ row }) =\u003e \u003cdiv\u003e{row.getValue(\"email\")}\u003c/div\u003e,\n      size: 250,\n    },\n    {\n      accessorKey: \"created_at\",\n      header: ({ column }) =\u003e (\n        \u003cDataTableColumnHeader column={column} title=\"Created\" /\u003e\n      ),\n      cell: ({ row }) =\u003e {\n        const date = new Date(row.getValue(\"created_at\"));\n        const formatted = format(date, \"MMM d, yyyy\");\n        return \u003cdiv\u003e{formatted}\u003c/div\u003e;\n      },\n      size: 120,\n    },\n    {\n      id: \"actions\",\n      header: ({ column }) =\u003e (\n        \u003cDataTableColumnHeader column={column} title=\"Actions\" /\u003e\n      ),\n      cell: ({ row, table }) =\u003e \u003cDataTableRowActions row={row} table={table} /\u003e,\n      size: 100,\n    },\n  ];\n\n  // Only include selection column if row selection is enabled\n  if (handleRowDeselection !== null) {\n    return [\n      {\n        id: \"select\",\n        header: ({ table }) =\u003e (\n          \u003cCheckbox\n            checked={table.getIsAllPageRowsSelected()}\n            onCheckedChange={(value) =\u003e\n              table.toggleAllPageRowsSelected(!!value)\n            }\n            aria-label=\"Select all\"\n            className=\"translate-y-0.5 cursor-pointer\"\n          /\u003e\n        ),\n        cell: ({ row }) =\u003e (\n          \u003cCheckbox\n            checked={row.getIsSelected()}\n            onCheckedChange={(value) =\u003e {\n              row.toggleSelected(!!value);\n              if (!value \u0026\u0026 handleRowDeselection) {\n                handleRowDeselection(row.id);\n              }\n            }}\n            aria-label=\"Select row\"\n            className=\"translate-y-0.5 cursor-pointer\"\n          /\u003e\n        ),\n        enableSorting: false,\n        enableHiding: false,\n        size: 50,\n      },\n      ...baseColumns,\n    ];\n  }\n\n  return baseColumns;\n};\n```\n\n### 5. Implement the main table component\n\n```typescript\n// src/app/(section)/entity-table/index.tsx\n\"use client\";\n\nimport { DataTable } from \"@/components/data-table/data-table\";\nimport { getColumns } from \"./components/columns\";\nimport { useExportConfig } from \"./utils/config\";\nimport { fetchEntitiesByIds } from \"@/api/entity/fetch-entities-by-ids\";\nimport { useEntitiesData } from \"./utils/data-fetching\";\nimport { ToolbarOptions } from \"./components/toolbar-options\";\nimport { Entity } from \"./schema\";\n\nexport default function EntityTable() {\n  return (\n    \u003cDataTable\u003cEntity, any\u003e\n      getColumns={getColumns}\n      exportConfig={useExportConfig()}\n      fetchDataFn={useEntitiesData}\n      fetchByIdsFn={fetchEntitiesByIds}\n      idField=\"id\"\n      pageSizeOptions={[10, 20, 50, 100]}\n      renderToolbarContent={({\n        selectedRows,\n        allSelectedIds,\n        totalSelectedCount,\n        resetSelection,\n      }) =\u003e (\n        \u003cToolbarOptions\n          selectedEntities={selectedRows.map((row) =\u003e ({\n            id: row.id,\n            name: row.name,\n          }))}\n          allSelectedIds={allSelectedIds}\n          totalSelectedCount={totalSelectedCount}\n          resetSelection={resetSelection}\n        /\u003e\n      )}\n      config={{\n        enableRowSelection: true,\n        enableSearch: true,\n        enableDateFilter: true,\n        enableColumnVisibility: true,\n        enableUrlState: true,\n        columnResizingTableId: \"entity-table\",\n      }}\n    /\u003e\n  );\n}\n```\n\n### 6. Add the table to your page\n\n```typescript\n// src/app/(section)/entities/page.tsx\nimport { Metadata } from \"next\";\nimport { Suspense } from \"react\";\nimport EntityTable from \"./entity-table\";\n\nexport const metadata: Metadata = {\n  title: \"Entities Management\",\n};\n\nexport default function EntitiesPage() {\n  return (\n    \u003cmain className=\"container mx-auto py-10\"\u003e\n      \u003ch1 className=\"text-xl font-bold mb-4\"\u003eEntities List\u003c/h1\u003e\n      \u003cSuspense fallback={\u003cdiv\u003eLoading...\u003c/div\u003e}\u003e\n        \u003cEntityTable /\u003e\n      \u003c/Suspense\u003e\n    \u003c/main\u003e\n  );\n}\n```\n\n---\n\n## Core Components\n\nThe data table is built using several key components that work together. Understanding these components will help you customize and extend the table according to your needs.\n\n### Main Data Table Component\n\nThe `DataTable` component is the main entry point for the table implementation. It handles:\n\n- State management\n- Data fetching\n- URL state persistence\n- Pagination\n- Sorting\n- Filtering\n- Row selection\n- Export functionality\n\n### Column Header Component\n\nThe `DataTableColumnHeader` component provides:\n\n- Visual indication of sort direction\n- Sorting controls\n- Column header rendering\n\n### Filter Components\n\nFilter components provide UI for filtering data:\n\n- Search input\n- Date range picker\n- Custom filters can be added\n\n### Row Actions\n\nRow action components handle operations on individual rows:\n\n- Action menus\n- Delete confirmations\n- Edit forms\n\n### Toolbar Components\n\nThe toolbar area provides:\n\n- Global actions (add new, bulk delete)\n- Filter controls\n- Export buttons\n- View options\n\n---\n\n## API Integration\n\n### API Layer Structure\n\nThe data table relies on a consistent API layer to communicate with your backend services. Each entity should have the following API functions:\n\n#### 1. Fetch List with Filtering\n\n```typescript\n// Function signature\nasync function fetchEntities({\n  search?: string,\n  from_date?: string,\n  to_date?: string,\n  sort_by?: string,\n  sort_order?: string,\n  page?: number,\n  limit?: number,\n}): Promise\u003cEntityResponse\u003e;\n```\n\n#### 2. Fetch Multiple by IDs\n\n```typescript\n// Function signature\nasync function fetchEntitiesByIds(ids: number[]): Promise\u003cEntity[]\u003e;\n```\n\n#### 3. Add Entity\n\n```typescript\n// Function signature\nasync function addEntity(data: NewEntity): Promise\u003cAddEntityResponse\u003e;\n```\n\n#### 4. Delete Entity\n\n```typescript\n// Function signature\nasync function deleteEntity(id: number): Promise\u003cDeleteEntityResponse\u003e;\n```\n\n### Error Handling in API Layer\n\nEach API function should include proper error handling:\n\n```typescript\nexport async function addEntity(\n  entityData: NewEntity\n): Promise\u003cAddEntityResponse\u003e {\n  try {\n    // Validate input data\n    const validatedData = addEntitySchema.parse(entityData);\n\n    // Make API request\n    const response = await fetch(`${API_BASE_URL}/entities/add`, {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify(validatedData),\n    });\n\n    // Parse response\n    const data = await response.json();\n\n    // Validate response\n    const validatedResponse = addEntityResponseSchema.parse(data);\n\n    // Check if the request was successful\n    if (!response.ok) {\n      throw new Error(validatedResponse.error || \"Failed to add entity\");\n    }\n\n    return validatedResponse;\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      throw new Error(\"Invalid response format from server\");\n    }\n    throw error;\n  }\n}\n```\n\n---\n\n## Advanced Configuration\n\n### Column Configuration\n\nColumns are defined using TanStack Table's `ColumnDef` interface. Each column can be customized with:\n\n#### Basic Properties\n\n- `accessorKey`: The key to use when accessing the data\n- `header`: Custom header rendering\n- `cell`: Custom cell rendering\n- `size`: Column width\n\n#### Advanced Properties\n\n- `enableSorting`: Enable/disable sorting for this column\n- `enableHiding`: Allow column to be hidden/shown\n- `meta`: Custom metadata for the column\n- `filterFn`: Custom filtering function\n\n#### Example Column Definition\n\n```typescript\n{\n  accessorKey: \"name\",\n  header: ({ column }) =\u003e (\n    \u003cDataTableColumnHeader column={column} title=\"Name\" /\u003e\n  ),\n  cell: ({ row }) =\u003e {\n    // Custom rendering with additional styling or components\n    return (\n      \u003cdiv className=\"flex items-center\"\u003e\n        \u003cAvatar className=\"mr-2\" name={row.getValue(\"name\")} /\u003e\n        \u003cspan className=\"font-medium\"\u003e{row.getValue(\"name\")}\u003c/span\u003e\n      \u003c/div\u003e\n    );\n  },\n  enableSorting: true,\n  enableHiding: true,\n  size: 200,\n}\n```\n\n### Row Actions\n\nRow actions provide operations on individual rows. They're implemented using the `DataTableRowActions` component:\n\n```typescript\n// src/app/(section)/entity-table/components/row-actions.tsx\n\"use client\";\n\nimport * as React from \"react\";\nimport { DotsHorizontalIcon } from \"@radix-ui/react-icons\";\nimport { Row } from \"@tanstack/react-table\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { entitySchema } from \"../schema\";\nimport { DeleteEntityPopup } from \"./actions/delete-entity-popup\";\n\ninterface DataTableRowActionsProps\u003cTData\u003e {\n  row: Row\u003cTData\u003e;\n  table: any; // Table instance\n}\n\nexport function DataTableRowActions\u003cTData\u003e({\n  row,\n  table,\n}: DataTableRowActionsProps\u003cTData\u003e) {\n  const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false);\n  const entity = entitySchema.parse(row.original);\n\n  // Function to reset all selections\n  const resetSelection = () =\u003e {\n    table.resetRowSelection();\n  };\n\n  return (\n    \u003c\u003e\n      \u003cDropdownMenu\u003e\n        \u003cDropdownMenuTrigger asChild\u003e\n          \u003cButton\n            variant=\"ghost\"\n            className=\"flex h-8 w-8 p-0 data-[state=open]:bg-muted\"\n          \u003e\n            \u003cDotsHorizontalIcon className=\"h-4 w-4\" /\u003e\n            \u003cspan className=\"sr-only\"\u003eOpen menu\u003c/span\u003e\n          \u003c/Button\u003e\n        \u003c/DropdownMenuTrigger\u003e\n        \u003cDropdownMenuContent align=\"end\" className=\"w-[160px]\"\u003e\n          \u003cDropdownMenuItem onClick={() =\u003e console.log(\"Edit\", entity)}\u003e\n            Edit\n          \u003c/DropdownMenuItem\u003e\n          \u003cDropdownMenuItem\u003eView Details\u003c/DropdownMenuItem\u003e\n          \u003cDropdownMenuSeparator /\u003e\n          \u003cDropdownMenuItem onClick={() =\u003e setDeleteDialogOpen(true)}\u003e\n            Delete\n          \u003c/DropdownMenuItem\u003e\n        \u003c/DropdownMenuContent\u003e\n      \u003c/DropdownMenu\u003e\n\n      \u003cDeleteEntityPopup\n        open={deleteDialogOpen}\n        onOpenChange={setDeleteDialogOpen}\n        entityId={entity.id}\n        entityName={entity.name}\n        resetSelection={resetSelection}\n      /\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n### Filtering \u0026 Sorting\n\nThe data table supports server-side filtering and sorting. Configure the API to handle the following parameters:\n\n- `search`: Text search term\n- `sort_by`: Column to sort by\n- `sort_order`: Sort direction (asc/desc)\n\n### Pagination\n\nServer-side pagination is handled through the following parameters:\n\n- `page`: Current page number (1-based)\n- `limit`: Number of items per page\n\nThe server should return pagination information in the response:\n\n```json\n{\n  \"success\": true,\n  \"data\": [...],\n  \"pagination\": {\n    \"page\": 1,\n    \"limit\": 10,\n    \"total_pages\": 5,\n    \"total_items\": 48\n  }\n}\n```\n\n### Date Range Filtering\n\nDate range filtering allows filtering records by a date field:\n\n- `from_date`: Start date in ISO format\n- `to_date`: End date in ISO format\n\nThis is useful for limiting records to a specific time period.\n\n### Row Selection\n\nRow selection enables operations on multiple rows. It's controlled by:\n\n```typescript\nconfig={{\n  enableRowSelection: true, // Enable row selection\n  enableClickRowSelect: false // Enable/disable row selection by clicking anywhere in the row\n}}\n```\n\nSelected rows can be accessed via the `renderToolbarContent` prop:\n\n```typescript\nrenderToolbarContent={({\n  selectedRows,  // Currently visible selected rows\n  allSelectedIds, // All selected IDs across pages\n  totalSelectedCount, // Total number of selected items\n  resetSelection  // Function to reset selection\n}) =\u003e (\n  \u003cToolbarOptions\n    selectedEntities={selectedRows}\n    allSelectedIds={allSelectedIds}\n    totalSelectedCount={totalSelectedCount}\n    resetSelection={resetSelection}\n  /\u003e\n)}\n```\n\n### Toolbar Customization\n\nThe toolbar area can be customized with your own components:\n\n```typescript\n// src/app/(section)/entity-table/components/toolbar-options.tsx\n\"use client\";\n\nimport * as React from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { AddEntityPopup } from \"./actions/add-entity-popup\";\nimport { BulkDeletePopup } from \"./actions/bulk-delete-popup\";\n\ninterface ToolbarOptionsProps {\n  selectedEntities: { id: number; name: string }[];\n  allSelectedIds?: number[];\n  totalSelectedCount: number;\n  resetSelection: () =\u003e void;\n}\n\nexport const ToolbarOptions = ({\n  selectedEntities,\n  allSelectedIds = [],\n  totalSelectedCount,\n  resetSelection,\n}: ToolbarOptionsProps) =\u003e {\n  const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false);\n\n  return (\n    \u003cdiv className=\"flex items-center gap-2\"\u003e\n      \u003cAddEntityPopup /\u003e\n\n      {totalSelectedCount \u003e 0 \u0026\u0026 (\n        \u003c\u003e\n          \u003cButton\n            variant=\"outline\"\n            size=\"sm\"\n            onClick={() =\u003e setDeleteDialogOpen(true)}\n          \u003e\n            Delete ({totalSelectedCount})\n          \u003c/Button\u003e\n\n          \u003cBulkDeletePopup\n            open={deleteDialogOpen}\n            onOpenChange={setDeleteDialogOpen}\n            selectedEntities={selectedEntities}\n            allSelectedIds={allSelectedIds}\n            totalSelectedCount={totalSelectedCount}\n            resetSelection={resetSelection}\n          /\u003e\n        \u003c/\u003e\n      )}\n    \u003c/div\u003e\n  );\n};\n```\n\n### Export Options\n\nThe data table supports exporting data in various formats. Configure export options:\n\n```typescript\n// src/app/(section)/entity-table/utils/config.ts\nimport { useMemo } from \"react\";\n\nexport function useExportConfig() {\n  // Column mapping for export\n  const columnMapping = useMemo(() =\u003e {\n    return {\n      id: \"ID\",\n      name: \"Name\",\n      email: \"Email\",\n      created_at: \"Created Date\",\n      // Add other fields\n    };\n  }, []);\n\n  // Column widths for Excel export\n  const columnWidths = useMemo(() =\u003e {\n    return [\n      { wch: 10 }, // ID\n      { wch: 20 }, // Name\n      { wch: 30 }, // Email\n      { wch: 20 }, // Created At\n    ];\n  }, []);\n\n  // Headers for CSV export\n  const headers = useMemo(() =\u003e {\n    return [\"id\", \"name\", \"email\", \"created_at\"];\n  }, []);\n\n  return {\n    columnMapping,\n    columnWidths,\n    headers,\n    entityName: \"entities\", // Used in filename\n  };\n}\n```\n\n---\n\n## Server Implementation\n\n### API Endpoints\n\nThe data table expects the following API endpoints:\n\n#### 1. List endpoint\n\n```\nGET /api/entities\n```\n\nParameters:\n\n- `search` (optional): Search term\n- `from_date` (optional): Start date filter\n- `to_date` (optional): End date filter\n- `sort_by` (optional): Column to sort by\n- `sort_order` (optional): 'asc' or 'desc'\n- `page` (optional): Page number (default: 1)\n- `limit` (optional): Items per page (default: 10)\n\n#### 2. Create endpoint\n\n```\nPOST /api/entities/add\n```\n\nBody: Entity data according to schema\n\n#### 3. Delete endpoint\n\n```\nDELETE /api/entities/:id\n```\n\nPath parameter: `id` - Entity ID\n\n### Request \u0026 Response Formats\n\n#### List Response Format\n\n```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Example Entity\",\n      \"email\": \"example@example.com\",\n      \"created_at\": \"2025-01-15T10:30:00Z\",\n      ...\n    },\n    ...\n  ],\n  \"pagination\": {\n    \"page\": 1,\n    \"limit\": 10,\n    \"total_pages\": 5,\n    \"total_items\": 48\n  }\n}\n```\n\n#### Create Request Format\n\n```json\n{\n  \"name\": \"New Entity\",\n  \"email\": \"new@example.com\",\n  ...\n}\n```\n\n#### Create Response Format\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 49,\n    \"name\": \"New Entity\",\n    \"email\": \"new@example.com\",\n    \"created_at\": \"2025-04-14T06:44:16Z\",\n    ...\n  }\n}\n```\n\n#### Delete Response Format\n\n```json\n{\n  \"success\": true,\n  \"message\": \"Entity deleted successfully\"\n}\n```\n\n### Error Handling\n\nAll API responses should follow a consistent error format:\n\n```json\n{\n  \"success\": false,\n  \"error\": \"Error message\",\n  \"details\": [] // Optional array with detailed error information\n}\n```\n\nHTTP status codes should also be appropriate:\n\n- 400: Bad Request (validation errors)\n- 404: Not Found\n- 409: Conflict (e.g., duplicate entity)\n- 500: Internal Server Error\n\n---\n\n## Popups \u0026 Modals\n\nThe data table uses several modal dialogs for different operations. Here's how to implement them:\n\n### Add Entity Popup\n\n```tsx\n// src/app/(section)/entity-table/components/actions/add-entity-popup.tsx\n\"use client\";\n\nimport * as React from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { z } from \"zod\";\nimport { toast } from \"sonner\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useQueryClient } from \"@tanstack/react-query\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n  DialogFooter,\n} from \"@/components/ui/dialog\";\nimport {\n  Form,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormControl,\n  FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\n\n// Import API\nimport { addEntity } from \"@/api/entity/add-entity\";\n\n// Form schema\nconst formSchema = z.object({\n  name: z.string().min(1, \"Name is required\"),\n  email: z.string().email(\"Invalid email format\"),\n  // Add other fields\n});\n\ntype FormValues = z.infer\u003ctypeof formSchema\u003e;\n\nexport function AddEntityPopup() {\n  const router = useRouter();\n  const [open, setOpen] = React.useState(false);\n  const [isLoading, setIsLoading] = React.useState(false);\n  const queryClient = useQueryClient();\n\n  const form = useForm\u003cFormValues\u003e({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      name: \"\",\n      email: \"\",\n    },\n  });\n\n  const onSubmit = async (data: FormValues) =\u003e {\n    try {\n      setIsLoading(true);\n      const response = await addEntity(data);\n\n      if (response.success) {\n        toast.success(\"Entity added successfully\");\n        form.reset();\n        setOpen(false);\n        router.refresh();\n        await queryClient.invalidateQueries({ queryKey: [\"entities\"] });\n      } else {\n        toast.error(response.error || \"Failed to add entity\");\n      }\n    } catch (error) {\n      toast.error(\n        error instanceof Error ? error.message : \"Failed to add entity\"\n      );\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  return (\n    \u003cDialog open={open} onOpenChange={setOpen}\u003e\n      \u003cDialogTrigger asChild\u003e\n        \u003cButton size=\"sm\"\u003eAdd Entity\u003c/Button\u003e\n      \u003c/DialogTrigger\u003e\n      \u003cDialogContent className=\"sm:max-w-[425px]\"\u003e\n        \u003cDialogHeader\u003e\n          \u003cDialogTitle\u003eAdd New Entity\u003c/DialogTitle\u003e\n        \u003c/DialogHeader\u003e\n        \u003cForm {...form}\u003e\n          \u003cform onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\"\u003e\n            \u003cFormField\n              control={form.control}\n              name=\"name\"\n              render={({ field }) =\u003e (\n                \u003cFormItem\u003e\n                  \u003cFormLabel\u003eName\u003c/FormLabel\u003e\n                  \u003cFormControl\u003e\n                    \u003cInput placeholder=\"Enter name\" {...field} /\u003e\n                  \u003c/FormControl\u003e\n                  \u003cFormMessage /\u003e\n                \u003c/FormItem\u003e\n              )}\n            /\u003e\n            \u003cFormField\n              control={form.control}\n              name=\"email\"\n              render={({ field }) =\u003e (\n                \u003cFormItem\u003e\n                  \u003cFormLabel\u003eEmail\u003c/FormLabel\u003e\n                  \u003cFormControl\u003e\n                    \u003cInput type=\"email\" placeholder=\"Enter email\" {...field} /\u003e\n                  \u003c/FormControl\u003e\n                  \u003cFormMessage /\u003e\n                \u003c/FormItem\u003e\n              )}\n            /\u003e\n            {/* Add other form fields */}\n            \u003cDialogFooter\u003e\n              \u003cButton\n                type=\"button\"\n                variant=\"outline\"\n                onClick={() =\u003e setOpen(false)}\n                disabled={isLoading}\n              \u003e\n                Cancel\n              \u003c/Button\u003e\n              \u003cButton type=\"submit\" disabled={isLoading}\u003e\n                {isLoading ? \"Adding...\" : \"Add Entity\"}\n              \u003c/Button\u003e\n            \u003c/DialogFooter\u003e\n          \u003c/form\u003e\n        \u003c/Form\u003e\n      \u003c/DialogContent\u003e\n    \u003c/Dialog\u003e\n  );\n}\n```\n\n### Delete Entity Popup\n\n```tsx\n// src/app/(section)/entity-table/components/actions/delete-entity-popup.tsx\n\"use client\";\n\nimport * as React from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { toast } from \"sonner\";\nimport { useQueryClient } from \"@tanstack/react-query\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from \"@/components/ui/dialog\";\n\n// Import API\nimport { deleteEntity } from \"@/api/entity/delete-entity\";\n\ninterface DeleteEntityPopupProps {\n  open: boolean;\n  onOpenChange: (open: boolean) =\u003e void;\n  entityId: number;\n  entityName: string;\n  resetSelection?: () =\u003e void;\n}\n\nexport function DeleteEntityPopup({\n  open,\n  onOpenChange,\n  entityId,\n  entityName,\n  resetSelection,\n}: DeleteEntityPopupProps) {\n  const router = useRouter();\n  const queryClient = useQueryClient();\n  const [isLoading, setIsLoading] = React.useState(false);\n\n  const handleDelete = async () =\u003e {\n    try {\n      setIsLoading(true);\n      const response = await deleteEntity(entityId);\n\n      if (response.success) {\n        toast.success(\"Entity deleted successfully\");\n        onOpenChange(false);\n\n        // Reset the selection state if the function is provided\n        if (resetSelection) {\n          resetSelection();\n        }\n\n        // Refresh data\n        router.refresh();\n        await queryClient.invalidateQueries({ queryKey: [\"entities\"] });\n      } else {\n        toast.error(response.error || \"Failed to delete entity\");\n      }\n    } catch (error) {\n      toast.error(\n        error instanceof Error ? error.message : \"Failed to delete entity\"\n      );\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  return (\n    \u003cDialog open={open} onOpenChange={onOpenChange}\u003e\n      \u003cDialogContent className=\"sm:max-w-[425px]\"\u003e\n        \u003cDialogHeader\u003e\n          \u003cDialogTitle\u003eDelete Entity\u003c/DialogTitle\u003e\n          \u003cDialogDescription\u003e\n            Are you sure you want to delete {entityName}? This action cannot be\n            undone.\n          \u003c/DialogDescription\u003e\n        \u003c/DialogHeader\u003e\n        \u003cDialogFooter\u003e\n          \u003cButton\n            type=\"button\"\n            variant=\"outline\"\n            onClick={() =\u003e onOpenChange(false)}\n            disabled={isLoading}\n          \u003e\n            Cancel\n          \u003c/Button\u003e\n          \u003cButton\n            type=\"button\"\n            variant=\"destructive\"\n            onClick={handleDelete}\n            disabled={isLoading}\n          \u003e\n            {isLoading ? \"Deleting...\" : \"Delete\"}\n          \u003c/Button\u003e\n        \u003c/DialogFooter\u003e\n      \u003c/DialogContent\u003e\n    \u003c/Dialog\u003e\n  );\n}\n```\n\n### Bulk Delete Popup\n\n```tsx\n// src/app/(section)/entity-table/components/actions/bulk-delete-popup.tsx\n\"use client\";\n\nimport * as React from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { toast } from \"sonner\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from \"@/components/ui/dialog\";\n\n// Import API\nimport { deleteEntity } from \"@/api/entity/delete-entity\";\n\ninterface BulkDeletePopupProps {\n  open: boolean;\n  onOpenChange: (open: boolean) =\u003e void;\n  selectedEntities: { id: number; name: string }[];\n  allSelectedIds?: number[];\n  totalSelectedCount?: number;\n  resetSelection: () =\u003e void;\n}\n\nexport function BulkDeletePopup({\n  open,\n  onOpenChange,\n  selectedEntities,\n  allSelectedIds,\n  totalSelectedCount,\n  resetSelection,\n}: BulkDeletePopupProps) {\n  const router = useRouter();\n  const queryClient = useQueryClient();\n  const [isLoading, setIsLoading] = React.useState(false);\n\n  // Use allSelectedIds if available, otherwise fallback to selectedEntities ids\n  const idsToDelete =\n    allSelectedIds || selectedEntities.map((entity) =\u003e entity.id);\n\n  // Use total count if available, otherwise fallback to visible items count\n  const itemCount = totalSelectedCount ?? selectedEntities.length;\n\n  const handleDelete = async () =\u003e {\n    try {\n      setIsLoading(true);\n\n      // Delete entities sequentially\n      for (const id of idsToDelete) {\n        const response = await deleteEntity(id);\n        if (!response.success) {\n          throw new Error(`Failed to delete entity ID ${id}`);\n        }\n      }\n\n      toast.success(\n        itemCount === 1\n          ? \"Entity deleted successfully\"\n          : `${itemCount} entities deleted successfully`\n      );\n\n      onOpenChange(false);\n      resetSelection();\n      router.refresh();\n      await queryClient.invalidateQueries({ queryKey: [\"entities\"] });\n    } catch (error) {\n      toast.error(\n        error instanceof Error ? error.message : \"Failed to delete entities\"\n      );\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  const getDialogTitle = () =\u003e {\n    if (itemCount === 1) {\n      return \"Delete Entity\";\n    }\n    return \"Delete Entities\";\n  };\n\n  const getDialogDescription = () =\u003e {\n    if (itemCount === 1 \u0026\u0026 selectedEntities.length === 1) {\n      return `Are you sure you want to delete ${selectedEntities[0].name}? This action cannot be undone.`;\n    }\n    return `Are you sure you want to delete ${itemCount} entities? This action cannot be undone.`;\n  };\n\n  return (\n    \u003cDialog open={open} onOpenChange={onOpenChange}\u003e\n      \u003cDialogContent className=\"sm:max-w-[425px]\"\u003e\n        \u003cDialogHeader\u003e\n          \u003cDialogTitle\u003e{getDialogTitle()}\u003c/DialogTitle\u003e\n          \u003cDialogDescription\u003e{getDialogDescription()}\u003c/DialogDescription\u003e\n        \u003c/DialogHeader\u003e\n        \u003cDialogFooter\u003e\n          \u003cButton\n            type=\"button\"\n            variant=\"outline\"\n            onClick={() =\u003e onOpenChange(false)}\n            disabled={isLoading}\n          \u003e\n            Cancel\n          \u003c/Button\u003e\n          \u003cButton\n            type=\"button\"\n            variant=\"destructive\"\n            onClick={handleDelete}\n            disabled={isLoading}\n          \u003e\n            {isLoading ? \"Deleting...\" : \"Delete\"}\n          \u003c/Button\u003e\n        \u003c/DialogFooter\u003e\n      \u003c/DialogContent\u003e\n    \u003c/Dialog\u003e\n  );\n}\n```\n\n---\n\n## Customization\n\n### Custom Column Rendering\n\nYou can customize column rendering with custom cell components:\n\n```typescript\n{\n  accessorKey: \"status\",\n  header: ({ column }) =\u003e (\n    \u003cDataTableColumnHeader column={column} title=\"Status\" /\u003e\n  ),\n  cell: ({ row }) =\u003e {\n    const status = row.getValue(\"status\") as string;\n\n    // Map status to badge variant\n    const variant = {\n      active: \"success\",\n      inactive: \"secondary\",\n      pending: \"warning\",\n      error: \"destructive\",\n    }[status] || \"outline\";\n\n    return (\n      \u003cdiv className=\"flex w-full justify-center\"\u003e\n        \u003cBadge variant={variant}\u003e{status}\u003c/Badge\u003e\n      \u003c/div\u003e\n    );\n  },\n  size: 100,\n}\n```\n\n### Custom Toolbar Content\n\nAdd your own content to the toolbar:\n\n```tsx\nrenderToolbarContent={({\n  selectedRows,\n  allSelectedIds,\n  totalSelectedCount,\n  resetSelection\n}) =\u003e (\n  \u003cdiv className=\"flex items-center gap-2\"\u003e\n    \u003cAddEntityPopup /\u003e\n\n    {totalSelectedCount \u003e 0 \u0026\u0026 (\n      \u003c\u003e\n        {/* Custom bulk action button */}\n        \u003cButton\n          variant=\"outline\"\n          size=\"sm\"\n          onClick={() =\u003e handleBulkApprove(allSelectedIds)}\n        \u003e\n          Approve ({totalSelectedCount})\n        \u003c/Button\u003e\n\n        {/* Default delete button */}\n        \u003cButton\n          variant=\"outline\"\n          size=\"sm\"\n          onClick={() =\u003e setDeleteDialogOpen(true)}\n        \u003e\n          Delete ({totalSelectedCount})\n        \u003c/Button\u003e\n\n        \u003cBulkDeletePopup\n          open={deleteDialogOpen}\n          onOpenChange={setDeleteDialogOpen}\n          selectedEntities={selectedRows}\n          allSelectedIds={allSelectedIds}\n          totalSelectedCount={totalSelectedCount}\n          resetSelection={resetSelection}\n        /\u003e\n      \u003c/\u003e\n    )}\n  \u003c/div\u003e\n)}\n```\n\n### Custom Filtering\n\nAdd custom filtering controls:\n\n```tsx\n// Inside your DataTable component\nconst renderFilters = () =\u003e (\n  \u003cdiv className=\"flex gap-2\"\u003e\n    \u003cSelect value={statusFilter} onValueChange={setStatusFilter}\u003e\n      \u003cSelectTrigger className=\"h-8 w-[150px]\"\u003e\n        \u003cSelectValue placeholder=\"Status\" /\u003e\n      \u003c/SelectTrigger\u003e\n      \u003cSelectContent\u003e\n        \u003cSelectItem value=\"all\"\u003eAll Status\u003c/SelectItem\u003e\n        \u003cSelectItem value=\"active\"\u003eActive\u003c/SelectItem\u003e\n        \u003cSelectItem value=\"inactive\"\u003eInactive\u003c/SelectItem\u003e\n        \u003cSelectItem value=\"pending\"\u003ePending\u003c/SelectItem\u003e\n      \u003c/SelectContent\u003e\n    \u003c/Select\u003e\n\n    {/* Use custom filters in your API call */}\n    {statusFilter !== \"all\" \u0026\u0026 (\n      \u003cBadge variant=\"outline\" className=\"h-8 px-3 flex items-center gap-1\"\u003e\n        Status: {statusFilter}\n        \u003cX\n          className=\"h-3 w-3 cursor-pointer\"\n          onClick={() =\u003e setStatusFilter(\"all\")}\n        /\u003e\n      \u003c/Badge\u003e\n    )}\n  \u003c/div\u003e\n);\n```\n\n### Styling\n\nThe data table uses Tailwind CSS for styling. You can customize the appearance:\n\n```tsx\n\u003cDataTable\n  className=\"border rounded-lg\"\n  tableClassName=\"min-w-full divide-y divide-gray-200\"\n  headerClassName=\"bg-gray-50 text-xs uppercase tracking-wider\"\n  rowClassName=\"even:bg-gray-50 hover:bg-gray-100\"\n/\u003e\n```\n\n---\n\n## Performance Optimization\n\n### Server-Side Operations\n\nFor optimal performance, ensure that all data operations are handled server-side:\n\n- Filtering\n- Sorting\n- Pagination\n\nThis approach ensures that:\n\n1. Only necessary data is transferred\n2. The client doesn't need to process large datasets\n3. Performance scales with your server capacity\n\n### Data Batching\n\nWhen fetching individual records (like for selection across pages), use batching:\n\n```typescript\nexport async function fetchEntitiesByIds(\n  entityIds: number[]\n): Promise\u003cEntity[]\u003e {\n  if (entityIds.length === 0) {\n    return [];\n  }\n\n  // Use batching to avoid URL length limits\n  const BATCH_SIZE = 50;\n  const results: Entity[] = [];\n\n  // Process in batches\n  for (let i = 0; i \u003c entityIds.length; i += BATCH_SIZE) {\n    const batchIds = entityIds.slice(i, i + BATCH_SIZE);\n\n    try {\n      const params = new URLSearchParams();\n      batchIds.forEach((id) =\u003e {\n        params.append(\"id\", id.toString());\n      });\n\n      const response = await fetch(\n        `${API_BASE_URL}/entities?${params.toString()}`\n      );\n\n      if (!response.ok) {\n        throw new Error(`Failed to fetch entities: ${response.statusText}`);\n      }\n\n      const data = await response.json();\n      const parsedData = entitiesResponseSchema.parse(data);\n\n      results.push(...parsedData.data);\n    } catch (error) {\n      console.error(`Error fetching batch of entities:`, error);\n    }\n  }\n\n  return results;\n}\n```\n\n### Query Caching\n\nThe data table uses React Query for data fetching, which provides:\n\n- Automatic caching\n- Background refetching\n- Stale data management\n\nTo optimize React Query usage:\n\n```typescript\nuseQuery({\n  queryKey: [\"entities\", ...], // Include all filters in the queryKey\n  queryFn: () =\u003e fetchEntities({...}),\n  placeholderData: keepPreviousData, // Show previous data while loading new data\n  staleTime: 30000, // Data is considered fresh for 30 seconds\n  refetchOnWindowFocus: false, // Disable refetching when window regains focus\n})\n```\n\n### Virtualized Rendering\n\nFor very large tables, consider adding virtualization:\n\n```tsx\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\n\n// Inside your component:\nconst tableContainerRef = React.useRef(null);\n\nconst rowVirtualizer = useVirtualizer({\n  count: rows.length,\n  getScrollElement: () =\u003e tableContainerRef.current,\n  estimateSize: () =\u003e 35, // Approximate row height\n  overscan: 10,\n});\n\n// Use with your table:\n\u003cdiv ref={tableContainerRef} className=\"max-h-[500px] overflow-auto\"\u003e\n  \u003ctable\u003e\n    \u003cthead\u003e{/* ... */}\u003c/thead\u003e\n    \u003ctbody\u003e\n      {rowVirtualizer.getVirtualItems().map((virtualRow) =\u003e {\n        const row = rows[virtualRow.index];\n        return (\n          \u003ctr\n            key={row.id}\n            data-index={virtualRow.index}\n            style={{\n              height: `${virtualRow.size}px`,\n              transform: `translateY(${virtualRow.start}px)`,\n            }}\n          \u003e\n            {/* Render cells */}\n          \u003c/tr\u003e\n        );\n      })}\n    \u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e;\n```\n\n---\n\n## Best Practices\n\n### 1. State Management\n\n- Use URL state for filters, sorting, and pagination to enable bookmarking and sharing\n- Keep complex state in React Query for automatic caching and refetching\n- Use local state only for UI-specific state like modal visibility\n\n### 2. Error Handling\n\n- Implement consistent error handling across all API calls\n- Use toast notifications for user feedback\n- Log errors to the console for debugging\n- Include error details in the UI when appropriate\n\n### 3. Loading States\n\n- Show loading indicators for initial load and filtering operations\n- Use optimistic updates for better UX during create/update/delete operations\n- Keep previous data visible while loading new data\n\n### 4. Accessibility\n\n- Use proper ARIA attributes for interactive elements\n- Ensure keyboard navigation works for all table interactions\n- Maintain sufficient color contrast for all text\n- Make sure all interactive elements have accessible labels\n\n### 5. Form Validation\n\n- Use Zod for consistent validation on both client and server\n- Provide clear error messages for validation failures\n- Validate form inputs as the user types for immediate feedback\n- Debounce validation to avoid excessive processing\n\n### 6. API Design\n\n- Use consistent API response formats across all endpoints\n- Include appropriate HTTP status codes\n- Validate input on both client and server\n- Use pagination for all list endpoints\n\n### 7. Performance\n\n- Only fetch the data you need\n- Use pagination for large datasets\n- Implement caching for frequently accessed data\n- Optimize server queries (use indexes, limit fields, etc.)\n\n### 8. Security\n\n- Validate all user inputs (client and server)\n- Implement proper authentication and authorization\n- Use HTTPS for all API requests\n- Sanitize data before displaying it in the UI\n\n### 9. Code Organization\n\n- Follow the file structure outlined in this documentation\n- Keep components focused on a single responsibility\n- Extract reusable logic into custom hooks\n- Use consistent naming conventions\n\n### 10. Testing\n\n- Write unit tests for critical components\n- Test edge cases (empty states, error states, etc.)\n- Consider using integration tests for complete workflows\n- Test accessibility with automated tools\n\n---\n\n## Troubleshooting\n\n### Common Issues and Solutions\n\n#### Issue: Table data doesn't update after adding or deleting items\n\n**Possible Causes:**\n\n- React Query cache not invalidated\n- Missing router.refresh() call\n\n**Solution:**\n\n```typescript\n// After successful mutation:\nawait queryClient.invalidateQueries({ queryKey: [\"entities\"] });\nrouter.refresh();\n```\n\n#### Issue: Form validation errors not displaying\n\n**Possible Causes:**\n\n- Missing FormMessage component\n- Incorrect field names in form\n\n**Solution:**\n\n```tsx\n\u003cFormField\n  control={form.control}\n  name=\"email\"\n  render={({ field }) =\u003e (\n    \u003cFormItem\u003e\n      \u003cFormLabel\u003eEmail\u003c/FormLabel\u003e\n      \u003cFormControl\u003e\n        \u003cInput type=\"email\" {...field} /\u003e\n      \u003c/FormControl\u003e\n      \u003cFormMessage /\u003e {/* Make sure to include this */}\n    \u003c/FormItem\u003e\n  )}\n/\u003e\n```\n\n#### Issue: Row selection not working correctly across pages\n\n**Possible Causes:**\n\n- Missing handleRowDeselection function\n- Not tracking selected rows across pages\n\n**Solution:**\n\n```typescript\n// In your DataTable component:\nconst [selectedRowIds, setSelectedRowIds] = React.useState\u003cRecord\u003cstring, boolean\u003e\u003e({});\n\n// Pass this to the getColumns function:\ngetColumns={(handleRowDeselection) =\u003e columns(handleRowDeselection)}\n\n// Define handleRowDeselection:\nconst handleRowDeselection = (rowId: string) =\u003e {\n  setSelectedRowIds((prev) =\u003e {\n    const newSelected = { ...prev };\n    delete newSelected[rowId];\n    return newSelected;\n  });\n};\n```\n\n#### Issue: API calls failing with validation errors\n\n**Possible Causes:**\n\n- Schema mismatch between client and server\n- Missing required fields\n- Format errors (e.g., date format)\n\n**Solution:**\n\n- Compare client and server schemas\n- Check API request/response in browser devtools\n- Add more detailed error reporting from your API\n\n#### Issue: \"TypeError: Cannot read property 'x' of undefined\"\n\n**Possible Causes:**\n\n- Trying to access nested properties that may not exist\n- Data structure mismatch\n\n**Solution:**\nUse optional chaining and nullish coalescing:\n\n```typescript\n// Instead of data.user.name (which may fail if user is undefined)\nconst userName = data?.user?.name ?? \"Unknown\";\n\n// Or with array access\nconst firstItem = data?.items?.[0]?.title ?? \"No items\";\n```\n\n---\n\n## Complete API Reference\n\n### DataTable Component Props\n\n| Prop                   | Type                                                                                                                               | Required | Default                 | Description                                  |\n| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------- | -------------------------------------------- |\n| `getColumns`           | `(handleRowDeselection?: (rowId: string) =\u003e void) =\u003e ColumnDef\u003cT\u003e[]`                                                               | Yes      | -                       | Function to get column definitions           |\n| `fetchDataFn`          | `(page: number, pageSize: number, search: string, dateRange: DateRange, sortBy: string, sortOrder: string) =\u003e QueryObserverResult` | Yes      | -                       | Function to fetch data                       |\n| `fetchByIdsFn`         | `(ids: number[]) =\u003e Promise\u003cT[]\u003e`                                                                                                  | No       | -                       | Function to fetch entities by IDs            |\n| `idField`              | `keyof T`                                                                                                                          | Yes      | -                       | Field to use as unique identifier            |\n| `pageSizeOptions`      | `number[]`                                                                                                                         | No       | `[10, 20, 30, 50, 100]` | Available page size options                  |\n| `renderToolbarContent` | `(options: ToolbarOptions\u003cT\u003e) =\u003e React.ReactNode`                                                                                  | No       | -                       | Function to render custom toolbar content    |\n| `exportConfig`         | `ExportConfig`                                                                                                                     | No       | -                       | Configuration for export functionality       |\n| `config`               | `DataTableConfig`                                                                                                                  | No       | -                       | Table configuration options                  |\n| `className`            | `string`                                                                                                                           | No       | -                       | Additional CSS class for the table container |\n| `tableClassName`       | `string`                                                                                                                           | No       | -                       | Additional CSS class for the table element   |\n\n### DataTableConfig Options\n\n| Option                     | Type      | Default | Description                        |\n| -------------------------- | --------- | ------- | ---------------------------------- |\n| `enableRowSelection`       | `boolean` | `false` | Enable row selection               |\n| `enableClickRowSelect`     | `boolean` | `false` | Allow clicking on row to select it |\n| `enableKeyboardNavigation` | `boolean` | `true`  | Enable keyboard navigation         |\n| `enableSearch`             | `boolean` | `true`  | Show search input                  |\n| `enableDateFilter`         | `boolean` | `false` | Show date range filter             |\n| `enableColumnVisibility`   | `boolean` | `true`  | Allow toggling column visibility   |\n| `enableUrlState`           | `boolean` | `true`  | Save table state in URL            |\n| `columnResizingTableId`    | `string`  | -       | ID for column resizing persistence |\n\n### ExportConfig Options\n\n| Option          | Type                     | Description                       |\n| --------------- | ------------------------ | --------------------------------- |\n| `columnMapping` | `Record\u003cstring, string\u003e` | Maps column keys to display names |\n| `columnWidths`  | `{ wch: number }[]`      | Column widths for Excel export    |\n| `headers`       | `string[]`               | Column keys to include in export  |\n| `entityName`    | `string`                 | Name for export files             |\n\n### ToolbarOptions Interface\n\n| Property             | Type         | Description                               |\n| -------------------- | ------------ | ----------------------------------------- |\n| `selectedRows`       | `T[]`        | Currently selected rows on current page   |\n| `allSelectedIds`     | `number[]`   | IDs of all selected rows across all pages |\n| `totalSelectedCount` | `number`     | Total number of selected rows             |\n| `resetSelection`     | `() =\u003e void` | Function to reset selection               |\n\n---\n\n## Example Implementations\n\n### Basic Example\n\n```tsx\n// src/app/(dashboard)/users/page.tsx\nimport { Suspense } from \"react\";\nimport UsersTable from \"./users-table\";\n\nexport default function UsersPage() {\n  return (\n    \u003cdiv className=\"container mx-auto py-10\"\u003e\n      \u003ch1 className=\"text-2xl font-bold mb-4\"\u003eUsers\u003c/h1\u003e\n      \u003cSuspense fallback={\u003cdiv\u003eLoading...\u003c/div\u003e}\u003e\n        \u003cUsersTable /\u003e\n      \u003c/Suspense\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n```tsx\n// src/app/(dashboard)/users/users-table/index.tsx\n\"use client\";\n\nimport { DataTable } from \"@/components/data-table/data-table\";\nimport { getColumns } from \"./components/columns\";\nimport { useExportConfig } from \"./utils/config\";\nimport { fetchUsersByIds } from \"@/api/user/fetch-users-by-ids\";\nimport { useUsersData } from \"./utils/data-fetching\";\nimport { ToolbarOptions } from \"./components/toolbar-options\";\nimport { User } from \"./schema\";\n\nexport default function UsersTable() {\n  return (\n    \u003cDataTable\u003cUser, any\u003e\n      getColumns={getColumns}\n      exportConfig={useExportConfig()}\n      fetchDataFn={useUsersData}\n      fetchByIdsFn={fetchUsersByIds}\n      idField=\"id\"\n      pageSizeOptions={[10, 20, 50, 100]}\n      renderToolbarContent={({\n        selectedRows,\n        allSelectedIds,\n        totalSelectedCount,\n        resetSelection,\n      }) =\u003e (\n        \u003cToolbarOptions\n          selectedUsers={selectedRows.map((row) =\u003e ({\n            id: row.id,\n            name: row.name,\n          }))}\n          allSelectedIds={allSelectedIds}\n          totalSelectedCount={totalSelectedCount}\n          resetSelection={resetSelection}\n        /\u003e\n      )}\n      config={{\n        enableRowSelection: true,\n        enableSearch: true,\n        enableDateFilter: true,\n        enableColumnVisibility: true,\n        enableUrlState: true,\n      }}\n    /\u003e\n  );\n}\n```\n\n### Complex Example with Custom Filters\n\n```tsx\n// src/app/(dashboard)/orders/orders-table/index.tsx\n\"use client\";\n\nimport * as React from \"react\";\nimport { DataTable } from \"@/components/data-table/data-table\";\nimport { getColumns } from \"./components/columns\";\nimport { useExportConfig } from \"./utils/config\";\nimport { fetchOrdersByIds } from \"@/api/order/fetch-orders-by-ids\";\nimport { useOrdersData } from \"./utils/data-fetching\";\nimport { ToolbarOptions } from \"./components/toolbar-options\";\nimport { Order, OrderStatus } from \"./schema\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { X } from \"lucide-react\";\n\nexport default function OrdersTable() {\n  const [statusFilter, setStatusFilter] = React.useState\u003cOrderStatus | \"all\"\u003e(\n    \"all\"\n  );\n\n  // Custom function that extends the base query to add the status filter\n  const fetchOrdersWithStatus = React.useCallback(\n    (\n      page: number,\n      pageSize: number,\n      search: string,\n      dateRange: any,\n      sortBy: string,\n      sortOrder: string\n    ) =\u003e {\n      return useOrdersData(\n        page,\n        pageSize,\n        search,\n        dateRange,\n        sortBy,\n        sortOrder,\n        statusFilter === \"all\" ? undefined : statusFilter\n      );\n    },\n    [statusFilter]\n  );\n\n  // Set isQueryHook property to true to match the DataTable expectations\n  fetchOrdersWithStatus.isQueryHook = true;\n\n  // Custom filters to render in the toolbar\n  const renderCustomFilters = () =\u003e (\n    \u003cdiv className=\"flex items-center gap-2\"\u003e\n      \u003cSelect\n        value={statusFilter}\n        onValueChange={(value: OrderStatus | \"all\") =\u003e setStatusFilter(value)}\n      \u003e\n        \u003cSelectTrigger className=\"h-8 w-[150px]\"\u003e\n          \u003cSelectValue placeholder=\"Status\" /\u003e\n        \u003c/SelectTrigger\u003e\n        \u003cSelectContent\u003e\n          \u003cSelectItem value=\"all\"\u003eAll Statuses\u003c/SelectItem\u003e\n          \u003cSelectItem value=\"pending\"\u003ePending\u003c/SelectItem\u003e\n          \u003cSelectItem value=\"processing\"\u003eProcessing\u003c/SelectItem\u003e\n          \u003cSelectItem value=\"completed\"\u003eCompleted\u003c/SelectItem\u003e\n          \u003cSelectItem value=\"cancelled\"\u003eCancelled\u003c/SelectItem\u003e\n        \u003c/SelectContent\u003e\n      \u003c/Select\u003e\n\n      {statusFilter !== \"all\" \u0026\u0026 (\n        \u003cBadge variant=\"outline\" className=\"h-8 px-3 flex items-center gap-1\"\u003e\n          Status: {statusFilter}\n          \u003cX\n            className=\"h-3 w-3 cursor-pointer\"\n            onClick={() =\u003e setStatusFilter(\"all\")}\n          /\u003e\n        \u003c/Badge\u003e\n      )}\n    \u003c/div\u003e\n  );\n\n  return (\n    \u003cDataTable\u003cOrder, any\u003e\n      getColumns={getColumns}\n      exportConfig={useExportConfig()}\n      fetchDataFn={fetchOrdersWithStatus}\n      fetchByIdsFn={fetchOrdersByIds}\n      idField=\"id\"\n      pageSizeOptions={[10, 20, 50, 100]}\n      renderToolbarContent={({\n        selectedRows,\n        allSelectedIds,\n        totalSelectedCount,\n        resetSelection,\n      }) =\u003e (\n        \u003cdiv className=\"flex items-center justify-between w-full\"\u003e\n          \u003cdiv\u003e{renderCustomFilters()}\u003c/div\u003e\n          \u003cToolbarOptions\n            selectedOrders={selectedRows.map((row) =\u003e ({\n              id: row.id,\n              reference: row.reference,\n            }))}\n            allSelectedIds={allSelectedIds}\n            totalSelectedCount={totalSelectedCount}\n            resetSelection={resetSelection}\n          /\u003e\n        \u003c/div\u003e\n      )}\n      config={{\n        enableRowSelection: true,\n        enableSearch: true,\n        enableDateFilter: true,\n        enableColumnVisibility: true,\n        enableUrlState: true,\n      }}\n    /\u003e\n  );\n}\n```\n\nBy following this documentation and the provided examples, you should now have a complete understanding of how to implement and customize the data table component for your specific needs. The component is designed to be highly flexible while maintaining performance and accessibility.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacksonkasi1%2Ftnks-data-table","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjacksonkasi1%2Ftnks-data-table","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacksonkasi1%2Ftnks-data-table/lists"}