@glyphtek/unspecd
Version:
A declarative UI framework for building internal tools and dashboards with TypeScript. Create interactive tables, forms, and dashboards using simple specifications.
439 lines (394 loc) • 13.4 kB
text/typescript
/**
* Task Management Dashboard - Advanced SmartTable Example for Unspec'd Framework
*
* This example demonstrates the editableTable content type with all advanced features:
* - Global filtering with multiple input types
* - Server-side pagination with proper data slicing
* - Column sorting with multiple sort directions
* - Inline editing with different field types (text, dropdown, number)
* - Row actions (delete functionality)
* - Table actions (global create button)
*
* Key features demonstrated:
* - Complex data loading with filtering, sorting, and pagination
* - Real-time updates with itemUpdater
* - Mixed column types (editable text, sortable fields, dropdown selects)
* - Action handling with confirmation dialogs
* - Comprehensive mock data with realistic task management scenarios
*/
import type { ToolSpec } from '../../src/lib/dsl-schema.js';
/**
* Interface for task data structure
*/
interface Task {
taskId: string;
taskName: string;
assignee: string;
status: 'To Do' | 'In Progress' | 'Done';
priority: number; // 1-5 scale (1 = Low, 5 = Critical)
createdAt: string;
dueDate: string;
}
/**
* Interface for data loading parameters
*/
interface TaskQueryParams {
// Filtering
statusFilter?: string;
assigneeFilter?: string;
// Pagination
page?: number;
pageSize?: number;
// Sorting
sortField?: string;
sortDirection?: 'asc' | 'desc';
}
/**
* Mock task data - comprehensive dataset for testing all features
*/
const MOCK_TASKS: Task[] = [
{
taskId: 'TASK-001',
taskName: 'Implement user authentication system',
assignee: 'Sarah Chen',
status: 'In Progress',
priority: 4,
createdAt: '2024-01-15T10:00:00Z',
dueDate: '2024-02-15T17:00:00Z'
},
{
taskId: 'TASK-002',
taskName: 'Design new landing page layout',
assignee: 'Marcus Rodriguez',
status: 'To Do',
priority: 3,
createdAt: '2024-01-16T09:30:00Z',
dueDate: '2024-02-01T17:00:00Z'
},
{
taskId: 'TASK-003',
taskName: 'Fix critical payment gateway bug',
assignee: 'David Kim',
status: 'Done',
priority: 5,
createdAt: '2024-01-10T14:15:00Z',
dueDate: '2024-01-20T17:00:00Z'
},
{
taskId: 'TASK-004',
taskName: 'Update API documentation',
assignee: 'Emily Watson',
status: 'To Do',
priority: 2,
createdAt: '2024-01-18T11:00:00Z',
dueDate: '2024-02-10T17:00:00Z'
},
{
taskId: 'TASK-005',
taskName: 'Implement real-time notifications',
assignee: 'Sarah Chen',
status: 'In Progress',
priority: 3,
createdAt: '2024-01-12T16:45:00Z',
dueDate: '2024-02-05T17:00:00Z'
},
{
taskId: 'TASK-006',
taskName: 'Optimize database queries',
assignee: 'James Miller',
status: 'Done',
priority: 4,
createdAt: '2024-01-08T08:30:00Z',
dueDate: '2024-01-25T17:00:00Z'
},
{
taskId: 'TASK-007',
taskName: 'Create mobile app wireframes',
assignee: 'Marcus Rodriguez',
status: 'In Progress',
priority: 3,
createdAt: '2024-01-20T13:20:00Z',
dueDate: '2024-02-20T17:00:00Z'
},
{
taskId: 'TASK-008',
taskName: 'Setup CI/CD pipeline',
assignee: 'David Kim',
status: 'To Do',
priority: 4,
createdAt: '2024-01-22T10:10:00Z',
dueDate: '2024-02-12T17:00:00Z'
},
{
taskId: 'TASK-009',
taskName: 'Write unit tests for auth module',
assignee: 'Jennifer Lopez',
status: 'Done',
priority: 3,
createdAt: '2024-01-05T15:00:00Z',
dueDate: '2024-01-30T17:00:00Z'
},
{
taskId: 'TASK-010',
taskName: 'Conduct security audit',
assignee: 'James Miller',
status: 'To Do',
priority: 5,
createdAt: '2024-01-25T12:00:00Z',
dueDate: '2024-02-25T17:00:00Z'
},
{
taskId: 'TASK-011',
taskName: 'Refactor legacy code modules',
assignee: 'Emily Watson',
status: 'In Progress',
priority: 2,
createdAt: '2024-01-14T09:45:00Z',
dueDate: '2024-03-01T17:00:00Z'
},
{
taskId: 'TASK-012',
taskName: 'Implement dark mode theme',
assignee: 'Marcus Rodriguez',
status: 'Done',
priority: 1,
createdAt: '2024-01-03T14:30:00Z',
dueDate: '2024-01-28T17:00:00Z'
}
];
/**
* Complete tool specification for the Task Management Dashboard.
* This tool demonstrates all advanced features of the editableTable component.
*/
export const taskDashboardTool: ToolSpec = {
id: 'task-dashboard',
title: 'Task Management Dashboard',
// Global filtering inputs
inputs: {
statusFilter: {
label: 'Filter by Status',
type: 'select',
required: false,
options: [
{ value: '', label: 'All Statuses' },
{ value: 'To Do', label: 'To Do' },
{ value: 'In Progress', label: 'In Progress' },
{ value: 'Done', label: 'Done' }
]
},
assigneeFilter: {
label: 'Filter by Assignee',
type: 'text',
required: false,
placeholder: 'Enter assignee name...'
}
},
// Advanced editableTable configuration
content: {
type: 'editableTable',
// Data loading with filtering, sorting, and pagination
dataLoader: {
functionName: 'getTasks'
},
// Comprehensive table configuration
tableConfig: {
// Item updating for inline edits
itemUpdater: {
functionName: 'updateTask'
},
// Unique identifier for each row
rowIdentifier: 'taskId',
// Advanced column definitions with mixed types
columns: [
{
field: 'taskId',
label: 'Task ID',
isSortable: true,
isEditable: false // Read-only identifier
},
{
field: 'taskName',
label: 'Task Name',
isSortable: true,
isEditable: true, // Inline text editing
editorType: 'text'
},
{
field: 'assignee',
label: 'Assignee',
isSortable: true,
isEditable: false // Sortable but not editable
},
{
field: 'status',
label: 'Status',
isSortable: true,
isEditable: true, // Inline dropdown editing
editorType: 'select',
editorOptions: [
{ value: 'To Do', label: 'To Do' },
{ value: 'In Progress', label: 'In Progress' },
{ value: 'Done', label: 'Done' }
]
},
{
field: 'priority',
label: 'Priority',
isSortable: true,
isEditable: true, // Inline number editing
editorType: 'number'
},
{
field: 'dueDate',
label: 'Due Date',
isSortable: true,
isEditable: false,
formatter: 'date'
}
],
// Row-level actions
rowActions: [
{
id: 'delete',
label: 'Delete',
functionName: 'deleteTask',
variant: 'danger',
needsConfirmation: true,
confirmationMessage: 'Are you sure you want to delete this task? This action cannot be undone.'
}
],
// Pagination configuration
pagination: {
defaultPageSize: 5,
showPageSizeSelector: true,
pageSizeOptions: [5, 10, 20, 50]
}
}
},
// Mock function implementations with full feature support
functions: {
/**
* Advanced data loading function that handles filtering, sorting, and pagination.
* This function demonstrates how to process all the parameters that the framework
* passes to implement server-side data operations.
*
* @param params - Complete query parameters including filters, sorting, and pagination
* @returns Promise resolving to paginated and filtered task data
*/
getTasks: async (params: TaskQueryParams): Promise<{ items: Task[]; totalItems: number }> => {
console.log('🔍 Loading tasks with parameters:', params);
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 800));
// Start with full dataset
let filteredTasks = [...MOCK_TASKS];
// Apply status filtering
if (params.statusFilter && params.statusFilter.trim() !== '') {
filteredTasks = filteredTasks.filter(task =>
task.status === params.statusFilter
);
console.log(`📊 Filtered by status "${params.statusFilter}": ${filteredTasks.length} tasks`);
}
// Apply assignee filtering (case-insensitive partial match)
if (params.assigneeFilter && params.assigneeFilter.trim() !== '') {
const assigneeQuery = params.assigneeFilter.toLowerCase().trim();
filteredTasks = filteredTasks.filter(task =>
task.assignee.toLowerCase().includes(assigneeQuery)
);
console.log(`👤 Filtered by assignee "${params.assigneeFilter}": ${filteredTasks.length} tasks`);
}
// Apply sorting
if (params.sortField && params.sortDirection) {
filteredTasks.sort((a, b) => {
const aValue = a[params.sortField as keyof Task];
const bValue = b[params.sortField as keyof Task];
let comparison = 0;
// Handle different data types
if (typeof aValue === 'string' && typeof bValue === 'string') {
comparison = aValue.localeCompare(bValue);
} else if (typeof aValue === 'number' && typeof bValue === 'number') {
comparison = aValue - bValue;
} else {
// Fallback to string comparison
comparison = String(aValue).localeCompare(String(bValue));
}
// Apply sort direction
return params.sortDirection === 'desc' ? -comparison : comparison;
});
console.log(`🔄 Sorted by ${params.sortField} (${params.sortDirection})`);
}
// Calculate total before pagination
const totalItems = filteredTasks.length;
// Apply pagination
const page = params.page || 1;
const pageSize = params.pageSize || 5;
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize;
const paginatedTasks = filteredTasks.slice(startIndex, endIndex);
console.log(`📄 Page ${page} (${pageSize} per page): Showing ${paginatedTasks.length} of ${totalItems} tasks`);
return {
items: paginatedTasks,
totalItems: totalItems
};
},
/**
* Updates an existing task with new values.
* This function handles inline edits from the table interface.
*
* @param params - Parameters containing itemId, changes, and currentItem
* @returns Promise resolving to the updated task
*/
updateTask: async (params: { itemId: any; changes: any; currentItem: any }): Promise<any> => {
console.log(`📝 Updating task ${params.itemId} with:`, params.changes);
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 500));
// Find and update the task in our mock data
const taskIndex = MOCK_TASKS.findIndex(task => task.taskId === params.itemId);
if (taskIndex === -1) {
throw new Error(`Task ${params.itemId} not found`);
}
// Apply updates
const updatedTask = { ...MOCK_TASKS[taskIndex], ...params.changes };
MOCK_TASKS[taskIndex] = updatedTask;
console.log(`✅ Successfully updated task ${params.itemId}`);
return updatedTask;
},
/**
* Deletes a task from the system.
* This function handles the "Delete" row action.
*
* @param params - Parameters containing itemId and item
* @returns Promise resolving when deletion is complete
*/
deleteTask: async (params: { itemId: any; item: any }): Promise<void> => {
console.log(`🗑️ Deleting task ${params.itemId}...`);
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 400));
// Find and remove the task from our mock data
const taskIndex = MOCK_TASKS.findIndex(task => task.taskId === params.itemId);
if (taskIndex === -1) {
throw new Error(`Task ${params.itemId} not found`);
}
const deletedTask = MOCK_TASKS.splice(taskIndex, 1)[0];
if (deletedTask) {
console.log(`✅ Successfully deleted task: ${deletedTask.taskName}`);
}
},
/**
* Placeholder function for creating new tasks.
* This function handles the "Create New Task" table action.
*
* @param taskData - Data for the new task
* @returns Promise resolving to the created task
*/
createNewTask: async (taskData?: Partial<Task>): Promise<void> => {
console.log('➕ Create New Task action triggered');
console.log('📋 This would typically open a modal or navigate to a creation form');
console.log('💡 Task data received:', taskData);
// In a real application, this might:
// - Open a modal with a form
// - Navigate to a dedicated task creation page
// - Add a new row to the table for inline creation
alert('Create New Task functionality would be implemented here!\n\nThis could open a modal form or navigate to a dedicated creation page.');
}
}
};