# DataTable Component Documentation

## Overview

A feature-rich data table component built with PrimeVue's DataTable. This component provides advanced functionality including server-side pagination, sorting, manual filtering with apply buttons, row selection, page data caching, and customizable column types with persistent state management.

## Basic Usage

```vue
<template>
  <DataTable
    :columns="tableColumns"
    :data="tableData"
    :tableActions="tableActions"
    table-name="my-table"
    @row-click="handleRowClick"
  />
</template>

<script setup>
import { ref } from "vue";
import DataTable from "./components/common/DataTable.vue";

const tableColumns = ref([
  {
    fieldName: "name",
    label: "Name",
    sortable: true,
    filterable: true,
  },
  {
    fieldName: "status",
    label: "Status",
    type: "status",
    sortable: true,
    filterable: true,
  },
]);

const tableData = ref([
  { id: 1, name: "John Doe", status: "completed" },
  { id: 2, name: "Jane Smith", status: "in progress" },
]);

const tableActions = ref([
  {
    label: "Add Item",
    action: () => console.log("Add clicked"),
    icon: "pi pi-plus",
    style: "primary",
    // Global action - always available
  },
  {
    label: "View Details",
    action: (rowData) => console.log("View:", rowData),
    icon: "pi pi-eye",
    style: "info",
    requiresSelection: true,
    // Single selection action - enabled when exactly one row selected
  },
  {
    label: "Edit",
    action: (rowData) => console.log("Edit:", rowData),
    icon: "pi pi-pencil",
    style: "secondary",
    rowAction: true,
    // Row action - appears in each row's actions column
  },
]);

const handleRowClick = (event) => {
  console.log("Row clicked:", event.data);
};
</script>
```

## Props

### `columns` (Array) - Required

- **Description:** Array of column configuration objects that define the table structure
- **Type:** `Array<Object>`
- **Required:** `true`

### `data` (Array) - Required

- **Description:** Array of data objects to display in the table
- **Type:** `Array<Object>`
- **Required:** `true`

### `tableName` (String) - Required

- **Description:** Unique identifier for the table, used for persistent filter state management
- **Type:** `String`
- **Required:** `true`

### `totalRecords` (Number)

- **Description:** Total number of records available on the server (for lazy loading)
- **Type:** `Number`
- **Default:** `0`

### `onLazyLoad` (Function)

- **Description:** Custom pagination event handler for server-side data loading
- **Type:** `Function`
- **Default:** `null`

### `filters` (Object)

- **Description:** Initial filter configuration object (used for non-lazy tables)
- **Type:** `Object`
- **Default:** `{ global: { value: null, matchMode: FilterMatchMode.CONTAINS } }`

### `tableActions` (Array)

- **Description:** Array of action objects that define interactive buttons for the table. Actions can be global (always available), single-selection (enabled when exactly one row is selected), row-specific (displayed per row), or bulk (for multiple selected rows).
- **Type:** `Array<Object>`
- **Default:** `[]`

## Server-Side Pagination & Lazy Loading

When `lazy` is set to `true`, the DataTable operates in server-side mode with the following features:

### Automatic Caching

- **Page Data Caching:** Previously loaded pages are cached to prevent unnecessary API calls
- **Cache Duration:** 5 minutes default expiration time
- **Cache Size:** Maximum 50 pages per table with automatic cleanup
- **Smart Cache Keys:** Based on page, sorting, and filter combinations

### Manual Filter Controls

- **Apply Button:** Filters are applied manually via button click to prevent excessive API calls
- **Clear Button:** Quick reset of all active filters
- **Enter Key Support:** Apply filters by pressing Enter in any filter field
- **Visual Feedback:** Shows active filters and pending changes

### Quick Page Navigation

- **Page Dropdown:** Jump directly to any page number
- **Page Info Display:** Shows current record range and totals
- **Persistent State:** Page selection survives component re-mounts

## Column Configuration

Each column object in the `columns` array supports the following properties:

### Basic Properties

