# Generic Toggle Buttons for DataLoader

## Overview

The `genericToggle` function provides a reusable way to create toggle buttons for any boolean field in DataLoader tables, eliminating the need to create separate handler functions for each button.

## Usage

### In Module Configurations (sql/modules.sql)

Use `genericToggle` as the `onClick` handler and configure the behavior through the `dataset` property:

```json
{
  "onClick": "genericToggle",
  "dataset": {
    "tableName": "time_entries",
    "primaryKey": "time_entry_id",
    "field": "billable",
    "confirmTrue": "Mark as billable?",
    "confirmFalse": "Mark as non-billable?",
    "entityName": "entry"
  }
}
```

### Complete Example

```json
{
  "columnButtons": {
    "approved": {
      "showValue": false,
      "buttonMap": {
        "true": {
          "text": "✓ Approved",
          "className": "badge badge-success badge-sm cursor-pointer",
          "title": "Click to disapprove",
          "onClick": "genericToggle",
          "dataset": {
            "tableName": "expenses",
            "primaryKey": "expense_id",
            "field": "approved",
            "confirmTrue": "Approve this expense?",
            "confirmFalse": "Disapprove this expense?",
            "entityName": "expense"
          }
        },
        "false": {
          "text": "⏳ Pending",
          "className": "badge badge-warning badge-sm cursor-pointer",
          "title": "Click to approve",
          "onClick": "genericToggle",
          "dataset": {
            "tableName": "expenses",
            "primaryKey": "expense_id",
            "field": "approved",
            "confirmTrue": "Approve this expense?",
            "confirmFalse": "Disapprove this expense?",
            "entityName": "expense"
          }
        }
      }
    }
  }
}
```

## Configuration Properties

### Required Properties

- **`tableName`** (string) - The database table to update
- **`primaryKey`** (string) - The primary key field name (e.g., `"expense_id"`, `"time_entry_id"`)
- **`field`** (string) - The boolean field to toggle (e.g., `"approved"`, `"billable"`, `"active"`)

### Optional Properties

- **`confirmTrue`** (string) - Confirmation message when setting field to `true` (default: `"Set {field} to true?"`)
- **`confirmFalse`** (string) - Confirmation message when setting field to `false` (default: `"Set {field} to false?"`)
- **`entityName`** (string) - Entity name for error messages (default: `"item"`)

## How It Works

1. **User clicks the button** in the table
2. **Confirmation dialog** appears with the appropriate message
3. If confirmed:
   - **`getManager()`** is called to get the singleton sync manager
   - **`syncManager.edit()`** is called with:
     - The table name from `dataset.tableName`
     - An update object: `{ [primaryKey]: row[primaryKey], [field]: !currentValue }`
     - Options: `{ pushNow: true, syncNow: true }`
4. **Table refreshes** automatically after the update

## Benefits

### Before (Specific Functions + Deprecated syncManager)

You had to create a function for each button using the deprecated `dataLoader.syncManager`:

```javascript
// Old approach (deprecated)
export async function toggleTimeEntryBillable(ctx) {
    const { row, dataLoader } = ctx;
    const newValue = !row.billable;
    if (confirm(`Mark as ${newValue ? 'billable' : 'non-billable'}?`)) {
        // ❌ Deprecated: dataLoader.syncManager
        await dataLoader.syncManager.edit('time_entries', {
            time_entry_id: row.time_entry_id,
            billable: newValue
        }, { pushNow: true, syncNow: true });
    }
}

export async function toggleTimeEntryApproval(ctx) { /* ... */ }
export async function toggleExpenseBillable(ctx) { /* ... */ }
export async function toggleExpenseApproval(ctx) { /* ... */ }
// ...and so on for every button
```

### After (Generic Function)

Now you just configure the button:

```json
{
  "onClick": "genericToggle",
  "dataset": {
    "tableName": "time_entries",
    "primaryKey": "time_entry_id",
    "field": "billable",
    "confirmTrue": "Mark as billable?",
    "confirmFalse": "Mark as non-billable?",
    "entityName": "entry"
  }
}
```

