# DataLoader Column Buttons

The `columnButtons` feature in DataLoader allows you to display **conditional buttons in any column** (not just the actions column). Buttons can show different text, classes, and behavior based on the column's value or row data.

## Overview

This feature enables:
- **Buttons in any column** - not limited to the actions column
- **Value-based rendering** - different buttons for different values (e.g., "Approve" for pending, "View" for approved)
- **Dynamic styling** - change button classes/text based on cell value
- **Show value alongside button** - optionally display the actual cell value with the button
- **Conditional visibility** - hide/show buttons based on complex conditions
- **API integration** - buttons can trigger API calls with automatic refresh

## Basic Usage

### Simple Button Map

Show different buttons based on the column value:

```javascript
new DataLoader({
    tableName: 'cases',
    selector: '#caseTable',
    columnButtons: {
        status: {
            buttonMap: {
                'pending': { text: 'Approve', className: 'btn-success' },
                'approved': { text: 'View', className: 'btn-info' },
                'rejected': { text: 'Reopen', className: 'btn-warning' }
            }
        }
    }
});
```

### Inline Column Definition

Define buttons directly in the `columns` array:

```javascript
new DataLoader({
    tableName: 'invoices',
    selector: '#invoiceTable',
    columns: [
        'invoice_number',
        'client_name',
        'amount',
        {
            field: 'status',
            label: 'Status',
            buttonMap: {
                'draft': { 
                    text: 'Send', 
                    className: 'btn-primary',
                    onClick: (ctx) => sendInvoice(ctx.row)
                },
                'sent': { 
                    text: 'Download', 
                    className: 'btn-outline',
                    href: (ctx) => `/api/invoices/${ctx.row.invoice_id}/pdf`
                }
            }
        }
    ]
});
```

## Configuration Options

### Column Button Definition

Each column can have a button definition with these properties:

#### Display Options
- **`showValue`** (boolean, default: `false`) - Display the cell value alongside the button
- **`valuePosition`** (string, default: `'after'`) - Where to show the value: `'before'`, `'after'`, or `'wrap'`
- **`valueClass`** (string) - CSS classes for the value display
- **`valueFormatter`** (function) - Transform the value before display

#### Container Options
- **`wrap`** (boolean, default: `true`) - Wrap buttons in a container element
- **`containerClass`** (string, default: `'flex items-center gap-2'`) - CSS classes for the wrapper
- **`containerTag`** (string, default: `'div'`) - HTML tag for the wrapper
- **`containerAttributes`** (object) - Additional attributes for the wrapper

#### Button Options
- **`baseClass`** (string, default: `'btn btn-xs btn-outline'`) - Default CSS classes for all buttons
- **`buttonTag`** (string, default: `'button'`) - HTML tag for buttons (can be `'a'` for links)

#### Button Variants
- **`buttonMap`** (object) - Simple value-to-button mapping
- **`variants`** (array) - Array of conditional button definitions
- **`default`** or **`buttons`** - Default button(s) shown when no variant matches

### Button Properties

Each button can have:

#### Content
- **`text`** (string|function) - Button label
- **`html`** (string|function) - Raw HTML content (overrides `text`)
- **`icon`** (string) - Icon class (e.g., FontAwesome)
- **`iconPosition`** (string, default: `'before'`) - Icon placement: `'before'` or `'after'`

#### Styling
- **`className`** (string|function) - Additional CSS classes
- **`classList`** (array) - Array of class names
- **`baseClass`** (string) - Override the default base classes
- **`style`** (string|function) - Inline CSS styles
- **`disabled`** (boolean|function) - Disable the button
- **`hidden`** (boolean|function) - Hide the button

#### Actions
- **`onClick`** (function|string) - Click handler function or global function name
- **`href`** (string|function) - Link URL (when using `<a>` tag)
- **`target`** (string) - Link target (`'_blank'`, etc.)
- **`request`** (object) - API request configuration
- **`apiUrl`** (string) - Shortcut for API endpoint
- **`method`** (string, default: `'POST'`) - HTTP method
- **`refreshAfter`** (boolean, default: `true` for requests) - Reload table after action

#### Confirmation
- **`confirm`** (boolean|string|function) - Show confirmation dialog before action

#### Attributes
- **`title`** (string|function) - Tooltip text
- **`ariaLabel`** (string|function) - Accessibility label
- **`dataset`** (object) - `data-*` attributes
- **`attributes`** (object) - Additional HTML attributes

#### Guard
- **`guard`** (boolean) - Use action guard to prevent double-clicks
- **`guardOptions`** (object) - Options for the guard

## Conditional Rendering

### Using Variants

Variants allow complex conditional logic:

