npm package: @hirehq/smart-table
Description: A powerful, feature-rich Angular smart table component built with PrimeNG, designed for enterprise applications. This library provides a comprehensive table solution with advanced features like pagination, sorting, filtering, custom templates, and more.
Latest version: 1.3.4

# @hirehq/smart-table

A powerful, feature-rich Angular smart table component built with PrimeNG, designed for enterprise applications. This library provides a comprehensive table solution with advanced features like pagination, sorting, filtering, custom templates, and more.

## 🚀 Features

- **Advanced Table Management**: Sortable, filterable, and paginated tables
- **Row Selection**: Single and multiple selection with checkbox/radio support
- **Custom Templates**: Built-in templates for badges, profiles, and actions
- **Tabbed Interface**: Support for multiple data views with URL synchronization
- **Responsive Design**: Mobile-friendly with adaptive layouts
- **TypeScript Support**: Full type safety with comprehensive interfaces
- **PrimeNG Integration**: Built on top of PrimeNG components for consistent UI
- **Customizable Styling**: Flexible theming and styling options
- **Event Handling**: Comprehensive event system for user interactions
- **Search & Filter**: Global search and column-specific filtering
- **Export Support**: CSV and Excel export capabilities
- **Bulk Operations**: Support for bulk actions on selected rows

## 📦 Installation

### Prerequisites

Make sure you have the following dependencies in your project:

```json
{
  "dependencies": {
    "@angular/common": "^19.2.0",
    "@angular/core": "^19.2.0",
    "@primeuix/themes": "^1.2.3",
    "primeng": "^20.0.1"
  }
}
```

### Install the Library

```bash
npm install @hirehq/smart-table
```

### Import in Your Module

```typescript
import { HirehqSmartTableComponent } from '@hirehq/smart-table';

@Component({
  // ... your component config
  imports: [HirehqSmartTableComponent],
  standalone: true
})
```

## 🎯 Basic Usage

### Simple Table

```typescript
import { Component } from '@angular/core';
import { HirehqSmartTableComponent, SmartTableConfig, SmartTableColumn } from '@hirehq/smart-table';

@Component({
  selector: 'app-my-table',
  template: `
    <lib-hirehq-smart-table
      [config]="tableConfig"
      [data]="tableData"
      [loading]="isLoading"
      (rowClick)="onRowClick($event)"
      (selectionChange)="onSelectionChange($event)"
      (pageChange)="onPageChange($event)"
    ></lib-hirehq-smart-table>
  `,
  imports: [HirehqSmartTableComponent],
  standalone: true
})
export class MyTableComponent {
  isLoading = false;
  tableData = [
    { id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
    { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' }
  ];

  tableConfig: SmartTableConfig = {
    columns: [
      {
        label: 'ID',
        columnKey: 'id',
        sortable: true,
        width: '80px'
      },
      {
        label: 'Name',
        columnKey: 'name',
        sortable: true,
        search: { enabled: true, placeholder: 'Search names...' }
      },
      {
        label: 'Email',
        columnKey: 'email',
        sortable: true
      },
      {
        label: 'Status',
        columnKey: 'status',
        type: 'badge',
        templateMapping: {
          badge: {
            conditions: [
              { value: 'active', severity: 'success' },
              { value: 'inactive', severity: 'danger' }
            ]
          }
        }
      }
    ],
    selection: {
      enabled: true,
      mode: 'multiple',
      type: 'checkbox'
    },
    preferences: {
      pagination: {
        enabled: true,
        pageSize: 10,
        pageSizeOptions: [5, 10, 20, 50]
      }
    }
  };

  onRowClick(row: any) {
    console.log('Row clicked:', row);
  }

  onSelectionChange(event: any) {
    console.log('Selection changed:', event);
  }

  onPageChange(event: any) {
    console.log('Page changed:', event);
  }
}
```

## ⚙️ Configuration

### SmartTableConfig Interface