- **`fieldName`** (String, required) - The field name in the data object
- **`label`** (String, required) - Display label for the column header
- **`sortable`** (Boolean, default: `false`) - Enables sorting for this column
- **`filterable`** (Boolean, default: `false`) - Enables row-level filtering for this column

### Column Types

- **`type`** (String) - Defines special rendering behavior for the column

#### Available Types:

##### `'status'` Type

Renders values as colored tags/badges:

```javascript
{
  fieldName: 'status',
  label: 'Status',
  type: 'status',
  sortable: true,
  filterable: true
}
```

**Status Colors:**

- `'completed'` → Success (green)
- `'in progress'` → Warning (yellow/orange)
- `'not started'` → Danger (red)
- Other values → Info (blue)

##### `'button'` Type

Renders values as clickable buttons:

```javascript
{
  fieldName: 'action',
  label: 'Action',
  type: 'button'
}
```

## Table Actions Configuration

Table actions allow you to add interactive buttons to your DataTable. Actions can be either global (displayed above the table) or row-specific (displayed in an actions column).

### Action Object Properties

Each action object in the `tableActions` array supports the following properties:

#### Basic Properties

- **`label`** (String, required) - Display text for the button
- **`action`** (Function, required) - Function to execute when button is clicked
- **`icon`** (String, optional) - PrimeVue icon class (e.g., 'pi pi-plus')
- **`style`** (String, optional) - Button severity: 'primary', 'secondary', 'success', 'info', 'warning', 'danger'
- **`size`** (String, optional) - Button size: 'small', 'normal', 'large'
- **`requiresSelection`** (Boolean, default: false) - When true, action appears above table but is only enabled when exactly one row is selected
- **`requiresMultipleSelection`** (Boolean, default: false) - Determines if action is for bulk operations on selected rows
- **`rowAction`** (Boolean, default: false) - When true, action appears in each row's actions column
- **`layout`** (Object, optional) - Layout configuration for action positioning and styling

#### Layout Configuration

The `layout` property allows you to control where and how actions are displayed:

##### For Top-Level Actions (Global and Single Selection)

```javascript
layout: {
  position: "left" | "center" | "right", // Where to position in action bar
  variant: "filled" | "outlined" | "text" // Visual style variant
}
```

##### For Row Actions

```javascript
layout: {
  priority: "primary" | "secondary" | "dropdown", // Display priority in row
  variant: "outlined" | "text" | "compact" | "icon-only" // Visual style
}
```

##### For Bulk Actions

```javascript
layout: {
  position: "left" | "center" | "right", // Where to position in bulk action bar
  variant: "filled" | "outlined" | "text" // Visual style variant
}
```

#### Action Types

##### Global Actions (default behavior)

Global actions are displayed above the table and are always available:

```javascript
{
  label: "Add New Item",
  action: () => {
    // Global action - no row data
    console.log("Opening create modal");
  },
  icon: "pi pi-plus",
  style: "primary"
  // No requiresSelection, requiresMultipleSelection, or rowAction properties
}
```

##### Single Selection Actions (`requiresSelection: true`)

Single selection actions are displayed above the table but are only enabled when exactly one row is selected. They receive the selected row data as a parameter:

```javascript
{
  label: "View Details",
  action: (rowData) => {
    // Single selection action - receives selected row data
    console.log("Viewing:", rowData.name);
    router.push(`/items/${rowData.id}`);
  },
  icon: "pi pi-eye",
  style: "info",
  requiresSelection: true
}
```

##### Row Actions (`rowAction: true`)

Row actions are displayed in an "Actions" column for each row and receive that row's data as a parameter:

```javascript
{
  label: "Edit",
  action: (rowData) => {
    // Row action - receives individual row data
    console.log("Editing:", rowData.name);
    openEditModal(rowData);
  },
  icon: "pi pi-pencil",
  style: "secondary",
  rowAction: true
}
```

##### Bulk Actions (`requiresMultipleSelection: true`)

Bulk actions are displayed above the table when rows are selected and receive an array of selected row data:

```javascript
{
  label: "Delete Selected",
  action: (selectedRows) => {
    // Bulk action - receives array of selected row data
    console.log("Deleting:", selectedRows.length, "items");
    selectedRows.forEach(row => deleteItem(row.id));
  },
  icon: "pi pi-trash",
  style: "danger",
  requiresMultipleSelection: true
}
```

### Example Table Actions Configuration

```javascript
const tableActions = [
  // Global action - shows above table, always available
  {
    label: "Add Client",
    action: () => modalStore.openModal("createClient"),
    icon: "pi pi-plus",
    style: "primary",
  },
  // Single selection action - shows above table, enabled when exactly one row selected
  {
    label: "View Details",
    action: (rowData) => router.push(`/clients/${rowData.id}`),
    icon: "pi pi-eye",
    style: "info",
    requiresSelection: true,
  },
  // Bulk action - shows when rows selected
  {
    label: "Delete Selected",
    action: (selectedRows) => {
      if (confirm(`Delete ${selectedRows.length} clients?`)) {
        selectedRows.forEach((row) => deleteClient(row.id));
      }
    },
    icon: "pi pi-trash",
    style: "danger",
    requiresMultipleSelection: true,
  },
  // Row actions - show in each row's actions column
  {
    label: "Edit",
    action: (rowData) => editClient(rowData),
    icon: "pi pi-pencil",
    style: "secondary",
    rowAction: true,
  },
  {
    label: "Quick View",
    action: (rowData) => showQuickPreview(rowData),
    icon: "pi pi-search",
    style: "info",
    rowAction: true,
  },
];
```

## Events

### `rowClick`

- **Description:** Emitted when a button-type column is clicked
- **Payload:** PrimeVue slot properties object containing row data
- **Usage:** `@row-click="handleRowClick"`

### `lazy-load`

- **Description:** Emitted when lazy loading is triggered (pagination, sorting, filtering)
- **Payload:** Event object with page, sorting, and filter information
- **Usage:** `@lazy-load="handleLazyLoad"`

### `page-change`

- **Description:** Emitted when page changes
- **Payload:** PrimeVue page event object

### `sort-change`

- **Description:** Emitted when sorting changes
- **Payload:** PrimeVue sort event object

### `filter-change`

- **Description:** Emitted when filters are applied
- **Payload:** PrimeVue filter event object

```javascript
const handleRowClick = (slotProps) => {
  console.log("Clicked row data:", slotProps.data);
  console.log("Row index:", slotProps.index);
};

const handleLazyLoad = async (event) => {
  // event contains: page, rows, sortField, sortOrder, filters
  console.log("Lazy load event:", event);

  // Load data from API based on event parameters
  const result = await Api.getData({
    page: event.page,
    pageSize: event.rows,
    sortField: event.sortField,
    sortOrder: event.sortOrder,
    filters: event.filters,
  });

  // Update component data
  tableData.value = result.data;
  totalRecords.value = result.totalRecords;
};
```

## Features

### Pagination

- **Rows per page options:** 5, 10, 20, 50
- **Default rows per page:** 10
- **Built-in pagination controls**

### Sorting

- **Multiple column sorting** support
- **Removable sort** - click to remove sort from a column
- **Sort indicators** in column headers

### Filtering

- **Manual filter application** with Apply/Clear buttons
- **Text-based search** for filterable columns
- **Persistent filter state** across component re-renders and page navigation
- **Visual filter feedback** showing active filters and pending changes
- **Enter key support** for quick filter application

### Selection

- **Multiple row selection** with checkboxes
- **Meta key selection** (Ctrl/Cmd + click for individual selection)
- **Unique row identification** using `dataKey="id"`

### Scrolling

- **Vertical scrolling** with fixed height (70vh)
- **Horizontal scrolling** for wide tables
- **Fixed headers** during scroll

### State Management

- **Persistent filters** using Pinia store (`useFiltersStore`)
- **Automatic filter initialization** on component mount
- **Cross-component filter synchronization**

### Table Actions

