// ==========================================
// 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 =
'
' +
'';
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)
});
})();