**Advantages:**
- ✅ **No new functions needed** - Reuse `genericToggle` for all boolean fields
- ✅ **Configuration-driven** - All behavior defined in module config
- ✅ **Easy to maintain** - Update confirmation messages in one place
- ✅ **Consistent behavior** - Same error handling and sync logic everywhere
- ✅ **Less code** - One generic function instead of dozens of specific ones

## Use Cases

### 1. Approval Toggles

```json
{
  "onClick": "genericToggle",
  "dataset": {
    "tableName": "purchase_orders",
    "primaryKey": "po_id",
    "field": "approved",
    "confirmTrue": "Approve this PO?",
    "confirmFalse": "Revoke approval?",
    "entityName": "purchase order"
  }
}
```

### 2. Active/Inactive Status

```json
{
  "onClick": "genericToggle",
  "dataset": {
    "tableName": "users",
    "primaryKey": "user_id",
    "field": "is_active",
    "confirmTrue": "Activate this user?",
    "confirmFalse": "Deactivate this user?",
    "entityName": "user"
  }
}
```

### 3. Feature Flags

```json
{
  "onClick": "genericToggle",
  "dataset": {
    "tableName": "tasks",
    "primaryKey": "task_id",
    "field": "is_urgent",
    "confirmTrue": "Mark as urgent?",
    "confirmFalse": "Remove urgent flag?",
    "entityName": "task"
  }
}
```

### 4. Visibility Toggles

```json
{
  "onClick": "genericToggle",
  "dataset": {
    "tableName": "documents",
    "primaryKey": "document_id",
    "field": "is_public",
    "confirmTrue": "Make public?",
    "confirmFalse": "Make private?",
    "entityName": "document"
  }
}
```

## Pre-configured Helpers

For common use cases, pre-configured helpers are still available:

```javascript
// Available in window scope
window.toggleTimeEntryBillable(ctx);
window.toggleTimeEntryApproval(ctx);
window.toggleExpenseBillable(ctx);
window.toggleExpenseApproval(ctx);
window.toggleOneTimeCostBillable(ctx);
window.toggleOneTimeCostApproval(ctx);
```

These are created using `createToggleHandler()`:

```javascript
export const toggleTimeEntryBillable = createToggleHandler({
    tableName: 'time_entries',
    primaryKey: 'time_entry_id',
    field: 'billable',
    confirmTrue: 'Mark as billable?',
    confirmFalse: 'Mark as non-billable?',
    entityName: 'entry'
});
```

## Creating Custom Toggle Handlers

If you need to reuse a specific toggle configuration across multiple modules, you can create a custom handler:

```javascript
// In a helper file
import { createToggleHandler } from 'helpers/togglehelpers';

export const toggleTaskPriority = createToggleHandler({
    tableName: 'tasks',
    primaryKey: 'task_id',
    field: 'is_high_priority',
    confirmTrue: 'Mark as high priority?',
    confirmFalse: 'Remove high priority?',
    entityName: 'task'
});

// Export to window
window.toggleTaskPriority = toggleTaskPriority;
```

Then use it in module configs:

```json
{
  "onClick": "toggleTaskPriority"
}
```

## Error Handling

The `genericToggle` function includes:

- **Console error logging** for debugging
- **Toast notifications** (if `window.toaster` is available)
- **Graceful failure** - errors don't crash the app

## Implementation Location

- **Helper File**: `FrontEnd/js/helpers/toggleHelpers.js`
- **Imported In**: `FrontEnd/js/modules/Dashboard.js`
- **Available Globally**: `window.genericToggle`

## Migration

Existing modules using specific toggle functions will continue to work. To migrate:

1. Replace specific function names (e.g., `toggleTimeEntryBillable`) with `genericToggle`
2. Add `dataset` configuration with table/field details
3. Remove the old function if no longer used elsewhere

See `/migrations/20251004_column_button_handlers.sql` for the migration script.