```typescript
interface SmartTableConfig<T = any> {
  columns: SmartTableColumn<T>[];
  data?: T[];
  loading?: boolean;
  title?: string;
  showHeader?: boolean;
  scrollHeight?: string;
  
  // Row Selection
  selection?: {
    enabled: boolean;
    mode?: 'single' | 'multiple';
    type?: 'checkbox' | 'radio';
    position?: 'left' | 'right';
    frozen?: boolean;
    width?: string;
    onSelectionChange?: (selectedRows: T[]) => void;
  };
  
  // Table Preferences
  preferences?: {
    pagination?: {
      enabled: boolean;
      pageSize?: number;
      pageSizeOptions?: number[];
      showFirstLast?: boolean;
      showPageNumbers?: boolean;
      totalRecords?: number;
    };
    globalSearch?: {
      enabled: boolean;
      placeholder?: string;
      onSearch?: (value: string) => void;
    };
    tabs?: {
      enabled: boolean;
      tabs?: SmartTableTab[];
      defaultTab?: number;
      scrollable?: boolean;
      showBadges?: boolean;
      onClick?: (tab: SmartTableTab, index: number) => void;
    };
    onSort?: (sortKey: string, direction: 'asc' | 'desc') => void;
  };
  
  // Export Actions
  exportActions?: {
    enabled: boolean;
    formats?: ('csv' | 'xlsx')[];
    onExport?: (format: 'csv' | 'xlsx', data: T[]) => void;
  };
}
```

### Column Configuration

```typescript
interface SmartTableColumn<T = any> {
  label: string;                    // Column header text
  columnKey: string;                // Data property key
  type?: 'badge' | 'profile' | 'action' | 'text' | 'custom' | 'date';
  visible?: boolean;                // Show/hide column
  sortable?: boolean;               // Enable sorting
  width?: string;                   // Column width
  search?: {                        // Column-specific search
    enabled: boolean;
    placeholder?: string;
    type?: 'text' | 'select' | 'date';
  };
  templateMapping?: {               // Template-specific configuration
    badge?: BadgeConfig;
    profile?: ProfileConfig;
    action?: ActionConfig;
  };
}
```

## 🎨 Templates

### Badge Template

```typescript
{
  label: 'Status',
  columnKey: 'status',
  type: 'badge',
  templateMapping: {
    badge: {
      conditions: [
        { value: 'active', severity: 'success' },
        { value: 'pending', severity: 'warn' },
        { value: 'inactive', severity: 'danger' }
      ],
      size: 'normal'
    }
  }
}
```

### Profile Template

```typescript
{
  label: 'User',
  columnKey: 'user',
  type: 'profile',
  templateMapping: {
    profile: {
      image: 'avatar',
      title: 'name',
      subtitle: 'role',
      size: 'normal',
      options: {
        showImage: true
      }
    }
  }
}
```

### Action Template

```typescript
{
  label: 'Actions',
  columnKey: 'actions',
  type: 'action',
  templateMapping: {
    action: {
      actions: [
        {
          key: 'view',
          label: 'View',
          icon: 'pi pi-eye',
          severity: 'info',
          onClick: (row: any) => this.viewItem(row)
        },
        {
          key: 'edit',
          label: 'Edit',
          icon: 'pi pi-pencil',
          severity: 'secondary',
          showMenu: true,
          menuItems: [
            {
              key: 'quick-edit',
              label: 'Quick Edit',
              icon: 'pi pi-pencil',
              onClick: (row: any) => this.quickEdit(row)
            },
            {
              key: 'advanced-edit',
              label: 'Advanced Edit',
              icon: 'pi pi-cog',
              onClick: (row: any) => this.advancedEdit(row)
            }
          ]
        },
        {
          key: 'delete',
          label: 'Delete',
          icon: 'pi pi-trash',
          severity: 'danger',
          onClick: (row: any) => this.deleteItem(row)
        }
      ]
    }
  }
}
```

## 📱 Tabs Support

