COLUMN Extensions
Overview
Section titled “Overview”COLUMN extensions add custom columns to list and table views, displaying calculated data, plugin-specific metrics, or custom formatting.
Use Cases
Section titled “Use Cases”- Display calculated metrics (total hours, completion %)
- Show plugin-specific data
- Custom formatting and rendering
- Sortable and filterable columns
- Action columns with buttons
Basic Example
Section titled “Basic Example”{ point: 'work-unit.list.columns', plugin: 'timer', priority: 60, match: { businessType: ['SERVICE', 'PROJECT'] }, extension: { type: ExtensionType.COLUMN, id: 'hours-tracked', header: 'Time Tracked', accessor: (row) => getHoursTracked(row), cell: TimerColumnCell, width: 120, sortable: true, align: 'center' }}COLUMN Extension Properties
Section titled “COLUMN Extension Properties”Required
Section titled “Required”- type:
ExtensionType.COLUMN - id: Unique identifier
- header: Column header text
- accessor:
(row: any) => any- Extract value from row - cell: Cell renderer component
Optional
Section titled “Optional”- width: Column width in pixels
- sortable: Enable sorting
- filterable: Enable filtering
- align:
'left' | 'center' | 'right' - sticky: Pin column to left/right
Cell Component Example
Section titled “Cell Component Example”export function TimerColumnCell({ row }: { row: any }) { const hours = getHoursTracked(row);
if (hours === 0) { return <Text c="dimmed">—</Text>; }
return ( <Group gap="xs"> <IconClock size={14} /> <Text size="sm">{hours.toFixed(1)}h</Text> </Group> );}
export function getHoursTracked(row: any): number { return row.totalSeconds ? row.totalSeconds / 3600 : 0;}Sortable Column
Section titled “Sortable Column”{ type: ExtensionType.COLUMN, id: 'completion', header: 'Completion', accessor: (row) => row.completionPercentage || 0, cell: CompletionCell, sortable: true, sortFn: (a, b) => { return (a.completionPercentage || 0) - (b.completionPercentage || 0); }, width: 100}
function CompletionCell({ row }: { row: any }) { const percentage = row.completionPercentage || 0; return <Progress value={percentage} label={`${percentage}%`} />;}Action Column
Section titled “Action Column”{ type: ExtensionType.COLUMN, id: 'quick-actions', header: 'Actions', cell: QuickActionsCell, width: 120, align: 'center', sortable: false}
function QuickActionsCell({ row }: { row: any }) { const [isProcessing, setIsProcessing] = useState(false);
return ( <Group gap="xs"> <ActionIcon loading={isProcessing} onClick={async () => { setIsProcessing(true); await processItem(row.id); setIsProcessing(false); }} > <IconCheck /> </ActionIcon> <ActionIcon color="error"> <IconTrash /> </ActionIcon> </Group> );}Best Practices
Section titled “Best Practices”- Keep cell components simple and fast
- Cache expensive calculations
- Handle null/undefined values gracefully
- Use consistent formatting
- Provide meaningful empty states