Week 8 • Module 16

Practical Example: Scheduler UI

Interactive Suitelet with real-time task status tracking

🎯 Project Overview

Build an interactive Suitelet that:

📋 Suitelet Form

function onRequest(context) {
    if (context.request.method === 'GET') {
        const form = serverWidget.createForm({ title: 'Run Report' });
        
        // Saved search selector
        form.addField({
            id: 'custpage_search',
            type: serverWidget.FieldType.SELECT,
            label: 'Customer Search',
            source: 'search'
        });
        
        // Status display (INLINEHTML for custom content)
        form.addField({
            id: 'custpage_status',
            type: serverWidget.FieldType.INLINEHTML,
            label: ' '
        }).defaultValue = '<div id="statusArea"></div>';
        
        form.addSubmitButton({ label: 'Run Report' });
        form.clientScriptModulePath = './scheduler_cs.js';
        
        context.response.writePage(form);
    } else {
        // Handle POST - schedule the task
        const searchId = context.request.parameters.custpage_search;
        
        const mrTask = task.create({
            taskType: task.TaskType.MAP_REDUCE,
            scriptId: 'customscript_csv_export_mr',
            params: { custscript_customer_search: searchId }
        });
        
        const taskId = mrTask.submit();
        
        // Return task ID for status polling
        context.response.write(JSON.stringify({ taskId: taskId }));
    }
}

🎨 Client Script with Status Polling

function pageInit(context) {
    // Attach to form submit
}

function saveRecord(context) {
    // Confirm before submitting
    return dialog.confirm({
        title: 'Confirm',
        message: 'Run the export now?'
    }).then(result => {
        if (result) {
            submitAndPoll();
        }
        return false; // Prevent normal form submit
    });
}

function submitAndPoll() {
    const statusDiv = document.getElementById('statusArea');
    
    // Show processing message
    const msg = message.create({
        title: 'Processing',
        message: 'Report generation started...',
        type: message.Type.INFORMATION
    });
    msg.show();
    
    // Poll for status every 3 seconds
    const interval = setInterval(() => {
        fetch(suiteletUrl + '&action=status&taskId=' + taskId)
            .then(response => response.json())
            .then(data => {
                if (data.status === 'COMPLETE') {
                    clearInterval(interval);
                    msg.hide();
                    message.create({
                        title: 'Complete',
                        message: 'Report generated successfully!',
                        type: message.Type.CONFIRMATION
                    }).show();
                } else if (data.status === 'FAILED') {
                    clearInterval(interval);
                    msg.hide();
                    message.create({
                        title: 'Error',
                        message: 'Report generation failed.',
                        type: message.Type.ERROR
                    }).show();
                }
            });
    }, 3000);
}

📊 Status Check Endpoint

// In the Suitelet, handle status check action
if (context.request.parameters.action === 'status') {
    const taskId = context.request.parameters.taskId;
    const status = task.checkStatus({ taskId: taskId });
    
    context.response.write(JSON.stringify({
        status: status.status
    }));
    return;
}

✅ Pattern: Action Routing

Use an "action" parameter to route different requests to the same Suitelet—default for form display, "schedule" for task creation, "status" for polling.

📖 Finding This in the Docs

This capstone combines multiple modules. Key references:

  1. Suitelet Script Type → onRequest entry point, context.request/response
  2. N/ui/serverWidget Module → form building
  3. N/task Module → task.create(), task.checkStatus()
  4. N/ui/message Module → banner notifications

Key pages to bookmark:

🎯 Key Takeaways