```typescript
@Component({
  template: `
    <lib-hirehq-smart-table
      [config]="tableConfig"
      [data]="tableData"
      [tabs]="tableTabs"
      [defaultTab]="0"
      [enableUrlSync]="true"
      (tabChange)="onTabChange($event)"
    ></lib-hirehq-smart-table>
  `
})
export class MyComponent {
  tableTabs: SmartTableTab[] = [
    { label: 'All Users', value: 0, key: 'all' },
    { label: 'Active Users', value: 1, key: 'active' },
    { label: 'Inactive Users', value: 2, key: 'inactive' }
  ];

  onTabChange(event: { tab: SmartTableTab; index: number }) {
    console.log('Tab changed to:', event.tab.key);
    // Load data based on selected tab
  }
}
```

## ✅ Selection

The smart table provides comprehensive row selection capabilities with both single and multiple selection modes.

### Basic Selection Configuration

```typescript
tableConfig: SmartTableConfig = {
  selection: {
    enabled: true,
    mode: 'multiple', // or 'single'
    type: 'checkbox', // or 'radio'
    position: 'left', // or 'right' (default: 'left')
    frozen: true, // Keep selection column visible when scrolling
    width: '5rem', // Custom width (default: '4rem')
    onSelectionChange: (selectedRows) => {
      console.log('Selected rows:', selectedRows);
    }
  },
  columns: [
    // ... your columns
  ]
};
```

### Selection Modes

#### Multiple Selection (Checkbox)

```typescript
@Component({
  template: `
    <lib-hirehq-smart-table
      [config]="tableConfig"
      [data]="tableData"
      (selectionChange)="onSelectionChange($event)"
    ></lib-hirehq-smart-table>
  `
})
export class MyComponent {
  selectedUsers: any[] = [];

  tableConfig: SmartTableConfig = {
    selection: {
      enabled: true,
      mode: 'multiple',
      type: 'checkbox'
    },
    columns: [
      { label: 'Name', columnKey: 'name' },
      { label: 'Email', columnKey: 'email' },
      { label: 'Status', columnKey: 'status' }
    ]
  };

  onSelectionChange(event: SmartTableSelectionEvent) {
    this.selectedUsers = event.rows;
    console.log('Selection changed:', {
      action: event.action, // 'selected' | 'deselected' | 'headerToggle'
      selectedRow: event.row,
      allSelectedRows: event.rows,
      allChecked: event.allChecked
    });
  }
}
```

#### Single Selection (Radio)

```typescript
tableConfig: SmartTableConfig = {
  selection: {
    enabled: true,
    mode: 'single',
    type: 'radio'
  },
  columns: [
    { label: 'Name', columnKey: 'name' },
    { label: 'Email', columnKey: 'email' }
  ]
};

onSelectionChange(event: SmartTableSelectionEvent) {
  const selectedUser = event.row;
  console.log('Selected user:', selectedUser);
}
```

### Advanced Selection Configuration

#### Selection Column Position

Position the selection column on either the left (default) or right side of the table:

```typescript
// Left-positioned selection column (default)
selection: {
  enabled: true,
  mode: 'multiple',
  position: 'left' // Default position
}

// Right-positioned selection column
selection: {
  enabled: true,
  mode: 'multiple',
  position: 'right'
}
```

#### Frozen Selection Column

Keep the selection column visible while horizontally scrolling through the table:

```typescript
selection: {
  enabled: true,
  mode: 'multiple',
  position: 'left',
  frozen: true, // Selection column stays visible during horizontal scroll
  width: '4rem'
}
```

#### Custom Selection Column Width

Customize the width of the selection column to fit your design needs:

```typescript
selection: {
  enabled: true,
  mode: 'multiple',
  width: '6rem', // Custom width (default is '4rem')
  position: 'left',
  frozen: true
}
```

#### Complete Advanced Configuration Example