- **Global actions** displayed above the table for general operations
- **Row-specific actions** in dedicated actions column with row data access
- **Bulk actions** for selected rows with multi-selection support
- **Customizable button styles** with PrimeVue severity levels
- **Icon support** using PrimeVue icons
- **Automatic action handling** with error catching
- **Disabled state** during loading operations
- **Dynamic bulk action visibility** based on row selection

## Usage Examples

### Server-Side Paginated Table (Recommended for Large Datasets)

```vue
<script setup>
import { ref } from "vue";
import DataTable from "./components/common/DataTable.vue";
import Api from "./api.js";

const columns = [
  { fieldName: "id", label: "ID", sortable: true },
  { fieldName: "name", label: "Name", sortable: true, filterable: true },
  { fieldName: "email", label: "Email", filterable: true },
];

const tableData = ref([]);
const totalRecords = ref(0);
const isLoading = ref(false);

const handleLazyLoad = async (event) => {
  try {
    isLoading.value = true;

    // Convert PrimeVue event to API parameters
    const params = {
      page: event.page,
      pageSize: event.rows,
      sortField: event.sortField,
      sortOrder: event.sortOrder,
    };

    // Convert filters
    const filters = {};
    if (event.filters) {
      Object.keys(event.filters).forEach((key) => {
        if (event.filters[key]?.value) {
          filters[key] = event.filters[key];
        }
      });
    }

    // API call with caching support
    const result = await Api.getPaginatedData(params, filters);

    tableData.value = result.data;
    totalRecords.value = result.totalRecords;
  } catch (error) {
    console.error("Error loading data:", error);
    tableData.value = [];
    totalRecords.value = 0;
  } finally {
    isLoading.value = false;
  }
};
</script>

<template>
  <DataTable
    :data="tableData"
    :columns="columns"
    tableName="myTable"
    :lazy="true"
    :totalRecords="totalRecords"
    :loading="isLoading"
    :onLazyLoad="handleLazyLoad"
    @lazy-load="handleLazyLoad"
  />
</template>
```

### Interactive Table with Actions

```vue
<script setup>
import { useRouter } from "vue-router";
import { useModalStore } from "./stores/modal";

const router = useRouter();
const modalStore = useModalStore();

const columns = [
  { fieldName: "name", label: "Name", sortable: true, filterable: true },
  { fieldName: "status", label: "Status", type: "status", sortable: true },
  { fieldName: "email", label: "Email", filterable: true },
];

const data = [
  { id: 1, name: "John Doe", status: "completed", email: "john@example.com" },
  {
    id: 2,
    name: "Jane Smith",
    status: "in progress",
    email: "jane@example.com",
  },
];

const tableActions = [
  // Global action
  {
    label: "Add User",
    action: () => {
      modalStore.openModal("createUser");
    },
    icon: "pi pi-plus",
    style: "primary",
  },
  // Single selection action
  {
    label: "View Details",
    action: (rowData) => {
      router.push(`/users/${rowData.id}`);
    },
    icon: "pi pi-eye",
    style: "info",
    requiresSelection: true,
  },
  // Bulk actions
  {
    label: "Export Selected",
    action: (selectedRows) => {
      exportUsers(selectedRows);
    },
    icon: "pi pi-download",
    style: "success",
    requiresMultipleSelection: true,
  },
  {
    label: "Delete Selected",
    action: (selectedRows) => {
      if (confirm(`Delete ${selectedRows.length} users?`)) {
        bulkDeleteUsers(selectedRows.map((row) => row.id));
      }
    },
    icon: "pi pi-trash",
    style: "danger",
    requiresMultipleSelection: true,
  },
  // Row actions
  {
    label: "Edit",
    action: (rowData) => {
      modalStore.openModal("editUser", rowData);
    },
    icon: "pi pi-pencil",
    style: "secondary",
    rowAction: true,
  },
  {
    label: "Quick Actions",
    action: (rowData) => {
      showQuickActionsMenu(rowData);
    },
    icon: "pi pi-ellipsis-v",
    style: "info",
    rowAction: true,
  },
];

const deleteUser = async (userId) => {
  // API call to delete user
  await Api.deleteUser(userId);
};

const bulkDeleteUsers = async (userIds) => {
  // API call to delete multiple users
  await Api.bulkDeleteUsers(userIds);
};

const exportUsers = (users) => {
  // Export selected users to CSV/Excel
  const csv = generateCSV(users);
  downloadFile(csv, "users.csv");
};

const refreshData = () => {
  // Refresh table data
  location.reload();
};
</script>

<template>
  <DataTable
    :columns="columns"
    :data="data"
    :tableActions="tableActions"
    table-name="users-table"
  />
</template>
```