```javascript
columnButtons: {
    amount: {
        variants: [
            {
                min: 1000,
                buttons: [{
                    text: 'High Value',
                    className: 'btn-danger',
                    onClick: (ctx) => flagForReview(ctx.row)
                }]
            },
            {
                max: 100,
                buttons: [{
                    text: 'Low Value',
                    className: 'btn-secondary',
                    onClick: (ctx) => autoApprove(ctx.row)
                }]
            }
        ],
        default: {
            text: 'Standard Process',
            className: 'btn-primary'
        }
    }
}
```

### Matching Conditions

Variants support these condition types:

- **`equals`** / **`value`** / **`when`** - Exact match (case-insensitive for strings)
- **`notEquals`** - Not equal
- **`in`** - Value is in array
- **`notIn`** - Value is not in array
- **`match`** - Regex pattern
- **`truthy`** - Any truthy value
- **`falsy`** - Any falsy value
- **`min`** - Numeric minimum
- **`max`** - Numeric maximum
- **`test`** (function) - Custom predicate function
- **`predicate`** (string) - Global function name for custom logic

### Example: Status-Based Buttons

```javascript
columnButtons: {
    approval_status: {
        showValue: true,
        valuePosition: 'before',
        variants: [
            {
                equals: 'pending',
                buttons: [
                    {
                        text: 'Approve',
                        icon: 'fas fa-check',
                        className: 'btn-success btn-sm',
                        request: {
                            url: '/api/approvals/{id}/approve',
                            method: 'POST'
                        },
                        confirm: 'Approve this item?'
                    },
                    {
                        text: 'Reject',
                        icon: 'fas fa-times',
                        className: 'btn-danger btn-sm',
                        request: {
                            url: '/api/approvals/{id}/reject',
                            method: 'POST'
                        },
                        confirm: 'Reject this item?'
                    }
                ]
            },
            {
                equals: 'approved',
                buttons: [{
                    text: 'View Details',
                    className: 'btn-info btn-sm',
                    onClick: (ctx) => showDetails(ctx.row)
                }]
            },
            {
                equals: 'rejected',
                buttons: [{
                    text: 'Review',
                    className: 'btn-warning btn-sm',
                    onClick: (ctx) => reviewRejection(ctx.row)
                }]
            }
        ]
    }
}
```

## API Integration

### Simple API Call

```javascript
columnButtons: {
    status: {
        buttonMap: {
            'pending': {
                text: 'Process',
                apiUrl: '/api/items/{id}/process',
                method: 'POST',
                refreshAfter: true
            }
        }
    }
}
```

### Advanced Request Configuration

```javascript
columnButtons: {
    status: {
        buttonMap: {
            'pending': {
                text: 'Approve',
                request: {
                    url: '/api/approvals/{id}/approve',
                    method: 'PATCH',
                    body: (ctx) => ({
                        approved_by: ctx.dataLoader.currentUserId,
                        approved_at: new Date().toISOString(),
                        notes: 'Auto-approved'
                    }),
                    headers: {
                        'X-Custom-Header': 'value'
                    },
                    before: (ctx) => {
                        console.log('Approving', ctx.row);
                    },
                    after: (ctx) => {
                        console.log('Approved', ctx.data);
                        showToast('Item approved successfully');
                    },
                    throwOnError: true
                },
                refreshAfter: true
            }
        }
    }
}
```

### URL Interpolation

Button URLs support placeholders that are replaced with row data:

- **`{id}`** - Primary key value
- **`{value}`** - Cell value
- **`{column}`** - Column name
- **`{table}`** - Table name
- **`{fieldName}`** - Any field from the row (case-insensitive)

Example:
```javascript
apiUrl: '/api/cases/{case_id}/invoices/{invoice_id}/download'
// Interpolates to: /api/cases/123/invoices/456/download
```

## Context Object

Click handlers and dynamic functions receive a context object:

```javascript
{
    value,              // Cell value
    row,                // Full row data
    column,             // Column name
    columnKey,          // Original column key
    columnKeyLc,        // Lowercase column key
    formattedValue,     // Formatted cell value
    dataLoader,         // DataLoader instance
    tableName,          // Table name
    primaryKey,         // Primary key field name
    columnConfig,       // Column button configuration
    event,              // Click event (for onClick handlers)
    button,             // Button configuration
    originalKey,        // Original column key
    rowId               // Row ID
}
```

## Examples

### Example 1: Invoice Actions