```typescript
@Component({
  template: `
    <lib-hirehq-smart-table
      [config]="tableConfig"
      [data]="wideTableData"
      (selectionChange)="onSelectionChange($event)"
    ></lib-hirehq-smart-table>
  `
})
export class WideTableComponent {
  wideTableData = [
    { 
      id: 1, name: 'John', email: 'john@example.com', 
      department: 'Engineering', position: 'Senior Developer',
      salary: 85000, startDate: '2020-01-15', manager: 'Alice Smith'
    },
    // ... more data
  ];

  tableConfig: SmartTableConfig = {
    selection: {
      enabled: true,
      mode: 'multiple',
      type: 'checkbox',
      position: 'right', // Position on right side
      frozen: true, // Keep visible during scroll
      width: '5rem' // Slightly wider for better UX
    },
    scrollHeight: '400px',
    columns: [
      { label: 'ID', columnKey: 'id', width: '80px' },
      { label: 'Name', columnKey: 'name', width: '150px' },
      { label: 'Email', columnKey: 'email', width: '200px' },
      { label: 'Department', columnKey: 'department', width: '150px' },
      { label: 'Position', columnKey: 'position', width: '200px' },
      { label: 'Salary', columnKey: 'salary', width: '120px' },
      { label: 'Start Date', columnKey: 'startDate', width: '120px' },
      { label: 'Manager', columnKey: 'manager', width: '150px' }
    ]
  };

  onSelectionChange(event: SmartTableSelectionEvent) {
    console.log('Selection changed:', event);
    // Handle selection with frozen column functionality
  }
}
```

### Selection Events

The component emits detailed selection events:

```typescript
interface SmartTableSelectionEvent<T = any> {
  rows: T[];           // All currently selected rows
  index?: number;      // Index of the row that triggered the event
  row?: T;            // The specific row that was selected/deselected
  action?: 'selected' | 'deselected' | 'headerToggle'; // Action type
  allChecked?: boolean; // Whether all rows are selected (for header toggle)
}
```

### Handling Selection Events

```typescript
export class UserManagementComponent {
  selectedUsers: User[] = [];
  
  onSelectionChange(event: SmartTableSelectionEvent<User>) {
    switch (event.action) {
      case 'selected':
        console.log(`Selected user: ${event.row?.name}`);
        this.handleUserSelected(event.row!);
        break;
        
      case 'deselected':
        console.log(`Deselected user: ${event.row?.name}`);
        this.handleUserDeselected(event.row!);
        break;
        
      case 'headerToggle':
        if (event.allChecked) {
          console.log('All users selected');
          this.handleAllUsersSelected(event.rows);
        } else {
          console.log('All users deselected');
          this.handleAllUsersDeselected();
        }
        break;
    }
    
    // Update local state
    this.selectedUsers = event.rows;
    
    // Enable/disable bulk actions
    this.updateBulkActionButtons();
  }
  
  private handleUserSelected(user: User) {
    // Individual selection logic
  }
  
  private handleUserDeselected(user: User) {
    // Individual deselection logic
  }
  
  private handleAllUsersSelected(users: User[]) {
    // Bulk selection logic
  }
  
  private handleAllUsersDeselected() {
    // Clear selection logic
  }
  
  private updateBulkActionButtons() {
    // Enable bulk operations based on selection count
    const hasSelection = this.selectedUsers.length > 0;
    // Update UI state accordingly
  }
}
```

### Programmatic Selection Control

```typescript
export class MyComponent {
  @ViewChild(HirehqSmartTableComponent) smartTable!: HirehqSmartTableComponent;
  
  // Programmatically select rows
  selectSpecificUsers() {
    const usersToSelect = this.tableData.filter(user => user.status === 'active');
    this.smartTable.selectedRows = usersToSelect;
  }
  
  // Clear all selections
  clearSelection() {
    this.smartTable.selectedRows = [];
  }
  
  // Select first row
  selectFirstRow() {
    if (this.tableData.length > 0) {
      this.smartTable.selectedRows = [this.tableData[0]];
    }
  }
}
```

### Selection with Custom Actions

