// ========================================== // Chat Switchboard — Task Sidebar Section // ========================================== // Sidebar section showing user's scheduled tasks with status indicators. // Click opens the task's output channel. Same pattern as WorkflowQueue. (function() { 'use strict'; const TaskSidebar = { _loaded: false, // ── Init ──────────────────────────── async init() { if (this._loaded) return; this._loaded = true; // Insert after Queue section (or after Channels if no queue) var anchor = document.getElementById('sbSectionQueue') || document.getElementById('sbSectionChannels'); if (!anchor) return; var section = document.createElement('div'); section.className = 'sb-section'; section.id = 'sbSectionTasks'; section.innerHTML = '
' + '\u25be' + 'Tasks' + '' + '
' + '
'; anchor.parentNode.insertBefore(section, anchor.nextSibling); this.refresh(); }, // ── Refresh ───────────────────────── async refresh() { var body = document.getElementById('sbBodyTasks'); var badge = document.getElementById('sbTaskBadge'); if (!body) return; try { // Personal tasks var resp = await API._get('/api/v1/tasks'); var tasks = (resp.data || []).filter(function(t) { return t.is_active; }); // Team tasks (v0.27.5) try { var teamsResp = await API._get('/api/v1/teams/mine'); var teams = teamsResp.data || teamsResp || []; var seen = {}; tasks.forEach(function(t) { seen[t.id] = true; }); for (var i = 0; i < teams.length; i++) { try { var tr = await API._get('/api/v1/teams/' + teams[i].id + '/tasks'); (tr.data || []).forEach(function(t) { if (!seen[t.id] && t.is_active) { t._teamName = teams[i].name; tasks.push(t); seen[t.id] = true; } }); } catch (_) {} } } catch (_) {} if (tasks.length === 0) { body.innerHTML = ''; if (badge) badge.style.display = 'none'; return; } if (badge) { badge.textContent = tasks.length; badge.style.display = ''; } var html = ''; tasks.forEach(function(t) { // Status indicator var icon = '\u25f7'; // default: clock var statusTip = 'Scheduled'; if (t.last_run_at) { // Check if most recent run was recent (within 2x schedule interval) icon = '\u2713'; // checkmark statusTip = 'Last: ' + new Date(t.last_run_at).toLocaleString(); } var nextTip = t.next_run_at ? 'Next: ' + new Date(t.next_run_at).toLocaleString() : ''; var channelId = t.output_channel_id || ''; var onclick = channelId ? 'selectChannel(\'' + channelId + '\')' : 'UI.toast(\'No output channel yet — task has not run\',\'info\')'; var label = _esc(t.name); if (t._teamName) label = '[' + _esc(t._teamName) + '] ' + label; html += '
' + '' + icon + '' + '' + label + '' + '' + '
'; }); body.innerHTML = html; } catch (err) { body.innerHTML = ''; if (badge) badge.style.display = 'none'; } }, // ── Run Now ───────────────────────── async runNow(taskId) { try { await API._post('/api/v1/tasks/' + taskId + '/run', {}); UI.toast('Task triggered', 'success'); } catch (err) { UI.toast(err.message || 'Failed to run task', 'error'); } } }; sb.ns('TaskSidebar', TaskSidebar); function _esc(s) { return s ? s.replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"') : ''; } // Auto-init when chat surface loads document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof API !== 'undefined' && API.accessToken) { TaskSidebar.init(); } }, 2200); // After WorkflowQueue (2000ms) }); })();