```javascript
new DataLoader({
    tableName: 'invoices',
    selector: '#invoiceTable',
    columnButtons: {
        status: {
            showValue: true,
            valuePosition: 'before',
            buttonMap: {
                'draft': {
                    text: 'Send',
                    icon: 'fas fa-paper-plane',
                    className: 'btn-primary btn-sm',
                    onClick: async (ctx) => {
                        await sendInvoice(ctx.row.invoice_id);
                        ctx.dataLoader.fetchData();
                    },
                    confirm: 'Send this invoice to the client?'
                },
                'sent': {
                    text: 'Download',
                    icon: 'fas fa-download',
                    className: 'btn-outline btn-sm',
                    href: (ctx) => `/api/invoices/${ctx.row.invoice_id}/pdf`,
                    target: '_blank'
                },
                'paid': {
                    text: 'Receipt',
                    icon: 'fas fa-receipt',
                    className: 'btn-success btn-sm',
                    onClick: (ctx) => window.downloadCaseInvoice(ctx.row)
                }
            }
        }
    }
});
```

### Example 2: Priority Indicators

```javascript
columnButtons: {
    priority: {
        showValue: false,
        wrap: false,
        variants: [
            {
                equals: 'high',
                buttons: [{
                    text: 'HIGH',
                    className: 'badge badge-error badge-sm',
                    onClick: (ctx) => escalate(ctx.row)
                }]
            },
            {
                equals: 'medium',
                buttons: [{
                    text: 'MEDIUM',
                    className: 'badge badge-warning badge-sm'
                }]
            },
            {
                equals: 'low',
                buttons: [{
                    text: 'LOW',
                    className: 'badge badge-info badge-sm'
                }]
            }
        ]
    }
}
```

### Example 3: Approval Workflow

```javascript
columnButtons: {
    approval_status: {
        containerClass: 'flex gap-1',
        showValue: true,
        valueClass: 'font-semibold',
        variants: [
            {
                equals: 'pending',
                buttons: [
                    {
                        text: '✓',
                        className: 'btn btn-xs btn-success',
                        title: 'Approve',
                        apiUrl: '/api/requests/{id}/approve',
                        confirm: 'Approve this request?',
                        guard: true
                    },
                    {
                        text: '✗',
                        className: 'btn btn-xs btn-error',
                        title: 'Reject',
                        apiUrl: '/api/requests/{id}/reject',
                        confirm: 'Reject this request?',
                        guard: true
                    }
                ]
            },
            {
                in: ['approved', 'rejected'],
                buttons: [{
                    text: 'View',
                    className: 'btn btn-xs btn-ghost',
                    onClick: (ctx) => viewHistory(ctx.row)
                }]
            }
        ]
    }
}
```

### Example 4: Dynamic Button Text

```javascript
columnButtons: {
    quantity: {
        showValue: true,
        valueFormatter: (ctx) => `${ctx.value} units`,
        variants: [
            {
                max: 10,
                buttons: [{
                    text: (ctx) => `Order More (${ctx.value} left)`,
                    className: 'btn-warning btn-sm',
                    onClick: (ctx) => reorderStock(ctx.row)
                }]
            },
            {
                min: 100,
                buttons: [{
                    text: 'Overstock',
                    className: 'btn-info btn-sm disabled: true
                }]
            }
        ],
        default: {
            text: 'Normal Stock',
            className: 'btn-ghost btn-sm',
            disabled: true
        }
    }
}
```

## Best Practices

1. **Keep button logic simple** - Complex logic should be in separate functions
2. **Use guard for destructive actions** - Prevents double-clicks during API calls
3. **Provide confirmation for important actions** - Use `confirm` property
4. **Use meaningful button text and icons** - Improve UX with clear labels
5. **Set appropriate `refreshAfter`** - Auto-refresh table after state changes
6. **Use `showValue` strategically** - Show original value when buttons replace content
7. **Leverage URL interpolation** - Use `{fieldName}` placeholders for dynamic URLs
8. **Test offline behavior** - Ensure buttons handle offline scenarios gracefully

## Integration with Module Configs

Column buttons work seamlessly with module configurations:

```javascript
// In sql/modules.sql
{
    "type": "table",
    "options": {
        "tableName": "cases",
        "columns": [
            "case_number",
            "client_name",
            {
                "field": "status",
                "label": "Status",
                "columnButtons": {
                    "buttonMap": {
                        "open": { "text": "Work On", "className": "btn-primary" },
                        "closed": { "text": "Archive", "className": "btn-secondary" }
                    }
                }
            }
        ]
    }
}
```

## Notes

- Button handlers receive the full row context, not just the cell value
- Multiple buttons can be shown for a single value
- Buttons are rendered as HTML and support all standard button attributes
- Function-based properties support both inline functions and global function names
- The feature is fully compatible with inline editing and row selection
- All button clicks stop event propagation by default to prevent row selection

## File Location

This documentation lives at `docs/frontend/DataLoaderColumnButtons.md`.