```typescript
@Component({
  template: `
    <div class="selection-actions" *ngIf="selectedUsers.length > 0">
      <p-button
        label="Delete Selected ({{ selectedUsers.length }})"
        icon="pi pi-trash"
        severity="danger"
        (onClick)="deleteSelectedUsers()"
      ></p-button>
      
      <p-button
        label="Export Selected"
        icon="pi pi-download"
        severity="secondary"
        (onClick)="exportSelectedUsers()"
      ></p-button>
    </div>
    
    <lib-hirehq-smart-table
      [config]="tableConfig"
      [data]="tableData"
      (selectionChange)="onSelectionChange($event)"
    ></lib-hirehq-smart-table>
  `
})
export class UserManagementComponent {
  selectedUsers: User[] = [];
  
  tableConfig: SmartTableConfig = {
    selection: {
      enabled: true,
      mode: 'multiple',
      type: 'checkbox'
    },
    // ... columns configuration
  };
  
  onSelectionChange(event: SmartTableSelectionEvent<User>) {
    this.selectedUsers = event.rows;
  }
  
  deleteSelectedUsers() {
    if (this.selectedUsers.length === 0) return;
    
    // Confirm deletion
    if (confirm(`Delete ${this.selectedUsers.length} users?`)) {
      // Perform bulk delete operation
      this.userService.deleteUsers(this.selectedUsers.map(u => u.id))
        .subscribe(() => {
          // Refresh table data
          this.loadUsers();
          // Clear selection
          this.selectedUsers = [];
        });
    }
  }
  
  exportSelectedUsers() {
    if (this.selectedUsers.length === 0) return;
    
    // Export selected users
    this.exportService.exportUsers(this.selectedUsers, 'selected-users.csv');
  }
}
```

### Selection Best Practices

1. **Provide Clear Visual Feedback**: Always show the number of selected items
2. **Bulk Operations**: Enable bulk actions when multiple items are selected
3. **Clear Selection**: Provide a way to clear all selections
4. **Performance**: For large datasets, consider virtual scrolling with selection
5. **Accessibility**: Use proper ARIA labels for screen readers
6. **Frozen Selection**: Use frozen selection columns for wide tables to maintain usability
7. **Position Strategy**: Consider positioning selection on the right for tables with many action buttons on the left
8. **Width Optimization**: Adjust selection column width based on your UI density requirements

```typescript
// Good: Clear visual feedback with advanced selection
template: `
  <div class="selection-summary" *ngIf="selectedUsers.length > 0">
    {{ selectedUsers.length }} user(s) selected
    <p-button label="Clear Selection" (onClick)="clearSelection()"></p-button>
  </div>
`

// Good: Wide table with right-positioned frozen selection
tableConfig: SmartTableConfig = {
  selection: {
    enabled: true,
    mode: 'multiple',
    position: 'right', // Better for tables with left-side actions
    frozen: true, // Essential for wide tables
    width: '4.5rem' // Optimized for checkbox + padding
  },
  scrollHeight: '500px',
  columns: [
    // Many columns...
  ]
};

// Good: Narrow table with standard left selection
tableConfig: SmartTableConfig = {
  selection: {
    enabled: true,
    mode: 'single',
    position: 'left', // Standard position for simple tables
    width: '3.5rem' // Narrower for radio buttons
  },
  columns: [
    // Few columns...
  ]
};
```

### Selection Configuration Summary

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `enabled` | `boolean` | - | Enable/disable row selection |
| `mode` | `'single' \| 'multiple'` | `'multiple'` | Selection mode |
| `type` | `'checkbox' \| 'radio'` | `'checkbox'` | Visual selection indicator |
| `position` | `'left' \| 'right'` | `'left'` | Position of selection column |
| `frozen` | `boolean` | `false` | Keep selection column visible during scroll |
| `width` | `string` | `'4rem'` | Custom width for selection column |
| `onSelectionChange` | `function` | - | Callback function for selection changes |

## 🔍 Search and Filtering

### Global Search

