Timill Platform Documentation

Scripted Charts

Create dynamic data visualizations using JavaScript and Apache ECharts in Timill Platform.

Create dynamic data visualizations using JavaScript with Apache ECharts. Charts run in both the browser and backend (Goja VM) with full RBAC enforcement.

Getting Started#

Creating a Chart#

  1. Navigate to any page or dashboard
  2. Click Add WidgetScripted Chart
  3. Enter a name and description
  4. Write your JavaScript code
  5. Save and preview

Chart Structure#

Every chart script follows this pattern:

function generateChart(api, params, context) {
    // 1. Query data
    const items = api.queryItems("type = 'task'");
    
    // 2. Transform data
    const grouped = api.groupBy(items, 'status');
    
    // 3. Return chart configuration
    return echarts.createPieChart(grouped, {
        title: 'Task Status Distribution',
        colors: ['#4ade80', '#fbbf24', '#f87171']
    });
}

API Reference#

Global Variables#

VariableTypeDescription
userIdstringCurrent user ID
groupIdstringGroup ID (if group-scoped)
instanceIdstringInstance ID
scriptIdstringCurrent script ID
scriptTypestring"chart"
itemIdstringItem ID (if item-scoped)

api Object#

MethodReturnsDescription
api.queryItems(query)Item[]Query items with RBAC
api.groupBy(items, field)objectGroup items by field value
api.aggregate(items, field, fn)numberSum, avg, min, max, count
api.filterItems(items, predicate)Item[]Filter using predicate function

echarts Object#

MethodReturnsDescription
echarts.createPieChart(data, options?)objectCreate pie chart config
echarts.createBarChart(data, options?)objectCreate bar chart config
echarts.createLineChart(data, options?)objectCreate line chart config
echarts.createScatterChart(data, options?)objectCreate scatter chart config

Item Object#

PropertyTypeDescription
item.ID()stringItem identifier
item.Title()stringItem title
item.Status()stringCurrent status ID
item.AssignedTo()stringAssignee user ID
item.DueDate()stringDue date (ISO 8601)
item.IsCompleted()booleanWhether completed

Examples#

Status Pie Chart#

function generateChart(api, params, context) {
    const items = api.queryItems("type = 'task'");
    const byStatus = api.groupBy(items, 'status');
    
    return echarts.createPieChart(byStatus, {
        title: 'Task Status Distribution',
        colors: ['#4ade80', '#fbbf24', '#f87171', '#60a5fa']
    });
}

See full example: examples/scripted-charts/01_status_pie_chart.js

User Workload Bar Chart#

function generateChart(api, params, context) {
    const items = api.queryItems("type = 'task'");
    const byUser = api.groupBy(items, 'assignee');
    
    const labels = Object.keys(byUser);
    const values = labels.map(u => byUser[u].length);
    
    return echarts.createBarChart({
        labels: labels,
        values: values
    }, {
        title: 'User Workload',
        xLabel: 'User',
        yLabel: 'Tasks'
    });
}

See full example: examples/scripted-charts/02_user_workload_bar.js

Priority Scatter Plot#

function generateChart(api, params, context) {
    const items = api.queryItems("type = 'task'");
    
    const data = items
        .filter(i => i.DueDate() !== null)
        .map(i => ({
            value: [
                new Date(i.DueDate()).getTime(),
                getPriorityScore(i.GetField('priority'))
            ],
            item: i
        }));
    
    return echarts.createScatterChart(data, {
        title: 'Priority vs Due Date',
        xLabel: 'Due Date',
        yLabel: 'Priority'
    });
}

function getPriorityScore(priority) {
    switch(priority) {
        case 'critical': return 4;
        case 'high': return 3;
        case 'medium': return 2;
        case 'low': return 1;
        default: return 0;
    }
}

See full example: examples/scripted-charts/03_priority_scatter.js

Query Syntax#

Use the Timill query syntax to filter items:

// Query open tasks
api.queryItems("type = 'task' status = 'open'")

// Query with date range
api.queryItems("type = 'task' dueDate >= 2024-01-01")

// Query by assignee
api.queryItems("type = 'task' assignee = '" + userId + "'")

// Complex query
api.queryItems("type = 'task' (status = 'open' OR status = 'in-progress') priority = 'high'")

See: Query Filter Reference

Best Practices#

Performance#

  • Keep query filters specific to reduce data transfer
  • Use api.groupBy() instead of manual iteration
  • Cache expensive computations

Error Handling#

function generateChart(api, params, context) {
    try {
        const items = api.queryItems("type = 'task'");
        if (!items || items.length === 0) {
            return { error: 'No data available' };
        }
        // ... generate chart
    } catch (err) {
        return { error: err.message };
    }
}

Security#

  • All queries are automatically RBAC-enforced
  • Scripts cannot access the file system
  • External API calls are rate-limited
  • Scripts timeout after 10 seconds

Troubleshooting#

Chart Not Rendering#

  1. Check browser console for JavaScript errors
  2. Verify the query returns data (test in backend scripts first)
  3. Ensure ECharts methods return the correct format

Query Returns No Data#

  1. Verify item type names are correct
  2. Check RBAC permissions for current user
  3. Test with a simpler query

Timeout Errors#

Scripts timeout after 10 seconds. If your chart is timing out:

  • Simplify the query
  • Reduce data transformations
  • Check for infinite loops

Next Steps#