### Basic Client-Side Table

```vue
<script setup>
const columns = [
  { fieldName: "id", label: "ID", sortable: true },
  { fieldName: "name", label: "Name", sortable: true, filterable: true },
  { fieldName: "email", label: "Email", filterable: true },
];

const data = [
  { id: 1, name: "John Doe", email: "john@example.com" },
  { id: 2, name: "Jane Smith", email: "jane@example.com" },
];
</script>

<template>
  <DataTable :data="data" :columns="columns" tableName="basicTable" />
</template>
```

### Status Table

```vue
<script setup>
const columns = [
  { fieldName: "task", label: "Task", sortable: true, filterable: true },
  {
    fieldName: "status",
    label: "Status",
    type: "status",
    sortable: true,
    filterable: true,
  },
  { fieldName: "assignee", label: "Assignee", filterable: true },
];

const data = [
  { id: 1, task: "Setup project", status: "completed", assignee: "John" },
  { id: 2, task: "Write tests", status: "in progress", assignee: "Jane" },
  { id: 3, task: "Deploy app", status: "not started", assignee: "Bob" },
];
</script>

<template>
  <DataTable :columns="columns" :data="data" table-name="tasks-table" />
</template>
```

### Interactive Table with Buttons

```vue
<script setup>
const columns = [
  { fieldName: "name", label: "Name", sortable: true, filterable: true },
  { fieldName: "status", label: "Status", type: "status", sortable: true },
  { fieldName: "action", label: "Action", type: "button" },
];

const data = [
  { id: 1, name: "Project A", status: "completed", action: "View Details" },
  { id: 2, name: "Project B", status: "in progress", action: "Edit" },
];

const handleRowClick = (slotProps) => {
  const { data, index } = slotProps;
  console.log(`Action clicked for ${data.name} at row ${index}`);
  // Handle the action (navigate, open modal, etc.)
};
</script>

<template>
  <DataTable
    :columns="columns"
    :data="data"
    table-name="projects-table"
    @row-click="handleRowClick"
  />
</template>
```

### Custom Filters

```vue
<script setup>
import { FilterMatchMode } from "@primevue/core";

const customFilters = {
  global: { value: null, matchMode: FilterMatchMode.CONTAINS },
  name: { value: "John", matchMode: FilterMatchMode.STARTS_WITH },
};
</script>

<template>
  <DataTable
    :columns="columns"
    :data="data"
    :filters="customFilters"
    table-name="filtered-table"
  />
</template>
```

### Layout-Aware Actions Example