```typescript
tableConfig: SmartTableConfig = {
  preferences: {
    globalSearch: {
      enabled: true,
      placeholder: 'Search all columns...',
      onSearch: (value) => {
        console.log('Global search:', value);
      }
    }
  },
  // ... other config
};
```

### Column-Specific Search

```typescript
{
  label: 'Category',
  columnKey: 'category',
  search: {
    enabled: true,
    type: 'select',
    options: [
      { label: 'Technology', value: 'tech' },
      { label: 'Finance', value: 'finance' },
      { label: 'Healthcare', value: 'healthcare' }
    ]
  }
}
```

## 📊 Pagination

The library includes a built-in pagination component with the following features:

- Page size selection
- Page navigation (first, previous, next, last)
- Page number display
- Total records count
- Responsive design

```typescript
tableConfig: SmartTableConfig = {
  pagination: {
    enabled: true,
    pageSize: 20,
    pageSizeOptions: [10, 20, 50, 100],
    showFirstLast: true,
    showPageNumbers: true
  }
};
```

## 🎭 Events

The component emits various events for user interactions:

```typescript
<lib-hirehq-smart-table
  (rowClick)="onRowClick($event)"
  (columnClick)="onColumnClick($event)"
  (selectionChange)="onSelectionChange($event)"
  (pageChange)="onPageChange($event)"
  (sortChange)="onSortChange($event)"
  (globalSearchChange)="onSearchChange($event)"
  (actionClick)="onActionClick($event)"
  (tabChange)="onTabChange($event)"
></lib-hirehq-smart-table>
```

## 🎨 Styling

### CSS Variables

The component uses CSS variables for dynamic styling:

```css
.smart-table-root-container {
  --smart-table-height: 400px;
  --smart-table-border-radius: 8px;
  --smart-table-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
```

### Custom Classes

```typescript
{
  label: 'Status',
  columnKey: 'status',
  cellClass: (row: any) => {
    return row.status === 'active' ? 'text-green-600' : 'text-red-600';
  },
  rowClass: (row: any) => {
    return row.isHighlighted ? 'bg-yellow-50' : '';
  }
}
```

## 📱 Responsive Design

The table automatically adapts to different screen sizes:

- Mobile-first approach
- Responsive column hiding
- Touch-friendly interactions
- Adaptive pagination controls

## 🔧 Advanced Configuration

### Custom Templates

```typescript
{
  label: 'Custom',
  columnKey: 'custom',
  type: 'custom',
  customTemplate: YourCustomTemplateComponent
}
```

### Frozen Columns

```typescript
{
  label: 'ID',
  columnKey: 'id',
  frozen: true,
  alignFrozen: 'left',
  width: '80px'
}
```

### Export Configuration

```typescript
tableConfig: SmartTableConfig = {
  export: {
    enabled: true,
    formats: ['csv', 'xlsx']
  }
};
```

## 🚀 Performance Tips

1. **Use OnPush Change Detection**: The component is optimized for OnPush strategy
2. **Virtual Scrolling**: For large datasets, consider implementing virtual scrolling
3. **Lazy Loading**: Load data in chunks for better performance
4. **Debounce Search**: Implement debouncing for search inputs

## 🐛 Troubleshooting

### Common Issues

1. **Table not rendering**: Check if PrimeNG modules are properly imported
2. **Styling issues**: Ensure PrimeNG themes are included in your project
3. **Performance problems**: Verify data structure and consider pagination

### Debug Mode

Enable console logging for debugging:

```typescript
// The component logs configuration changes to console
console.log('config', val); // Check browser console for config updates
```

## 📚 Examples

See the following files for comprehensive examples:

- `src/lib/pagination/example-usage.ts` - Pagination examples
- `src/lib/templates/action-example-usage.ts` - Action template examples

## 🤝 Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests if applicable
5. Submit a pull request

## 📄 License

This project is licensed under the MIT License.

## 🆘 Support

For support and questions:

- Check the documentation
- Review the examples
- Open an issue on GitHub
- Contact the development team

---

**Built with ❤️ by the HireHQ team**