```vue
<script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
import { useModalStore } from "@/stores/modal";

const router = useRouter();
const modalStore = useModalStore();

const columns = [
  { fieldName: "name", label: "Client Name", sortable: true, filterable: true },
  { fieldName: "email", label: "Email", filterable: true },
  { fieldName: "status", label: "Status", type: "status", sortable: true },
];

const data = ref([
  { id: 1, name: "Acme Corp", email: "contact@acme.com", status: "completed" },
  {
    id: 2,
    name: "Tech Solutions",
    email: "info@tech.com",
    status: "in progress",
  },
]);

const tableActions = [
  // Left-positioned action with filled style
  {
    label: "Quick Export",
    icon: "pi pi-download",
    action: () => exportAllClients(),
    severity: "success",
    layout: { position: "left", variant: "outlined" },
  },
  // Center-positioned bulk action
  {
    label: "Archive Selected",
    icon: "pi pi-archive",
    action: (selectedRows) => archiveClients(selectedRows),
    severity: "warning",
    requiresMultipleSelection: true,
    layout: { position: "center", variant: "outlined" },
  },
  // Right-positioned main action
  {
    label: "Create Client",
    icon: "pi pi-plus",
    action: () => modalStore.openModal("createClient"),
    severity: "info",
    layout: { position: "right", variant: "filled" },
  },
  // Single selection action
  {
    label: "Edit Details",
    icon: "pi pi-pencil",
    action: (rowData) => router.push(`/clients/${rowData.id}/edit`),
    severity: "secondary",
    requiresSelection: true,
    layout: { position: "left", variant: "text" },
  },
  // Primary row action - most important
  {
    label: "View",
    icon: "pi pi-eye",
    action: (rowData) => router.push(`/clients/${rowData.id}`),
    severity: "info",
    rowAction: true,
    layout: { priority: "primary", variant: "outlined" },
  },
  // Secondary row action - less important
  {
    label: "Contact",
    icon: "pi pi-phone",
    action: (rowData) => initiateContact(rowData),
    severity: "success",
    rowAction: true,
    layout: { priority: "secondary", variant: "text" },
  },
  // Dropdown row action - additional options
  {
    label: "More",
    icon: "pi pi-ellipsis-v",
    action: (rowData) => showMoreOptions(rowData),
    rowAction: true,
    layout: { priority: "dropdown", variant: "icon-only" },
  },
];

const exportAllClients = () => {
  // Export logic
};

const archiveClients = (clients) => {
  // Archive logic
};

const initiateContact = (client) => {
  // Contact logic
};

const showMoreOptions = (client) => {
  // More options logic
};
</script>

<template>
  <DataTable
    :columns="columns"
    :data="data"
    :tableActions="tableActions"
    table-name="clients-with-layout"
  />
</template>
```

## Store Integration

The component integrates with a Pinia store (`useFiltersStore`) for persistent filter state:

### Store Methods Used

- `initializeTableFilters(tableName, columns)` - Initialize filters for a table
- `getTableFilters(tableName)` - Get current filters for a table
- `updateTableFilter(tableName, fieldName, value, matchMode)` - Update a specific filter

### Filter Persistence

- Filters are automatically saved when changed
- Filters persist across component re-mounts
- Each table maintains separate filter state based on `tableName`

## Styling

The component uses PrimeVue's default DataTable styling with:

- **Scrollable layout** with fixed 70vh height
- **Responsive design** that adapts to container width
- **Consistent spacing** and typography
- **Accessible color schemes** for status badges

## Performance Considerations

### Large Datasets

- **Virtual scrolling** is not implemented - consider for datasets > 1000 rows
- **Client-side pagination** may impact performance with very large datasets
- **Debounced filtering** helps with real-time search performance

### Memory Management

- **Filter state persistence** may accumulate over time
- Consider implementing filter cleanup for unused tables
- **Component re-rendering** is optimized through computed properties

## Best Practices

1. **Use unique `tableName`** for each table instance to avoid filter conflicts
2. **Define clear column labels** for better user experience
3. **Enable sorting and filtering** on searchable/comparable columns
4. **Use appropriate column types** (`status`, `button`) for better UX
5. **Handle `rowClick` events** for interactive functionality
6. **Consider data structure** - ensure `id` field exists for selection
7. **Test with various data sizes** to ensure performance
8. **Use consistent status values** for proper badge coloring

## Accessibility

The component includes:

- **Keyboard navigation** support via PrimeVue
- **Screen reader compatibility** with proper ARIA labels
- **High contrast** status badges for visibility
- **Focus management** for interactive elements
- **Semantic HTML structure** for assistive technologies

## Browser Support

Compatible with all modern browsers that support:

- Vue 3 Composition API
- ES6+ features
- CSS Grid and Flexbox
- PrimeVue components

## Dependencies

- **Vue 3** with Composition API
- **PrimeVue** DataTable, Column, Tag, Button, InputText components
- **@primevue/core** for FilterMatchMode
- **Pinia** store for state management (`useFiltersStore`)
