@agenticmail/enterprise 0.5.211 → 0.5.213
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-heartbeat-ELQ5JLHO.js +510 -0
- package/dist/chunk-B3M7646O.js +430 -0
- package/dist/chunk-C2TOU7TU.js +1224 -0
- package/dist/chunk-DCYML7SN.js +3685 -0
- package/dist/chunk-RCWI4IVQ.js +4457 -0
- package/dist/cli-agent-OK4C2TOY.js +1719 -0
- package/dist/cli-serve-JTPLFI7G.js +114 -0
- package/dist/cli.js +3 -3
- package/dist/dashboard/components/icons.js +1 -1
- package/dist/dashboard/pages/agent-detail/configuration.js +383 -3
- package/dist/dashboard/pages/task-pipeline.js +624 -431
- package/dist/index.js +3 -3
- package/dist/routes-GMWDZHJ7.js +13050 -0
- package/dist/runtime-AJKUBWPF.js +45 -0
- package/dist/server-JLDYZGOH.js +15 -0
- package/dist/setup-XNFHPUH3.js +20 -0
- package/package.json +1 -1
- package/src/dashboard/pages/task-pipeline.js +145 -126
|
@@ -2,129 +2,154 @@ import { h, useState, useEffect, useCallback, useRef, Fragment, useApp, engineCa
|
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
4
|
|
|
5
|
-
// ───
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
5
|
+
// ─── Constants ───────────────────────────────────────────
|
|
6
|
+
var NODE_W = 200;
|
|
7
|
+
var NODE_H = 64;
|
|
8
|
+
var AGENT_W = 160;
|
|
9
|
+
var AGENT_H = 52;
|
|
10
|
+
var H_GAP = 48; // horizontal gap (left→right flow)
|
|
11
|
+
var V_GAP = 24; // vertical gap between lanes
|
|
12
|
+
var PAD = 40;
|
|
13
|
+
|
|
14
|
+
var STATUS_COLORS = { created: '#6366f1', assigned: '#f59e0b', in_progress: '#06b6d4', completed: '#22c55e', failed: '#ef4444', cancelled: '#6b7394' };
|
|
15
|
+
var PRIORITY_COLORS = { urgent: '#ef4444', high: '#f59e0b', normal: '#6366f1', low: '#6b7394' };
|
|
16
|
+
var DELEGATION_COLORS = { delegation: '#6366f1', review: '#f59e0b', revision: '#f97316', escalation: '#ef4444', return: '#22c55e' };
|
|
17
|
+
var BG = '#0a0c14';
|
|
18
|
+
var EDGE_COLOR = 'rgba(255,255,255,0.18)';
|
|
19
|
+
var EDGE_HL = 'rgba(99,102,241,0.7)';
|
|
20
|
+
|
|
21
|
+
// ─── CSS Keyframes (injected once) ──────────────────────
|
|
22
|
+
var _injected = false;
|
|
23
|
+
function injectCSS() {
|
|
24
|
+
if (_injected) return; _injected = true;
|
|
25
|
+
var style = document.createElement('style');
|
|
26
|
+
style.textContent = `
|
|
27
|
+
@keyframes flowDash { to { stroke-dashoffset: -24; } }
|
|
28
|
+
@keyframes flowPulse { 0%,100% { opacity: 0.4; } 50% { opacity: 1; } }
|
|
29
|
+
@keyframes taskPulse { 0%,100% { box-shadow: 0 0 0 0 rgba(6,182,212,0.3); } 50% { box-shadow: 0 0 8px 2px rgba(6,182,212,0.2); } }
|
|
30
|
+
.tp-flow-active { animation: flowDash 1.2s linear infinite; }
|
|
31
|
+
.tp-node-active { animation: taskPulse 2s ease-in-out infinite; }
|
|
32
|
+
.tp-node:hover { transform: scale(1.03); z-index: 10; }
|
|
33
|
+
.tp-customer-badge { background: linear-gradient(135deg, #6366f1, #a855f7); color: #fff; font-size: 9px; padding: 1px 6px; border-radius: 8px; font-weight: 600; }
|
|
34
|
+
.tp-chain-tag { font-size: 9px; padding: 1px 5px; border-radius: 4px; font-weight: 600; letter-spacing: 0.02em; }
|
|
35
|
+
`;
|
|
36
|
+
document.head.appendChild(style);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ─── Layout: Left-to-Right Chain Flow ────────────────────
|
|
40
|
+
// Tasks flow horizontally. Each chain = a horizontal row.
|
|
41
|
+
// Multiple chains stack vertically. Circular flows curve back.
|
|
42
|
+
|
|
43
|
+
function layoutChains(tasks) {
|
|
44
|
+
if (!tasks.length) return { nodes: [], edges: [], width: 0, height: 0, chains: [] };
|
|
45
|
+
|
|
46
|
+
// Group by chainId
|
|
47
|
+
var chainMap = new Map();
|
|
48
|
+
var orphans = [];
|
|
35
49
|
tasks.forEach(function(t) {
|
|
50
|
+
if (t.chainId) {
|
|
51
|
+
if (!chainMap.has(t.chainId)) chainMap.set(t.chainId, []);
|
|
52
|
+
chainMap.get(t.chainId).push(t);
|
|
53
|
+
} else {
|
|
54
|
+
orphans.push(t);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Sort each chain by chainSeq
|
|
59
|
+
chainMap.forEach(function(arr) { arr.sort(function(a, b) { return (a.chainSeq || 0) - (b.chainSeq || 0); }); });
|
|
60
|
+
|
|
61
|
+
// Also group orphans by agent for a simpler layout
|
|
62
|
+
var orphansByAgent = new Map();
|
|
63
|
+
orphans.forEach(function(t) {
|
|
36
64
|
var key = t.assignedTo || 'unassigned';
|
|
37
|
-
if (!
|
|
38
|
-
|
|
65
|
+
if (!orphansByAgent.has(key)) orphansByAgent.set(key, []);
|
|
66
|
+
orphansByAgent.get(key).push(t);
|
|
39
67
|
});
|
|
40
68
|
|
|
41
|
-
// Build task nodes under each agent
|
|
42
69
|
var allNodes = [];
|
|
43
|
-
var
|
|
44
|
-
var
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
var
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
var allEdges = [];
|
|
71
|
+
var chainInfos = [];
|
|
72
|
+
var y = PAD;
|
|
73
|
+
|
|
74
|
+
// Layout each chain as a horizontal row
|
|
75
|
+
chainMap.forEach(function(chainTasks, chainId) {
|
|
76
|
+
var x = PAD;
|
|
77
|
+
var rowNodes = [];
|
|
78
|
+
var maxH = NODE_H;
|
|
79
|
+
|
|
80
|
+
chainTasks.forEach(function(t, i) {
|
|
81
|
+
var node = { id: t.id, task: t, x: x, y: y, w: NODE_W, h: NODE_H, isAgent: false, chainId: chainId };
|
|
82
|
+
rowNodes.push(node);
|
|
83
|
+
allNodes.push(node);
|
|
84
|
+
|
|
85
|
+
if (i > 0) {
|
|
86
|
+
var prev = rowNodes[i - 1];
|
|
87
|
+
var dtype = t.delegationType || 'delegation';
|
|
88
|
+
var isReturn = dtype === 'return' || dtype === 'revision';
|
|
89
|
+
// Check for circular: does this task go back to an agent already seen?
|
|
90
|
+
var seenAgents = chainTasks.slice(0, i).map(function(ct) { return ct.assignedTo; });
|
|
91
|
+
var isCircular = seenAgents.indexOf(t.assignedTo) !== -1 && isReturn;
|
|
92
|
+
|
|
93
|
+
allEdges.push({
|
|
94
|
+
from: prev, to: node,
|
|
95
|
+
delegationType: dtype,
|
|
96
|
+
isCircular: isCircular,
|
|
97
|
+
isActive: prev.task.status === 'in_progress' || t.status === 'in_progress',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
x += NODE_W + H_GAP;
|
|
56
102
|
});
|
|
57
103
|
|
|
58
|
-
//
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
104
|
+
// Customer context badge on first node
|
|
105
|
+
var firstTask = chainTasks[0];
|
|
106
|
+
chainInfos.push({
|
|
107
|
+
chainId: chainId,
|
|
108
|
+
y: y,
|
|
109
|
+
taskCount: chainTasks.length,
|
|
110
|
+
customer: firstTask.customerContext,
|
|
111
|
+
status: chainTasks[chainTasks.length - 1].status,
|
|
112
|
+
title: firstTask.title,
|
|
66
113
|
});
|
|
67
114
|
|
|
68
|
-
|
|
69
|
-
agentRoots.push(agent);
|
|
115
|
+
y += maxH + V_GAP + 16; // extra space between chains
|
|
70
116
|
});
|
|
71
117
|
|
|
72
|
-
//
|
|
73
|
-
function
|
|
74
|
-
var
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
total += (node.children.length - 1) * H_GAP;
|
|
79
|
-
node.subtreeW = Math.max(w, total);
|
|
80
|
-
return node.subtreeW;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Assign positions
|
|
84
|
-
function assignPos(node, x, y) {
|
|
85
|
-
var w = node.isAgent ? NODE_W : NODE_W;
|
|
86
|
-
node.x = x + node.subtreeW / 2 - w / 2;
|
|
87
|
-
node.y = y;
|
|
88
|
-
if (node.children.length === 0) return;
|
|
89
|
-
var childrenW = node.children.reduce(function(s, c) { return s + c.subtreeW; }, 0) + (node.children.length - 1) * H_GAP;
|
|
90
|
-
var cx = node.x + w / 2 - childrenW / 2;
|
|
91
|
-
node.children.forEach(function(c) {
|
|
92
|
-
assignPos(c, cx, y + NODE_H + V_GAP);
|
|
93
|
-
cx += c.subtreeW + H_GAP;
|
|
118
|
+
// Layout orphans as simple rows per agent
|
|
119
|
+
orphansByAgent.forEach(function(agentTasks, agentId) {
|
|
120
|
+
var x = PAD;
|
|
121
|
+
agentTasks.forEach(function(t) {
|
|
122
|
+
allNodes.push({ id: t.id, task: t, x: x, y: y, w: NODE_W, h: NODE_H, isAgent: false, chainId: null });
|
|
123
|
+
x += NODE_W + H_GAP;
|
|
94
124
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// Layout agent roots side by side
|
|
98
|
-
agentRoots.forEach(function(r) { computeW(r); });
|
|
99
|
-
var cx = PAD;
|
|
100
|
-
agentRoots.forEach(function(r) {
|
|
101
|
-
assignPos(r, cx, PAD);
|
|
102
|
-
cx += r.subtreeW + H_GAP * 2;
|
|
125
|
+
y += NODE_H + V_GAP;
|
|
103
126
|
});
|
|
104
127
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
maxX = Math.max(maxX, node.x + w);
|
|
111
|
-
maxY = Math.max(maxY, node.y + NODE_H);
|
|
112
|
-
if (parent) edgeList.push({ parent: parent, child: node });
|
|
113
|
-
node.children.forEach(function(c) { flatten(c, node); });
|
|
114
|
-
}
|
|
115
|
-
agentRoots.forEach(function(r) { flatten(r, null); });
|
|
128
|
+
var maxX = 0;
|
|
129
|
+
allNodes.forEach(function(n) { maxX = Math.max(maxX, n.x + n.w); });
|
|
130
|
+
|
|
131
|
+
return { nodes: allNodes, edges: allEdges, width: maxX + PAD, height: y + PAD, chains: chainInfos };
|
|
132
|
+
}
|
|
116
133
|
|
|
117
|
-
|
|
134
|
+
// ─── SVG Edge Paths ──────────────────────────────────────
|
|
135
|
+
function horizontalPath(from, to) {
|
|
136
|
+
var x1 = from.x + from.w;
|
|
137
|
+
var y1 = from.y + from.h / 2;
|
|
138
|
+
var x2 = to.x;
|
|
139
|
+
var y2 = to.y + to.h / 2;
|
|
140
|
+
var midX = x1 + (x2 - x1) * 0.5;
|
|
141
|
+
return 'M ' + x1 + ' ' + y1 + ' C ' + midX + ' ' + y1 + ', ' + midX + ' ' + y2 + ', ' + x2 + ' ' + y2;
|
|
118
142
|
}
|
|
119
143
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
var x1 =
|
|
123
|
-
var y1 =
|
|
124
|
-
var x2 =
|
|
125
|
-
var y2 =
|
|
126
|
-
var
|
|
127
|
-
|
|
144
|
+
function circularPath(from, to) {
|
|
145
|
+
// Arc back: goes up and curves back left
|
|
146
|
+
var x1 = from.x + from.w;
|
|
147
|
+
var y1 = from.y + from.h / 2;
|
|
148
|
+
var x2 = to.x;
|
|
149
|
+
var y2 = to.y + to.h / 2;
|
|
150
|
+
var lift = 40;
|
|
151
|
+
var topY = Math.min(y1, y2) - lift;
|
|
152
|
+
return 'M ' + x1 + ' ' + y1 + ' C ' + (x1 + 50) + ' ' + topY + ', ' + (x2 - 50) + ' ' + topY + ', ' + x2 + ' ' + y2;
|
|
128
153
|
}
|
|
129
154
|
|
|
130
155
|
// ─── Helpers ─────────────────────────────────────────────
|
|
@@ -137,7 +162,6 @@ function timeAgo(ts) {
|
|
|
137
162
|
if (diff < 86400000) return Math.floor(diff / 3600000) + 'h ago';
|
|
138
163
|
return Math.floor(diff / 86400000) + 'd ago';
|
|
139
164
|
}
|
|
140
|
-
|
|
141
165
|
function formatDuration(ms) {
|
|
142
166
|
if (!ms) return '-';
|
|
143
167
|
var s = Math.floor(ms / 1000);
|
|
@@ -146,86 +170,136 @@ function formatDuration(ms) {
|
|
|
146
170
|
if (m < 60) return m + 'm ' + (s % 60) + 's';
|
|
147
171
|
return Math.floor(m / 60) + 'h ' + (m % 60) + 'm';
|
|
148
172
|
}
|
|
149
|
-
|
|
150
|
-
var CATEGORY_ICONS = { email: '\u2709', research: '\uD83D\uDD0D', meeting: '\uD83D\uDCC5', workflow: '\u2699', writing: '\u270F', deployment: '\uD83D\uDE80', review: '\u2714', monitoring: '\uD83D\uDCE1', custom: '\uD83D\uDCCB' };
|
|
151
|
-
|
|
152
|
-
function tagStyle(color) {
|
|
153
|
-
return { fontSize: 9, fontWeight: 600, padding: '1px 5px', borderRadius: 4, background: color + '22', color: color, letterSpacing: '0.02em' };
|
|
154
|
-
}
|
|
173
|
+
function tag(color, text) { return h('span', { className: 'tp-chain-tag', style: { background: color + '22', color: color } }, text); }
|
|
155
174
|
|
|
156
175
|
var toolbarBtnStyle = {
|
|
157
|
-
background: 'rgba(255,255,255,0.08)',
|
|
158
|
-
|
|
159
|
-
borderRadius: 6,
|
|
160
|
-
color: '#fff',
|
|
161
|
-
fontSize: 14,
|
|
162
|
-
fontWeight: 600,
|
|
163
|
-
padding: '4px 8px',
|
|
164
|
-
cursor: 'pointer',
|
|
165
|
-
lineHeight: '1.2',
|
|
176
|
+
background: 'rgba(255,255,255,0.08)', border: '1px solid rgba(255,255,255,0.12)',
|
|
177
|
+
borderRadius: 6, color: '#fff', fontSize: 12, fontWeight: 600, padding: '4px 10px', cursor: 'pointer',
|
|
166
178
|
};
|
|
167
|
-
|
|
168
179
|
function legendDot(color, label) {
|
|
169
180
|
return h('div', { style: { display: 'flex', alignItems: 'center', gap: 4 } },
|
|
170
181
|
h('div', { style: { width: 8, height: 8, borderRadius: '50%', background: color } }),
|
|
171
|
-
h('span', { style: { color: 'rgba(255,255,255,0.5)', fontSize:
|
|
182
|
+
h('span', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 11 } }, label)
|
|
172
183
|
);
|
|
173
184
|
}
|
|
174
|
-
|
|
175
|
-
function tooltipRow(label, value, color) {
|
|
176
|
-
return h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontSize: 11 } },
|
|
177
|
-
h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, label),
|
|
178
|
-
h('span', { style: { fontWeight: 600, color: color || '#fff' } }, value)
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ─── Help tooltip styles ─────────────────────────────────
|
|
183
185
|
var _h4 = { marginTop: 16, marginBottom: 8, fontSize: 14 };
|
|
184
186
|
var _ul = { paddingLeft: 20, margin: '4px 0 8px' };
|
|
185
187
|
var _tip = { marginTop: 12, padding: 12, background: 'var(--bg-secondary, #1e293b)', borderRadius: 'var(--radius, 8px)', fontSize: 13 };
|
|
186
188
|
|
|
189
|
+
// ─── Customer Profile Mini-Card ──────────────────────────
|
|
190
|
+
function CustomerBadge(props) {
|
|
191
|
+
var c = props.customer;
|
|
192
|
+
if (!c) return null;
|
|
193
|
+
return h('div', { style: { display: 'flex', alignItems: 'center', gap: 6, padding: '4px 8px', background: 'rgba(99,102,241,0.08)', border: '1px solid rgba(99,102,241,0.2)', borderRadius: 8, fontSize: 11, marginBottom: 4 } },
|
|
194
|
+
h('div', { style: { width: 20, height: 20, borderRadius: '50%', background: 'linear-gradient(135deg, #6366f1, #a855f7)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 9, fontWeight: 700, flexShrink: 0 } }, (c.name || '?').charAt(0).toUpperCase()),
|
|
195
|
+
h('div', { style: { overflow: 'hidden' } },
|
|
196
|
+
h('div', { style: { fontWeight: 600, color: '#fff', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, c.name || 'Unknown'),
|
|
197
|
+
h('div', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 9 } },
|
|
198
|
+
c.isNew ? 'New customer' : (c.company || c.email || c.channel || '')
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
187
204
|
// ─── Task Detail Modal ───────────────────────────────────
|
|
188
205
|
function TaskDetail(props) {
|
|
189
206
|
var task = props.task;
|
|
207
|
+
var chain = props.chain;
|
|
190
208
|
var onClose = props.onClose;
|
|
191
209
|
var onCancel = props.onCancel;
|
|
192
210
|
if (!task) return null;
|
|
193
211
|
var statusColor = STATUS_COLORS[task.status] || '#6b7394';
|
|
194
212
|
|
|
195
213
|
return h('div', { className: 'modal-overlay', onClick: onClose },
|
|
196
|
-
h('div', { className: 'modal', onClick: function(e) { e.stopPropagation(); }, style: { width:
|
|
214
|
+
h('div', { className: 'modal', onClick: function(e) { e.stopPropagation(); }, style: { width: 640, maxHeight: '85vh', overflow: 'auto' } },
|
|
197
215
|
h('div', { className: 'modal-header' },
|
|
198
|
-
h('h2',
|
|
216
|
+
h('h2', { style: { fontSize: 16 } }, task.title),
|
|
199
217
|
h('button', { className: 'btn btn-ghost btn-icon', onClick: onClose }, '\u00D7')
|
|
200
218
|
),
|
|
201
219
|
h('div', { className: 'modal-body', style: { padding: 20 } },
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
h('span', { style: { padding: '3px 10px', borderRadius: 12, fontSize:
|
|
205
|
-
h('span', { style: { padding: '3px 10px', borderRadius: 12, fontSize:
|
|
220
|
+
// Status badges
|
|
221
|
+
h('div', { style: { display: 'flex', gap: 6, marginBottom: 16, alignItems: 'center', flexWrap: 'wrap' } },
|
|
222
|
+
h('span', { style: { padding: '3px 10px', borderRadius: 12, fontSize: 11, fontWeight: 600, background: statusColor + '22', color: statusColor, border: '1px solid ' + statusColor + '44' } }, task.status.replace('_', ' ').toUpperCase()),
|
|
223
|
+
h('span', { style: { padding: '3px 10px', borderRadius: 12, fontSize: 11, background: (PRIORITY_COLORS[task.priority] || '#6366f1') + '22', color: PRIORITY_COLORS[task.priority] || '#6366f1' } }, task.priority.toUpperCase()),
|
|
224
|
+
task.chainId && h('span', { style: { padding: '3px 10px', borderRadius: 12, fontSize: 11, background: 'rgba(99,102,241,0.1)', color: '#6366f1', fontFamily: 'var(--font-mono)' } }, 'Chain #' + task.chainId.slice(0, 8)),
|
|
225
|
+
task.delegationType && tag(DELEGATION_COLORS[task.delegationType] || '#6b7394', task.delegationType)
|
|
206
226
|
),
|
|
227
|
+
|
|
228
|
+
// Customer context
|
|
229
|
+
task.customerContext && h('div', { style: { padding: 12, background: 'rgba(99,102,241,0.06)', border: '1px solid rgba(99,102,241,0.15)', borderRadius: 'var(--radius)', marginBottom: 16 } },
|
|
230
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: 'rgba(255,255,255,0.5)', marginBottom: 8 } }, 'CUSTOMER'),
|
|
231
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '6px 16px', fontSize: 13 } },
|
|
232
|
+
task.customerContext.name && h(Fragment, null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11 } }, 'Name'), h('div', null, task.customerContext.name)),
|
|
233
|
+
task.customerContext.email && h(Fragment, null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11 } }, 'Email'), h('div', null, task.customerContext.email)),
|
|
234
|
+
task.customerContext.company && h(Fragment, null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11 } }, 'Company'), h('div', null, task.customerContext.company)),
|
|
235
|
+
task.customerContext.channel && h(Fragment, null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11 } }, 'Channel'), h('div', null, task.customerContext.channel)),
|
|
236
|
+
h(Fragment, null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11 } }, 'Type'), h('div', null, task.customerContext.isNew ? 'New Customer' : 'Returning'))
|
|
237
|
+
)
|
|
238
|
+
),
|
|
239
|
+
|
|
207
240
|
task.description && h('div', { style: { marginBottom: 16, fontSize: 13, lineHeight: 1.6, color: 'var(--text-secondary)' } }, task.description),
|
|
241
|
+
|
|
242
|
+
// Progress
|
|
208
243
|
task.status === 'in_progress' && h('div', { style: { marginBottom: 16 } },
|
|
209
244
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 4 } }, h('span', null, 'Progress'), h('span', null, task.progress + '%')),
|
|
210
245
|
h('div', { style: { height: 6, background: 'var(--border)', borderRadius: 3, overflow: 'hidden' } },
|
|
211
|
-
h('div', { style: { height: '100%', width: task.progress + '%', background: STATUS_COLORS.in_progress, borderRadius: 3 } })
|
|
246
|
+
h('div', { style: { height: '100%', width: task.progress + '%', background: STATUS_COLORS.in_progress, borderRadius: 3, transition: 'width 0.3s' } })
|
|
212
247
|
)
|
|
213
248
|
),
|
|
214
|
-
|
|
249
|
+
|
|
250
|
+
// Grid details
|
|
251
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px 24px', fontSize: 13, marginBottom: 16 } },
|
|
215
252
|
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Assigned To'), h('div', null, task.assignedToName || task.assignedTo || '-')),
|
|
216
253
|
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Created By'), h('div', null, task.createdByName || task.createdBy || '-')),
|
|
217
254
|
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Created'), h('div', null, task.createdAt ? new Date(task.createdAt).toLocaleString() : '-')),
|
|
218
|
-
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Started'), h('div', null, task.startedAt ? new Date(task.startedAt).toLocaleString() : '-')),
|
|
219
|
-
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Completed'), h('div', null, task.completedAt ? new Date(task.completedAt).toLocaleString() : '-')),
|
|
220
255
|
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Duration'), h('div', null, formatDuration(task.actualDurationMs))),
|
|
221
256
|
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Model'), h('div', null, task.modelUsed || task.model || '-')),
|
|
222
257
|
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Tokens / Cost'), h('div', null, (task.tokensUsed || 0).toLocaleString() + ' / $' + (task.costUsd || 0).toFixed(4)))
|
|
223
258
|
),
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
h('
|
|
259
|
+
|
|
260
|
+
// Task chain timeline (if part of a chain)
|
|
261
|
+
chain && chain.length > 1 && h('div', { style: { marginBottom: 16 } },
|
|
262
|
+
h('div', { style: { fontSize: 12, fontWeight: 600, color: 'var(--text-muted)', marginBottom: 8 } }, 'DELEGATION CHAIN'),
|
|
263
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 0, overflow: 'auto', padding: '8px 0' } },
|
|
264
|
+
chain.map(function(ct, i) {
|
|
265
|
+
var isMe = ct.id === task.id;
|
|
266
|
+
var sc = STATUS_COLORS[ct.status] || '#6b7394';
|
|
267
|
+
return h(Fragment, { key: ct.id },
|
|
268
|
+
i > 0 && h('div', { style: { display: 'flex', alignItems: 'center', flexShrink: 0 } },
|
|
269
|
+
h('div', { style: { width: 32, height: 2, background: (DELEGATION_COLORS[ct.delegationType] || '#6366f1') + '66' } }),
|
|
270
|
+
h('div', { style: { fontSize: 8, color: 'rgba(255,255,255,0.4)', position: 'relative', top: -8 } }, ct.delegationType || '')
|
|
271
|
+
),
|
|
272
|
+
h('div', { style: {
|
|
273
|
+
padding: '6px 10px', borderRadius: 8, fontSize: 11, flexShrink: 0,
|
|
274
|
+
background: isMe ? sc + '22' : 'rgba(255,255,255,0.03)',
|
|
275
|
+
border: '1px solid ' + (isMe ? sc : 'rgba(255,255,255,0.1)'),
|
|
276
|
+
fontWeight: isMe ? 700 : 400, color: isMe ? sc : 'rgba(255,255,255,0.6)',
|
|
277
|
+
} },
|
|
278
|
+
h('div', { style: { fontWeight: 600 } }, ct.assignedToName || ct.assignedTo),
|
|
279
|
+
h('div', { style: { fontSize: 9, marginTop: 2, opacity: 0.6 } }, ct.status.replace('_', ' '))
|
|
280
|
+
)
|
|
281
|
+
);
|
|
282
|
+
})
|
|
283
|
+
)
|
|
284
|
+
),
|
|
285
|
+
|
|
286
|
+
// Activity log
|
|
287
|
+
task.activityLog && task.activityLog.length > 0 && h('div', { style: { marginBottom: 16 } },
|
|
288
|
+
h('div', { style: { fontSize: 12, fontWeight: 600, color: 'var(--text-muted)', marginBottom: 8 } }, 'ACTIVITY LOG'),
|
|
289
|
+
h('div', { style: { maxHeight: 180, overflow: 'auto', border: '1px solid var(--border)', borderRadius: 'var(--radius)' } },
|
|
290
|
+
task.activityLog.map(function(entry, i) {
|
|
291
|
+
return h('div', { key: i, style: { display: 'flex', gap: 8, padding: '6px 10px', borderBottom: '1px solid var(--border)', fontSize: 11 } },
|
|
292
|
+
h('span', { style: { color: 'var(--text-muted)', flexShrink: 0, fontFamily: 'var(--font-mono)', fontSize: 10 } }, entry.ts ? new Date(entry.ts).toLocaleTimeString() : ''),
|
|
293
|
+
h('span', { style: { fontWeight: 600, flexShrink: 0, minWidth: 60 } }, entry.type),
|
|
294
|
+
h('span', { style: { color: 'var(--text-secondary)' } }, entry.detail)
|
|
295
|
+
);
|
|
296
|
+
})
|
|
297
|
+
)
|
|
228
298
|
),
|
|
299
|
+
|
|
300
|
+
task.error && h('div', { style: { padding: 12, background: 'rgba(239,68,68,0.1)', border: '1px solid rgba(239,68,68,0.3)', borderRadius: 'var(--radius)', marginBottom: 16, fontSize: 13, color: '#ef4444' } }, h('strong', null, 'Error: '), task.error),
|
|
301
|
+
|
|
302
|
+
// Actions
|
|
229
303
|
(task.status === 'created' || task.status === 'assigned' || task.status === 'in_progress') && h('div', { style: { display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 16, borderTop: '1px solid var(--border)', paddingTop: 16 } },
|
|
230
304
|
h('button', { className: 'btn btn-danger btn-sm', onClick: function() { onCancel(task.id); } }, 'Cancel Task')
|
|
231
305
|
)
|
|
@@ -234,22 +308,63 @@ function TaskDetail(props) {
|
|
|
234
308
|
);
|
|
235
309
|
}
|
|
236
310
|
|
|
311
|
+
// ─── Metrics Bar ─────────────────────────────────────────
|
|
312
|
+
function MetricsBar(props) {
|
|
313
|
+
var s = props.stats;
|
|
314
|
+
function chip(label, value, color) {
|
|
315
|
+
return h('div', { style: { display: 'flex', alignItems: 'center', gap: 4, padding: '3px 8px', background: 'rgba(255,255,255,0.04)', borderRadius: 6 } },
|
|
316
|
+
h('span', { style: { fontSize: 10, color: 'rgba(255,255,255,0.35)' } }, label),
|
|
317
|
+
h('span', { style: { fontSize: 11, fontWeight: 700, color: color } }, value)
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
var hasActivity = s.total > 0;
|
|
321
|
+
|
|
322
|
+
return h('div', { style: { display: 'flex', alignItems: 'center', gap: 6, padding: '6px 16px', borderBottom: '1px solid rgba(255,255,255,0.06)', background: 'rgba(0,0,0,0.12)', flexShrink: 0, overflowX: 'auto', fontSize: 11 } },
|
|
323
|
+
h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.25)', fontWeight: 600, letterSpacing: '0.06em', marginRight: 2 } }, 'TODAY'),
|
|
324
|
+
chip('Done', s.todayCompleted || 0, '#22c55e'),
|
|
325
|
+
chip('Active', s.inProgress || 0, '#06b6d4'),
|
|
326
|
+
chip('New', s.todayCreated || 0, '#f59e0b'),
|
|
327
|
+
s.todayFailed > 0 && chip('Failed', s.todayFailed, '#ef4444'),
|
|
328
|
+
hasActivity && h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.08)' } }),
|
|
329
|
+
hasActivity && h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.25)', fontWeight: 600, letterSpacing: '0.06em' } }, 'ALL'),
|
|
330
|
+
hasActivity && chip('Total', s.total, 'rgba(255,255,255,0.6)'),
|
|
331
|
+
s.avgDurationMs > 0 && chip('Avg', formatDuration(s.avgDurationMs), '#fff'),
|
|
332
|
+
s.totalTokens > 0 && chip('Tokens', s.totalTokens > 999999 ? (s.totalTokens / 1000000).toFixed(1) + 'M' : s.totalTokens > 999 ? (s.totalTokens / 1000).toFixed(1) + 'K' : s.totalTokens, '#a855f7'),
|
|
333
|
+
s.totalCost > 0 && chip('Cost', '$' + s.totalCost.toFixed(2), '#22c55e'),
|
|
334
|
+
s.topAgents && s.topAgents.length > 0 && h(Fragment, null,
|
|
335
|
+
h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.08)' } }),
|
|
336
|
+
s.topAgents.slice(0, 3).map(function(a) {
|
|
337
|
+
return h('div', { key: a.agent, style: { display: 'flex', alignItems: 'center', gap: 3, padding: '2px 6px', background: 'rgba(255,255,255,0.04)', borderRadius: 6 } },
|
|
338
|
+
h('div', { style: { width: 12, height: 12, borderRadius: '50%', background: '#6366f133', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 7, fontWeight: 700, color: '#6366f1' } }, (a.name || '?').charAt(0).toUpperCase()),
|
|
339
|
+
h('span', { style: { fontSize: 10, color: '#fff', fontWeight: 600 } }, a.name),
|
|
340
|
+
h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.3)' } }, a.completed + '/' + a.active)
|
|
341
|
+
);
|
|
342
|
+
})
|
|
343
|
+
)
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// (ChainFlowInline removed — chain flow now renders inline on canvas)
|
|
348
|
+
|
|
237
349
|
// ─── Main Page ───────────────────────────────────────────
|
|
238
350
|
export function TaskPipelinePage() {
|
|
351
|
+
injectCSS();
|
|
239
352
|
var app = useApp();
|
|
240
353
|
var toast = app.toast;
|
|
241
354
|
var _tasks = useState([]);
|
|
242
355
|
var tasks = _tasks[0]; var setTasks = _tasks[1];
|
|
243
|
-
var _stats = useState({ created: 0, assigned: 0, inProgress: 0, completed: 0, failed: 0, cancelled: 0, total: 0 });
|
|
356
|
+
var _stats = useState({ created: 0, assigned: 0, inProgress: 0, completed: 0, failed: 0, cancelled: 0, total: 0, todayCompleted: 0, todayFailed: 0, todayCreated: 0, avgDurationMs: 0, totalCost: 0, totalTokens: 0, topAgents: [] });
|
|
244
357
|
var stats = _stats[0]; var setStats = _stats[1];
|
|
358
|
+
var _expandedTaskId = useState(null);
|
|
359
|
+
var expandedTaskId = _expandedTaskId[0]; var setExpandedTaskId = _expandedTaskId[1];
|
|
245
360
|
var _loading = useState(true);
|
|
246
361
|
var loading = _loading[0]; var setLoading = _loading[1];
|
|
247
362
|
var _selectedTask = useState(null);
|
|
248
363
|
var selectedTask = _selectedTask[0]; var setSelectedTask = _selectedTask[1];
|
|
364
|
+
var _selectedChain = useState(null);
|
|
365
|
+
var selectedChain = _selectedChain[0]; var setSelectedChain = _selectedChain[1];
|
|
249
366
|
var _hoveredId = useState(null);
|
|
250
367
|
var hoveredId = _hoveredId[0]; var setHoveredId = _hoveredId[1];
|
|
251
|
-
var _mousePos = useState({ x: 0, y: 0 });
|
|
252
|
-
var mousePos = _mousePos[0]; var setMousePos = _mousePos[1];
|
|
253
368
|
var _zoom = useState(1);
|
|
254
369
|
var zoom = _zoom[0]; var setZoom = _zoom[1];
|
|
255
370
|
var _pan = useState({ x: 0, y: 0 });
|
|
@@ -260,6 +375,8 @@ export function TaskPipelinePage() {
|
|
|
260
375
|
var dragStart = _dragStart[0]; var setDragStart = _dragStart[1];
|
|
261
376
|
var _filter = useState('active');
|
|
262
377
|
var filter = _filter[0]; var setFilter = _filter[1];
|
|
378
|
+
var _mousePos = useState({ x: 0, y: 0 });
|
|
379
|
+
var mousePos = _mousePos[0]; var setMousePos = _mousePos[1];
|
|
263
380
|
var containerRef = useRef(null);
|
|
264
381
|
|
|
265
382
|
var loadData = useCallback(function() {
|
|
@@ -270,9 +387,8 @@ export function TaskPipelinePage() {
|
|
|
270
387
|
]).then(function(res) {
|
|
271
388
|
setTasks(res[0]?.tasks || []);
|
|
272
389
|
setStats(res[1] || stats);
|
|
273
|
-
}).catch(function(err) {
|
|
274
|
-
|
|
275
|
-
}).finally(function() { setLoading(false); });
|
|
390
|
+
}).catch(function(err) { console.error('[TaskPipeline]', err); })
|
|
391
|
+
.finally(function() { setLoading(false); });
|
|
276
392
|
}, []);
|
|
277
393
|
|
|
278
394
|
// SSE
|
|
@@ -299,10 +415,9 @@ export function TaskPipelinePage() {
|
|
|
299
415
|
if (idx >= 0) { var next = prev.slice(); next[idx] = event.task; return next; }
|
|
300
416
|
return [event.task].concat(prev);
|
|
301
417
|
});
|
|
302
|
-
setSelectedTask(function(prev) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
});
|
|
418
|
+
setSelectedTask(function(prev) { return prev && prev.id === event.task.id ? event.task : prev; });
|
|
419
|
+
// Refresh stats on every task event for real-time metrics
|
|
420
|
+
engineCall('/task-pipeline/stats').then(function(s) { if (s) setStats(s); }).catch(function() {});
|
|
306
421
|
}
|
|
307
422
|
} catch (err) {}
|
|
308
423
|
};
|
|
@@ -310,11 +425,10 @@ export function TaskPipelinePage() {
|
|
|
310
425
|
return function() { if (es) es.close(); };
|
|
311
426
|
}, []);
|
|
312
427
|
|
|
313
|
-
// Periodic stats
|
|
314
428
|
useEffect(function() {
|
|
315
429
|
var iv = setInterval(function() {
|
|
316
430
|
engineCall('/task-pipeline/stats').then(function(s) { if (s) setStats(s); }).catch(function() {});
|
|
317
|
-
},
|
|
431
|
+
}, 15000);
|
|
318
432
|
return function() { clearInterval(iv); };
|
|
319
433
|
}, []);
|
|
320
434
|
|
|
@@ -326,6 +440,34 @@ export function TaskPipelinePage() {
|
|
|
326
440
|
}).catch(function(err) { toast(err.message || 'Failed', 'error'); });
|
|
327
441
|
}, []);
|
|
328
442
|
|
|
443
|
+
function openTaskDetail(t) {
|
|
444
|
+
setSelectedTask(t);
|
|
445
|
+
if (t.chainId) {
|
|
446
|
+
var chainTasks = tasks.filter(function(ct) { return ct.chainId === t.chainId; });
|
|
447
|
+
chainTasks.sort(function(a, b) { return (a.chainSeq || 0) - (b.chainSeq || 0); });
|
|
448
|
+
setSelectedChain(chainTasks.length > 1 ? chainTasks : null);
|
|
449
|
+
} else {
|
|
450
|
+
setSelectedChain(null);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Toggle inline chain flowchart (single click on node)
|
|
455
|
+
function toggleExpand(t) {
|
|
456
|
+
if (expandedTaskId === t.id) {
|
|
457
|
+
setExpandedTaskId(null);
|
|
458
|
+
setSelectedChain(null);
|
|
459
|
+
} else {
|
|
460
|
+
setExpandedTaskId(t.id);
|
|
461
|
+
if (t.chainId) {
|
|
462
|
+
var chainTasks = tasks.filter(function(ct) { return ct.chainId === t.chainId; });
|
|
463
|
+
chainTasks.sort(function(a, b) { return (a.chainSeq || 0) - (b.chainSeq || 0); });
|
|
464
|
+
setSelectedChain(chainTasks.length > 0 ? chainTasks : [t]);
|
|
465
|
+
} else {
|
|
466
|
+
setSelectedChain([t]);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
329
471
|
// Filter
|
|
330
472
|
var filtered = tasks.filter(function(t) {
|
|
331
473
|
if (filter === 'active') return t.status === 'created' || t.status === 'assigned' || t.status === 'in_progress';
|
|
@@ -335,34 +477,28 @@ export function TaskPipelinePage() {
|
|
|
335
477
|
});
|
|
336
478
|
|
|
337
479
|
// Layout
|
|
338
|
-
var layout =
|
|
339
|
-
var
|
|
480
|
+
var layout = layoutChains(filtered);
|
|
481
|
+
var nodes = layout.nodes;
|
|
340
482
|
var edges = layout.edges;
|
|
341
483
|
var treeW = layout.width;
|
|
342
484
|
var treeH = layout.height;
|
|
485
|
+
var chainInfos = layout.chains;
|
|
343
486
|
|
|
344
|
-
// Zoom
|
|
487
|
+
// Zoom/Pan handlers
|
|
345
488
|
var handleWheel = useCallback(function(e) {
|
|
346
489
|
e.preventDefault();
|
|
347
|
-
|
|
348
|
-
setZoom(function(z) { return Math.min(3, Math.max(0.15, z + delta)); });
|
|
490
|
+
setZoom(function(z) { return Math.min(3, Math.max(0.15, z + (e.deltaY > 0 ? -0.08 : 0.08))); });
|
|
349
491
|
}, []);
|
|
350
|
-
|
|
351
|
-
// Pan
|
|
352
492
|
var handleMouseDown = useCallback(function(e) {
|
|
353
|
-
if (e.button !== 0) return;
|
|
354
|
-
if (e.target.closest('.task-node')) return;
|
|
493
|
+
if (e.button !== 0 || e.target.closest('.tp-node')) return;
|
|
355
494
|
setDragging(true);
|
|
356
495
|
setDragStart({ x: e.clientX - pan.x, y: e.clientY - pan.y });
|
|
357
496
|
}, [pan]);
|
|
358
|
-
|
|
359
497
|
var handleMouseMove = useCallback(function(e) {
|
|
360
498
|
if (!dragging) return;
|
|
361
499
|
setPan({ x: e.clientX - dragStart.x, y: e.clientY - dragStart.y });
|
|
362
500
|
}, [dragging, dragStart]);
|
|
363
|
-
|
|
364
501
|
var handleMouseUp = useCallback(function() { setDragging(false); }, []);
|
|
365
|
-
|
|
366
502
|
useEffect(function() {
|
|
367
503
|
if (dragging) {
|
|
368
504
|
window.addEventListener('mousemove', handleMouseMove);
|
|
@@ -371,7 +507,6 @@ export function TaskPipelinePage() {
|
|
|
371
507
|
}
|
|
372
508
|
}, [dragging, handleMouseMove, handleMouseUp]);
|
|
373
509
|
|
|
374
|
-
// Fit
|
|
375
510
|
var fitToView = useCallback(function() {
|
|
376
511
|
if (!containerRef.current || !treeW || !treeH) return;
|
|
377
512
|
var rect = containerRef.current.getBoundingClientRect();
|
|
@@ -379,128 +514,96 @@ export function TaskPipelinePage() {
|
|
|
379
514
|
var scaleY = (rect.height - 40) / treeH;
|
|
380
515
|
var scale = Math.min(scaleX, scaleY, 1.5);
|
|
381
516
|
setZoom(scale);
|
|
382
|
-
setPan({ x: (rect.width - treeW * scale) / 2, y:
|
|
517
|
+
setPan({ x: (rect.width - treeW * scale) / 2, y: 20 });
|
|
383
518
|
}, [treeW, treeH]);
|
|
519
|
+
useEffect(function() { if (nodes.length > 0) fitToView(); }, [nodes.length]);
|
|
384
520
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
positioned.forEach(function(n) { byId.set(n.id || n.agentId, n); });
|
|
392
|
-
// Children
|
|
393
|
-
function addDesc(node) {
|
|
394
|
-
(node.children || []).forEach(function(c) { connected.add(c.id || c.agentId); addDesc(c); });
|
|
395
|
-
}
|
|
396
|
-
var node = byId.get(id);
|
|
397
|
-
if (node) addDesc(node);
|
|
398
|
-
// Parent edges
|
|
399
|
-
edges.forEach(function(e) {
|
|
400
|
-
var cId = e.child.id || e.child.agentId;
|
|
401
|
-
var pId = e.parent.id || e.parent.agentId;
|
|
402
|
-
if (cId === id) connected.add(pId);
|
|
403
|
-
});
|
|
404
|
-
return connected;
|
|
405
|
-
}, [positioned, edges]);
|
|
406
|
-
|
|
407
|
-
var connected = hoveredId ? getConnected(hoveredId) : null;
|
|
521
|
+
// Highlight connected chain on hover
|
|
522
|
+
var hoveredChainId = null;
|
|
523
|
+
if (hoveredId) {
|
|
524
|
+
var hn = nodes.find(function(n) { return n.id === hoveredId; });
|
|
525
|
+
if (hn) hoveredChainId = hn.chainId;
|
|
526
|
+
}
|
|
408
527
|
|
|
409
|
-
|
|
410
|
-
|
|
528
|
+
var hoveredNode = hoveredId ? nodes.find(function(n) { return n.id === hoveredId; }) : null;
|
|
529
|
+
|
|
530
|
+
// ─── Toolbar ─────────────────────────────────────────
|
|
531
|
+
var toolbar = h('div', { style: { display: 'flex', alignItems: 'center', gap: 10, padding: '10px 16px', borderBottom: '1px solid rgba(255,255,255,0.08)', background: 'rgba(0,0,0,0.3)', flexShrink: 0, flexWrap: 'wrap' } },
|
|
532
|
+
h('div', { style: { fontWeight: 700, fontSize: 14, color: '#fff', display: 'flex', alignItems: 'center', gap: 6 } },
|
|
533
|
+
I.workflow(), 'Task Pipeline',
|
|
534
|
+
h(HelpButton, { label: 'Task Pipeline' },
|
|
535
|
+
h('p', null, 'Visual flow of all agent tasks. Tasks flow left-to-right showing delegation chains, multi-agent handoffs, and circular review loops.'),
|
|
536
|
+
h('h4', { style: _h4 }, 'Features'),
|
|
537
|
+
h('ul', { style: _ul },
|
|
538
|
+
h('li', null, h('strong', null, 'Horizontal flow'), ' \u2014 Tasks move left to right through agents'),
|
|
539
|
+
h('li', null, h('strong', null, 'Chain tracking'), ' \u2014 When a manager delegates to a junior, the chain shows the full flow'),
|
|
540
|
+
h('li', null, h('strong', null, 'Circular flows'), ' \u2014 If a task returns (revision/review), the arc curves back'),
|
|
541
|
+
h('li', null, h('strong', null, 'Animated lines'), ' \u2014 Active tasks show flowing dashes on their connections'),
|
|
542
|
+
h('li', null, h('strong', null, 'Customer profiles'), ' \u2014 Support tasks show the customer\'s info'),
|
|
543
|
+
h('li', null, h('strong', null, 'Real-time SSE'), ' \u2014 Updates stream live, no refresh needed')
|
|
544
|
+
),
|
|
545
|
+
h('h4', { style: _h4 }, 'Interactions'),
|
|
546
|
+
h('ul', { style: _ul },
|
|
547
|
+
h('li', null, h('strong', null, 'Hover'), ' \u2014 Highlights the entire chain'),
|
|
548
|
+
h('li', null, h('strong', null, 'Click'), ' \u2014 Opens detail modal with chain timeline, activity log, customer context'),
|
|
549
|
+
h('li', null, h('strong', null, 'Scroll'), ' \u2014 Zoom'),
|
|
550
|
+
h('li', null, h('strong', null, 'Drag'), ' \u2014 Pan')
|
|
551
|
+
),
|
|
552
|
+
h('div', { style: _tip }, h('strong', null, 'Tip: '), 'Each task has a unique chain ID linking all delegation steps. Even circular review loops (agent A \u2192 B \u2192 A) are tracked.')
|
|
553
|
+
)
|
|
554
|
+
),
|
|
555
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 4 } },
|
|
556
|
+
h('div', { style: { width: 8, height: 8, borderRadius: '50%', background: '#22c55e', animation: 'flowPulse 2s infinite' } }),
|
|
557
|
+
h('span', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 11 } }, 'Live')
|
|
558
|
+
),
|
|
559
|
+
h('div', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 11 } },
|
|
560
|
+
(stats.inProgress || 0) + ' active \u00B7 ' + (stats.completed || 0) + ' done \u00B7 ' + (stats.total || 0) + ' total'
|
|
561
|
+
),
|
|
562
|
+
h('div', { style: { flex: 1 } }),
|
|
563
|
+
// Filters
|
|
564
|
+
['active', 'all', 'completed', 'failed'].map(function(f) {
|
|
565
|
+
return h('button', { key: f, onClick: function() { setFilter(f); }, style: Object.assign({}, toolbarBtnStyle, { fontSize: 11, background: filter === f ? 'rgba(99,102,241,0.3)' : toolbarBtnStyle.background }) }, f.charAt(0).toUpperCase() + f.slice(1));
|
|
566
|
+
}),
|
|
567
|
+
h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.12)' } }),
|
|
568
|
+
legendDot(STATUS_COLORS.in_progress, 'Active'),
|
|
569
|
+
legendDot(STATUS_COLORS.completed, 'Done'),
|
|
570
|
+
legendDot(STATUS_COLORS.failed, 'Failed'),
|
|
571
|
+
h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.12)' } }),
|
|
572
|
+
h('button', { onClick: function() { setZoom(function(z) { return Math.min(3, z + 0.2); }); }, style: toolbarBtnStyle }, '+'),
|
|
573
|
+
h('div', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 11, minWidth: 36, textAlign: 'center' } }, Math.round(zoom * 100) + '%'),
|
|
574
|
+
h('button', { onClick: function() { setZoom(function(z) { return Math.max(0.15, z - 0.2); }); }, style: toolbarBtnStyle }, '\u2212'),
|
|
575
|
+
h('button', { onClick: fitToView, style: toolbarBtnStyle }, 'Fit'),
|
|
576
|
+
h('button', { onClick: loadData, style: toolbarBtnStyle }, 'Refresh'),
|
|
577
|
+
);
|
|
411
578
|
|
|
412
579
|
if (loading) return h('div', { style: { padding: 40, textAlign: 'center', color: 'var(--text-muted)' } }, 'Loading task pipeline...');
|
|
413
580
|
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
h(
|
|
417
|
-
h('div', { style: { fontWeight: 700, fontSize: 16, color: '#fff', display: 'flex', alignItems: 'center', gap: 8 } }, I.workflow(), ' Task Pipeline',
|
|
418
|
-
h(HelpButton, { label: 'Task Pipeline' },
|
|
419
|
-
h('p', null, 'Visual hierarchy of all agent tasks in your organization. Tasks flow from agents (top) to individual tasks and sub-tasks (below), connected by arrows.'),
|
|
420
|
-
h('h4', { style: _h4 }, 'How It Works'),
|
|
421
|
-
h('ul', { style: _ul },
|
|
422
|
-
h('li', null, 'Tasks are automatically recorded when agents are spawned for work'),
|
|
423
|
-
h('li', null, 'The pipeline tracks task status from creation through completion'),
|
|
424
|
-
h('li', null, 'Real-time updates via SSE — no need to refresh'),
|
|
425
|
-
h('li', null, 'Smart metadata extraction categorizes tasks automatically')
|
|
426
|
-
),
|
|
427
|
-
h('h4', { style: _h4 }, 'Task Lifecycle'),
|
|
428
|
-
h('ul', { style: _ul },
|
|
429
|
-
h('li', null, h('strong', null, 'Queued'), ' — Waiting to be picked up'),
|
|
430
|
-
h('li', null, h('strong', null, 'Assigned'), ' — Agent selected, about to start'),
|
|
431
|
-
h('li', null, h('strong', null, 'In Progress'), ' — Actively executing'),
|
|
432
|
-
h('li', null, h('strong', null, 'Completed'), ' — Finished successfully'),
|
|
433
|
-
h('li', null, h('strong', null, 'Failed'), ' — Encountered an error')
|
|
434
|
-
),
|
|
435
|
-
h('div', { style: _tip }, 'Tip: Tasks will appear here as agents are assigned work. Sub-tasks show as children connected by arrows.')
|
|
436
|
-
)
|
|
437
|
-
),
|
|
438
|
-
),
|
|
581
|
+
if (nodes.length === 0) return h('div', { style: { height: '100%', display: 'flex', flexDirection: 'column', background: BG, borderRadius: 'var(--radius-lg)', overflow: 'hidden' } },
|
|
582
|
+
toolbar,
|
|
583
|
+
h(MetricsBar, { stats: stats }),
|
|
439
584
|
h('div', { style: { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column' } },
|
|
440
|
-
h('div', { style: {
|
|
441
|
-
h('div', { style: { fontSize:
|
|
442
|
-
h('div', { style: { color: 'rgba(255,255,255,0.5)' } }, 'Tasks will appear here as agents are assigned work.')
|
|
585
|
+
h('div', { style: { width: 48, height: 48, borderRadius: 12, background: 'rgba(99,102,241,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 16, color: '#6366f1' } }, I.workflow()),
|
|
586
|
+
h('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 6, color: '#fff' } }, 'No Tasks in Pipeline'),
|
|
587
|
+
h('div', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 13 } }, 'Tasks will appear here as agents are assigned work.')
|
|
443
588
|
)
|
|
444
589
|
);
|
|
445
590
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
h('li', null, h('strong', null, 'Scroll'), ' — Zoom in/out'),
|
|
458
|
-
h('li', null, h('strong', null, 'Drag'), ' — Pan the canvas')
|
|
459
|
-
),
|
|
460
|
-
h('h4', { style: _h4 }, 'Task Lifecycle'),
|
|
461
|
-
h('ul', { style: _ul },
|
|
462
|
-
h('li', null, h('strong', null, 'Queued'), ' — Waiting to be picked up'),
|
|
463
|
-
h('li', null, h('strong', null, 'Assigned'), ' — Agent selected, about to start'),
|
|
464
|
-
h('li', null, h('strong', null, 'In Progress'), ' — Actively executing'),
|
|
465
|
-
h('li', null, h('strong', null, 'Completed'), ' — Finished successfully'),
|
|
466
|
-
h('li', null, h('strong', null, 'Failed'), ' — Encountered an error')
|
|
467
|
-
),
|
|
468
|
-
h('div', { style: _tip }, 'Tip: Sub-tasks appear as children of their parent task. The arrow system shows task delegation flow.')
|
|
469
|
-
)
|
|
470
|
-
),
|
|
471
|
-
// Live dot
|
|
472
|
-
h('div', { style: { display: 'flex', alignItems: 'center', gap: 4 } },
|
|
473
|
-
h('div', { style: { width: 8, height: 8, borderRadius: '50%', background: '#22c55e', animation: 'pulse 2s infinite' } }),
|
|
474
|
-
h('span', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 12 } }, 'Live')
|
|
475
|
-
),
|
|
476
|
-
// Stats
|
|
477
|
-
h('div', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 12 } },
|
|
478
|
-
(stats.inProgress || 0) + ' active \u00B7 ' + (stats.completed || 0) + ' done \u00B7 ' + (stats.total || 0) + ' total'
|
|
479
|
-
),
|
|
480
|
-
h('div', { style: { flex: 1 } }),
|
|
481
|
-
// Filter
|
|
482
|
-
['active', 'all', 'completed', 'failed'].map(function(f) {
|
|
483
|
-
return h('button', {
|
|
484
|
-
key: f,
|
|
485
|
-
onClick: function() { setFilter(f); },
|
|
486
|
-
style: Object.assign({}, toolbarBtnStyle, { fontSize: 11, padding: '4px 10px', background: filter === f ? 'rgba(99,102,241,0.3)' : toolbarBtnStyle.background, borderColor: filter === f ? 'rgba(99,102,241,0.5)' : toolbarBtnStyle.border })
|
|
487
|
-
}, f.charAt(0).toUpperCase() + f.slice(1));
|
|
488
|
-
}),
|
|
489
|
-
h('div', { style: { width: 1, height: 16, background: 'rgba(255,255,255,0.12)' } }),
|
|
490
|
-
// Legend
|
|
491
|
-
legendDot(STATUS_COLORS.in_progress, 'Active'),
|
|
492
|
-
legendDot(STATUS_COLORS.assigned, 'Assigned'),
|
|
493
|
-
legendDot(STATUS_COLORS.completed, 'Done'),
|
|
494
|
-
legendDot(STATUS_COLORS.failed, 'Failed'),
|
|
495
|
-
h('div', { style: { width: 1, height: 16, background: 'rgba(255,255,255,0.12)' } }),
|
|
496
|
-
// Zoom
|
|
497
|
-
h('button', { onClick: function() { setZoom(function(z) { return Math.min(3, z + 0.2); }); }, style: toolbarBtnStyle }, '+'),
|
|
498
|
-
h('div', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 12, minWidth: 40, textAlign: 'center' } }, Math.round(zoom * 100) + '%'),
|
|
499
|
-
h('button', { onClick: function() { setZoom(function(z) { return Math.max(0.15, z - 0.2); }); }, style: toolbarBtnStyle }, '\u2212'),
|
|
500
|
-
h('button', { onClick: fitToView, style: Object.assign({}, toolbarBtnStyle, { fontSize: 11, padding: '4px 10px' }) }, 'Fit'),
|
|
501
|
-
h('button', { onClick: loadData, style: Object.assign({}, toolbarBtnStyle, { fontSize: 11, padding: '4px 10px' }) }, 'Refresh'),
|
|
502
|
-
),
|
|
591
|
+
// Build expanded chain for inline flowchart
|
|
592
|
+
var expandedChain = null;
|
|
593
|
+
if (expandedTaskId) {
|
|
594
|
+
var et = tasks.find(function(t) { return t.id === expandedTaskId; });
|
|
595
|
+
if (et && et.chainId) {
|
|
596
|
+
expandedChain = tasks.filter(function(ct) { return ct.chainId === et.chainId; });
|
|
597
|
+
expandedChain.sort(function(a, b) { return (a.chainSeq || 0) - (b.chainSeq || 0); });
|
|
598
|
+
} else if (et) {
|
|
599
|
+
expandedChain = [et];
|
|
600
|
+
}
|
|
601
|
+
}
|
|
503
602
|
|
|
603
|
+
return h('div', { style: { height: '100%', display: 'flex', flexDirection: 'column', background: BG, borderRadius: 'var(--radius-lg)', overflow: 'hidden' } },
|
|
604
|
+
toolbar,
|
|
605
|
+
// Metrics bar
|
|
606
|
+
h(MetricsBar, { stats: stats }),
|
|
504
607
|
// Canvas
|
|
505
608
|
h('div', {
|
|
506
609
|
ref: containerRef,
|
|
@@ -509,153 +612,256 @@ export function TaskPipelinePage() {
|
|
|
509
612
|
onWheel: handleWheel,
|
|
510
613
|
},
|
|
511
614
|
h('div', { style: { transform: 'translate(' + pan.x + 'px, ' + pan.y + 'px) scale(' + zoom + ')', transformOrigin: '0 0', position: 'absolute', top: 0, left: 0 } },
|
|
512
|
-
|
|
513
|
-
|
|
615
|
+
|
|
616
|
+
// Chain labels (left side)
|
|
617
|
+
chainInfos.map(function(ci, i) {
|
|
618
|
+
return h('div', { key: ci.chainId, style: { position: 'absolute', left: 4, top: ci.y - 2, fontSize: 9, color: 'rgba(255,255,255,0.25)', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em', maxWidth: PAD - 8, overflow: 'hidden' } },
|
|
619
|
+
ci.customer && h(CustomerBadge, { customer: ci.customer })
|
|
620
|
+
);
|
|
621
|
+
}),
|
|
622
|
+
|
|
623
|
+
// SVG edges with animated flow
|
|
624
|
+
h('svg', { width: treeW + 100, height: treeH + 100, style: { position: 'absolute', top: 0, left: 0, pointerEvents: 'none', overflow: 'visible' } },
|
|
514
625
|
h('defs', null,
|
|
515
|
-
h('marker', { id: '
|
|
516
|
-
h('polygon', { points: '0 0,
|
|
626
|
+
h('marker', { id: 'tp-arr', markerWidth: 7, markerHeight: 5, refX: 7, refY: 2.5, orient: 'auto' },
|
|
627
|
+
h('polygon', { points: '0 0, 7 2.5, 0 5', fill: EDGE_COLOR })
|
|
517
628
|
),
|
|
518
|
-
h('marker', { id: '
|
|
519
|
-
h('polygon', { points: '0 0,
|
|
629
|
+
h('marker', { id: 'tp-arr-hl', markerWidth: 7, markerHeight: 5, refX: 7, refY: 2.5, orient: 'auto' },
|
|
630
|
+
h('polygon', { points: '0 0, 7 2.5, 0 5', fill: EDGE_HL })
|
|
631
|
+
),
|
|
632
|
+
// Animated glow filter
|
|
633
|
+
h('filter', { id: 'tp-glow' },
|
|
634
|
+
h('feGaussianBlur', { stdDeviation: 2, result: 'blur' }),
|
|
635
|
+
h('feMerge', null, h('feMergeNode', { in: 'blur' }), h('feMergeNode', { in: 'SourceGraphic' }))
|
|
520
636
|
)
|
|
521
637
|
),
|
|
522
638
|
edges.map(function(e, i) {
|
|
523
|
-
var
|
|
524
|
-
var
|
|
525
|
-
var isHl =
|
|
526
|
-
var dim =
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
639
|
+
var fromId = e.from.id;
|
|
640
|
+
var toId = e.to.id;
|
|
641
|
+
var isHl = hoveredChainId && e.from.chainId === hoveredChainId;
|
|
642
|
+
var dim = hoveredChainId && !isHl;
|
|
643
|
+
var dType = e.delegationType || 'delegation';
|
|
644
|
+
var edgeColor = isHl ? EDGE_HL : dim ? 'rgba(255,255,255,0.06)' : (DELEGATION_COLORS[dType] || EDGE_COLOR) + '88';
|
|
645
|
+
var d = e.isCircular ? circularPath(e.from, e.to) : horizontalPath(e.from, e.to);
|
|
646
|
+
|
|
647
|
+
return h(Fragment, { key: i },
|
|
648
|
+
// Base path
|
|
649
|
+
h('path', {
|
|
650
|
+
d: d, stroke: edgeColor, strokeWidth: isHl ? 2.5 : 1.5, fill: 'none',
|
|
651
|
+
markerEnd: isHl ? 'url(#tp-arr-hl)' : 'url(#tp-arr)',
|
|
652
|
+
style: { transition: 'stroke 0.2s, opacity 0.2s', opacity: dim ? 0.2 : 1 },
|
|
653
|
+
}),
|
|
654
|
+
// Animated flow dash overlay for active tasks
|
|
655
|
+
e.isActive && h('path', {
|
|
656
|
+
d: d, stroke: STATUS_COLORS.in_progress, strokeWidth: 2, fill: 'none',
|
|
657
|
+
strokeDasharray: '6 18',
|
|
658
|
+
className: 'tp-flow-active',
|
|
659
|
+
filter: 'url(#tp-glow)',
|
|
660
|
+
style: { opacity: dim ? 0.1 : 0.7 },
|
|
661
|
+
}),
|
|
662
|
+
// Delegation type label on edge
|
|
663
|
+
!dim && dType !== 'delegation' && h('text', {
|
|
664
|
+
x: (e.from.x + e.from.w + e.to.x) / 2,
|
|
665
|
+
y: (e.from.y + e.to.y) / 2 + (e.from.h / 2) - (e.isCircular ? 20 : 6),
|
|
666
|
+
fill: (DELEGATION_COLORS[dType] || 'rgba(255,255,255,0.3)') + (dim ? '33' : ''),
|
|
667
|
+
fontSize: 8, textAnchor: 'middle', fontWeight: 600,
|
|
668
|
+
}, dType)
|
|
669
|
+
);
|
|
536
670
|
})
|
|
537
671
|
),
|
|
538
672
|
|
|
539
|
-
//
|
|
540
|
-
|
|
541
|
-
var nodeId = node.id || node.agentId;
|
|
542
|
-
var isHovered = hoveredId === nodeId;
|
|
543
|
-
var dim = connected && !connected.has(nodeId);
|
|
544
|
-
|
|
545
|
-
if (node.isAgent) {
|
|
546
|
-
// Agent node (top-level, like org-chart node)
|
|
547
|
-
var taskCount = (node.children || []).length;
|
|
548
|
-
return h('div', {
|
|
549
|
-
key: nodeId,
|
|
550
|
-
className: 'task-node',
|
|
551
|
-
onMouseEnter: function(ev) { setHoveredId(nodeId); setMousePos({ x: ev.clientX, y: ev.clientY }); },
|
|
552
|
-
onMouseMove: function(ev) { if (isHovered) setMousePos({ x: ev.clientX, y: ev.clientY }); },
|
|
553
|
-
onMouseLeave: function() { setHoveredId(null); },
|
|
554
|
-
style: {
|
|
555
|
-
position: 'absolute', left: node.x, top: node.y, width: NODE_W, height: NODE_H,
|
|
556
|
-
background: isHovered ? 'rgba(99,102,241,0.12)' : 'rgba(255,255,255,0.03)',
|
|
557
|
-
border: '1.5px solid ' + (isHovered ? 'rgba(99,102,241,0.5)' : 'rgba(255,255,255,0.12)'),
|
|
558
|
-
borderRadius: 12, padding: '10px 14px', cursor: 'default',
|
|
559
|
-
transition: 'all 0.2s', opacity: dim ? 0.2 : 1,
|
|
560
|
-
backdropFilter: 'blur(8px)',
|
|
561
|
-
display: 'flex', alignItems: 'center', gap: 10, userSelect: 'none',
|
|
562
|
-
},
|
|
563
|
-
},
|
|
564
|
-
h('div', { style: { width: 36, height: 36, borderRadius: '50%', background: 'linear-gradient(135deg, ' + ACCENT + '44, ' + ACCENT + '11)', border: '2px solid ' + ACCENT, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 14, fontWeight: 700, color: ACCENT, flexShrink: 0 } },
|
|
565
|
-
(node.name || '?').charAt(0).toUpperCase()
|
|
566
|
-
),
|
|
567
|
-
h('div', { style: { overflow: 'hidden', flex: 1, minWidth: 0 } },
|
|
568
|
-
h('div', { style: { fontSize: 13, fontWeight: 600, color: '#fff', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, node.name),
|
|
569
|
-
h('div', { style: { fontSize: 11, color: 'rgba(255,255,255,0.45)', marginTop: 2 } }, 'Agent'),
|
|
570
|
-
h('div', { style: { display: 'flex', gap: 4, marginTop: 4 } },
|
|
571
|
-
h('span', { style: tagStyle(ACCENT) }, taskCount + ' task' + (taskCount !== 1 ? 's' : ''))
|
|
572
|
-
)
|
|
573
|
-
)
|
|
574
|
-
);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// Task node
|
|
673
|
+
// Task nodes
|
|
674
|
+
nodes.map(function(node) {
|
|
578
675
|
var t = node.task;
|
|
579
|
-
var
|
|
580
|
-
var
|
|
581
|
-
var
|
|
676
|
+
var sc = STATUS_COLORS[t.status] || '#6b7394';
|
|
677
|
+
var isHovered = hoveredId === node.id;
|
|
678
|
+
var isChainHl = hoveredChainId && node.chainId === hoveredChainId;
|
|
679
|
+
var dim = hoveredChainId && !isChainHl;
|
|
680
|
+
var isActive = t.status === 'in_progress';
|
|
582
681
|
|
|
682
|
+
var isExpanded = expandedTaskId === node.id;
|
|
583
683
|
return h('div', {
|
|
584
|
-
key:
|
|
585
|
-
className: '
|
|
586
|
-
onClick: function() {
|
|
587
|
-
|
|
588
|
-
|
|
684
|
+
key: node.id,
|
|
685
|
+
className: 'tp-node' + (isActive ? ' tp-node-active' : ''),
|
|
686
|
+
onClick: function() { toggleExpand(t); },
|
|
687
|
+
onDoubleClick: function() { openTaskDetail(t); },
|
|
688
|
+
onMouseEnter: function(ev) { setHoveredId(node.id); setMousePos({ x: ev.clientX, y: ev.clientY }); },
|
|
689
|
+
onMouseMove: function(ev) { setMousePos({ x: ev.clientX, y: ev.clientY }); },
|
|
589
690
|
onMouseLeave: function() { setHoveredId(null); },
|
|
590
691
|
style: {
|
|
591
|
-
position: 'absolute', left: node.x, top: node.y, width:
|
|
692
|
+
position: 'absolute', left: node.x, top: node.y, width: node.w, height: node.h,
|
|
592
693
|
background: isHovered ? 'rgba(255,255,255,0.06)' : 'rgba(255,255,255,0.02)',
|
|
593
|
-
border: '
|
|
594
|
-
borderLeft: '3px solid ' +
|
|
595
|
-
borderRadius:
|
|
596
|
-
transition: 'all 0.
|
|
597
|
-
backdropFilter: 'blur(
|
|
598
|
-
display: 'flex', flexDirection: 'column', justifyContent: 'center', gap:
|
|
694
|
+
border: '1px solid ' + (isExpanded ? sc : isHovered || isChainHl ? sc + '66' : 'rgba(255,255,255,0.1)'),
|
|
695
|
+
borderLeft: '3px solid ' + sc,
|
|
696
|
+
borderRadius: 10, padding: '6px 10px', cursor: 'pointer',
|
|
697
|
+
transition: 'all 0.15s', opacity: dim ? 0.15 : 1,
|
|
698
|
+
backdropFilter: 'blur(6px)',
|
|
699
|
+
display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 3, userSelect: 'none',
|
|
599
700
|
},
|
|
600
701
|
},
|
|
601
|
-
//
|
|
602
|
-
h('div', { style: { display: 'flex', alignItems: 'center', gap:
|
|
603
|
-
h('
|
|
604
|
-
|
|
702
|
+
// Agent + title row
|
|
703
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 5 } },
|
|
704
|
+
h('div', { style: { width: 18, height: 18, borderRadius: '50%', background: sc + '33', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 8, fontWeight: 700, color: sc, flexShrink: 0, border: '1px solid ' + sc + '44' } },
|
|
705
|
+
(t.assignedToName || t.assignedTo || '?').charAt(0).toUpperCase()
|
|
706
|
+
),
|
|
707
|
+
h('span', { style: { fontSize: 11, fontWeight: 600, color: '#fff', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, t.title)
|
|
605
708
|
),
|
|
606
|
-
// Status +
|
|
709
|
+
// Status + agent name + time
|
|
607
710
|
h('div', { style: { display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' } },
|
|
608
|
-
|
|
609
|
-
h('span', { style:
|
|
610
|
-
|
|
711
|
+
tag(sc, t.status.replace('_', ' ')),
|
|
712
|
+
h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.4)' } }, t.assignedToName || t.assignedTo),
|
|
713
|
+
t.delegationType && tag(DELEGATION_COLORS[t.delegationType] || '#6b7394', t.delegationType),
|
|
714
|
+
h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.3)', marginLeft: 'auto' } }, timeAgo(t.createdAt))
|
|
611
715
|
),
|
|
612
716
|
// Progress bar
|
|
613
|
-
|
|
614
|
-
h('div', { style: { height: '100%', width: t.progress + '%', background:
|
|
717
|
+
isActive && t.progress > 0 && h('div', { style: { height: 2, background: 'rgba(255,255,255,0.08)', borderRadius: 1, overflow: 'hidden', marginTop: 1 } },
|
|
718
|
+
h('div', { style: { height: '100%', width: t.progress + '%', background: sc, borderRadius: 1, transition: 'width 0.3s' } })
|
|
615
719
|
)
|
|
616
720
|
);
|
|
617
|
-
})
|
|
721
|
+
}),
|
|
722
|
+
|
|
723
|
+
// ── Expanded chain flow (rendered ON the canvas below clicked node) ──
|
|
724
|
+
expandedChain && expandedChain.length > 0 && (function() {
|
|
725
|
+
// Find the clicked node position to anchor below it
|
|
726
|
+
var anchor = nodes.find(function(n) { return n.id === expandedTaskId; });
|
|
727
|
+
if (!anchor) return null;
|
|
728
|
+
var flowY = anchor.y + anchor.h + 20;
|
|
729
|
+
var flowX = anchor.x;
|
|
730
|
+
var STEP_W = 120;
|
|
731
|
+
var STEP_H = 44;
|
|
732
|
+
var STEP_GAP = 48;
|
|
733
|
+
var ARROW_W = STEP_GAP;
|
|
734
|
+
|
|
735
|
+
// Build person-centric flow steps: createdBy → assignedTo for each chain task, then final status
|
|
736
|
+
var steps = [];
|
|
737
|
+
expandedChain.forEach(function(ct, i) {
|
|
738
|
+
if (i === 0 && ct.createdBy && ct.createdBy !== 'system') {
|
|
739
|
+
steps.push({ label: ct.createdByName || ct.createdBy, type: 'person', isHuman: ct.createdBy.indexOf('agent') === -1 && ct.createdBy !== 'system', status: null, arrow: ct.delegationType || 'assigned' });
|
|
740
|
+
} else if (i === 0 && ct.createdBy === 'system') {
|
|
741
|
+
steps.push({ label: 'System', type: 'system', isHuman: false, status: null, arrow: 'assigned' });
|
|
742
|
+
}
|
|
743
|
+
steps.push({ label: ct.assignedToName || ct.assignedTo, type: 'agent', isHuman: false, status: ct.status, taskId: ct.id, arrow: i < expandedChain.length - 1 ? (expandedChain[i + 1].delegationType || 'delegation') : null, duration: ct.actualDurationMs, progress: ct.progress });
|
|
744
|
+
});
|
|
745
|
+
// Add final status node
|
|
746
|
+
var lastTask = expandedChain[expandedChain.length - 1];
|
|
747
|
+
var isDone = lastTask.status === 'completed' || lastTask.status === 'failed' || lastTask.status === 'cancelled';
|
|
748
|
+
if (isDone) {
|
|
749
|
+
steps.push({ label: lastTask.status === 'completed' ? 'Completed!' : lastTask.status === 'failed' ? 'Failed' : 'Cancelled', type: 'terminal', isHuman: false, status: lastTask.status, arrow: null });
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
var totalW = steps.length * STEP_W + (steps.length - 1) * STEP_GAP;
|
|
753
|
+
|
|
754
|
+
return h('div', { style: { position: 'absolute', left: flowX, top: flowY, pointerEvents: 'auto' } },
|
|
755
|
+
// Background card
|
|
756
|
+
h('div', { style: { background: 'rgba(10,12,20,0.85)', border: '1px solid rgba(99,102,241,0.2)', borderRadius: 12, padding: '14px 16px 12px', backdropFilter: 'blur(8px)' } },
|
|
757
|
+
// Header
|
|
758
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 } },
|
|
759
|
+
h('span', { style: { fontSize: 10, fontWeight: 600, color: 'rgba(255,255,255,0.4)', letterSpacing: '0.06em' } }, 'TASK FLOW'),
|
|
760
|
+
expandedChain[0].chainId && h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.2)', fontFamily: 'var(--font-mono)' } }, '#' + expandedChain[0].chainId.slice(0, 8)),
|
|
761
|
+
h('div', { style: { flex: 1 } }),
|
|
762
|
+
h('button', { className: 'tp-node', onClick: function() { setExpandedTaskId(null); }, style: { background: 'none', border: 'none', color: 'rgba(255,255,255,0.3)', cursor: 'pointer', fontSize: 14, padding: '0 2px' } }, '\u00D7')
|
|
763
|
+
),
|
|
764
|
+
// Flow
|
|
765
|
+
h('div', { style: { position: 'relative', height: STEP_H + 8, minWidth: totalW } },
|
|
766
|
+
// SVG arrows
|
|
767
|
+
h('svg', { width: totalW, height: STEP_H + 8, style: { position: 'absolute', top: 0, left: 0, pointerEvents: 'none' } },
|
|
768
|
+
h('defs', null,
|
|
769
|
+
h('marker', { id: 'fc-arr', markerWidth: 6, markerHeight: 4, refX: 6, refY: 2, orient: 'auto' },
|
|
770
|
+
h('polygon', { points: '0 0, 6 2, 0 4', fill: 'rgba(99,102,241,0.5)' })
|
|
771
|
+
)
|
|
772
|
+
),
|
|
773
|
+
steps.map(function(step, i) {
|
|
774
|
+
if (i === steps.length - 1 || !step.arrow) return null;
|
|
775
|
+
var x1 = i * (STEP_W + STEP_GAP) + STEP_W;
|
|
776
|
+
var x2 = (i + 1) * (STEP_W + STEP_GAP);
|
|
777
|
+
var y = 4 + STEP_H / 2;
|
|
778
|
+
var arrowColor = DELEGATION_COLORS[step.arrow] || 'rgba(99,102,241,0.5)';
|
|
779
|
+
var nextStep = steps[i + 1];
|
|
780
|
+
var isFlowActive = step.status === 'in_progress' || (nextStep && nextStep.status === 'in_progress');
|
|
781
|
+
return h(Fragment, { key: 'a' + i },
|
|
782
|
+
h('line', { x1: x1, y1: y, x2: x2, y2: y, stroke: arrowColor + '88', strokeWidth: 2, markerEnd: 'url(#fc-arr)' }),
|
|
783
|
+
isFlowActive && h('line', { x1: x1, y1: y, x2: x2, y2: y, stroke: STATUS_COLORS.in_progress, strokeWidth: 2, strokeDasharray: '4 12', className: 'tp-flow-active', style: { opacity: 0.7 } }),
|
|
784
|
+
step.arrow !== 'assigned' && step.arrow !== 'delegation' && h('text', { x: (x1 + x2) / 2, y: y - 6, fill: arrowColor, fontSize: 8, textAnchor: 'middle', fontWeight: 600 }, step.arrow)
|
|
785
|
+
);
|
|
786
|
+
})
|
|
787
|
+
),
|
|
788
|
+
// Step nodes
|
|
789
|
+
steps.map(function(step, i) {
|
|
790
|
+
var x = i * (STEP_W + STEP_GAP);
|
|
791
|
+
var sc = step.type === 'terminal'
|
|
792
|
+
? (STATUS_COLORS[step.status] || '#22c55e')
|
|
793
|
+
: step.type === 'person' || step.isHuman
|
|
794
|
+
? '#f59e0b'
|
|
795
|
+
: step.status ? (STATUS_COLORS[step.status] || '#6366f1') : '#6366f1';
|
|
796
|
+
var isTerminal = step.type === 'terminal';
|
|
797
|
+
var isMe = step.taskId === expandedTaskId;
|
|
798
|
+
|
|
799
|
+
return h('div', {
|
|
800
|
+
key: i,
|
|
801
|
+
className: 'tp-node',
|
|
802
|
+
onClick: function(e) { e.stopPropagation(); if (step.taskId) { var ct = expandedChain.find(function(c) { return c.id === step.taskId; }); if (ct) openTaskDetail(ct); } },
|
|
803
|
+
style: {
|
|
804
|
+
position: 'absolute', left: x, top: 4, width: STEP_W, height: STEP_H,
|
|
805
|
+
background: isTerminal ? sc + '15' : isMe ? 'rgba(255,255,255,0.06)' : 'rgba(255,255,255,0.02)',
|
|
806
|
+
border: '1.5px solid ' + (isTerminal ? sc + '44' : isMe ? sc : 'rgba(255,255,255,0.1)'),
|
|
807
|
+
borderRadius: isTerminal ? 22 : 10,
|
|
808
|
+
display: 'flex', alignItems: 'center', gap: 8, padding: '0 10px',
|
|
809
|
+
cursor: step.taskId ? 'pointer' : 'default',
|
|
810
|
+
},
|
|
811
|
+
},
|
|
812
|
+
// Avatar
|
|
813
|
+
h('div', { style: {
|
|
814
|
+
width: 26, height: 26, borderRadius: '50%', flexShrink: 0,
|
|
815
|
+
background: isTerminal ? sc + '33' : step.isHuman || step.type === 'person' ? 'linear-gradient(135deg, #f59e0b, #f97316)' : step.type === 'system' ? 'rgba(255,255,255,0.1)' : 'linear-gradient(135deg, #6366f1, #8b5cf6)',
|
|
816
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
817
|
+
fontSize: isTerminal ? 12 : 10, fontWeight: 700,
|
|
818
|
+
color: isTerminal ? sc : '#fff',
|
|
819
|
+
border: '2px solid ' + (isTerminal ? sc + '44' : 'transparent'),
|
|
820
|
+
} },
|
|
821
|
+
isTerminal ? (step.status === 'completed' ? '\u2714' : step.status === 'failed' ? '\u2716' : '\u2716') : step.label.charAt(0).toUpperCase()
|
|
822
|
+
),
|
|
823
|
+
// Info
|
|
824
|
+
h('div', { style: { overflow: 'hidden', flex: 1, minWidth: 0 } },
|
|
825
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: isTerminal ? sc : '#fff', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, step.label),
|
|
826
|
+
!isTerminal && h('div', { style: { fontSize: 9, color: 'rgba(255,255,255,0.35)', marginTop: 1 } },
|
|
827
|
+
step.type === 'person' || step.isHuman ? 'Human' : step.type === 'system' ? 'System' : 'Agent',
|
|
828
|
+
step.duration ? ' \u00B7 ' + formatDuration(step.duration) : '',
|
|
829
|
+
step.status === 'in_progress' && step.progress > 0 ? ' \u00B7 ' + step.progress + '%' : ''
|
|
830
|
+
)
|
|
831
|
+
)
|
|
832
|
+
);
|
|
833
|
+
})
|
|
834
|
+
)
|
|
835
|
+
)
|
|
836
|
+
);
|
|
837
|
+
})()
|
|
618
838
|
)
|
|
619
839
|
),
|
|
620
840
|
|
|
621
841
|
// Hover tooltip
|
|
622
|
-
hoveredNode &&
|
|
842
|
+
hoveredNode && hoveredNode.task && h('div', { style: {
|
|
623
843
|
position: 'fixed', left: mousePos.x + 16, top: mousePos.y - 10,
|
|
624
844
|
background: 'rgba(15,17,23,0.95)', backdropFilter: 'blur(12px)',
|
|
625
845
|
border: '1px solid rgba(255,255,255,0.15)', borderRadius: 10,
|
|
626
|
-
padding: '
|
|
846
|
+
padding: '10px 14px', pointerEvents: 'none', zIndex: 1000, minWidth: 180, maxWidth: 280,
|
|
627
847
|
}},
|
|
628
|
-
h(
|
|
629
|
-
h('div', { style: {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
hoveredNode.task.
|
|
635
|
-
hoveredNode.task.
|
|
636
|
-
hoveredNode.task.actualDurationMs && tooltipRow('Duration', formatDuration(hoveredNode.task.actualDurationMs)),
|
|
637
|
-
hoveredNode.task.progress > 0 && tooltipRow('Progress', hoveredNode.task.progress + '%', STATUS_COLORS.in_progress)
|
|
848
|
+
hoveredNode.task.customerContext && h(CustomerBadge, { customer: hoveredNode.task.customerContext }),
|
|
849
|
+
h('div', { style: { fontSize: 12, fontWeight: 600, color: '#fff', marginBottom: 6 } }, hoveredNode.task.title),
|
|
850
|
+
h('div', { style: { display: 'flex', flexDirection: 'column', gap: 3, fontSize: 11 } },
|
|
851
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, 'Agent'), h('span', { style: { fontWeight: 600 } }, hoveredNode.task.assignedToName || '-')),
|
|
852
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, 'Status'), h('span', { style: { color: STATUS_COLORS[hoveredNode.task.status] } }, hoveredNode.task.status.replace('_', ' '))),
|
|
853
|
+
hoveredNode.task.chainId && h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, 'Chain Step'), h('span', null, '#' + ((hoveredNode.task.chainSeq || 0) + 1))),
|
|
854
|
+
hoveredNode.task.delegationType && h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, 'Type'), h('span', { style: { color: DELEGATION_COLORS[hoveredNode.task.delegationType] } }, hoveredNode.task.delegationType)),
|
|
855
|
+
hoveredNode.task.progress > 0 && h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, 'Progress'), h('span', { style: { color: STATUS_COLORS.in_progress } }, hoveredNode.task.progress + '%'))
|
|
638
856
|
)
|
|
639
857
|
),
|
|
640
858
|
|
|
641
|
-
//
|
|
642
|
-
|
|
643
|
-
position: 'fixed', left: mousePos.x + 16, top: mousePos.y - 10,
|
|
644
|
-
background: 'rgba(15,17,23,0.95)', backdropFilter: 'blur(12px)',
|
|
645
|
-
border: '1px solid rgba(255,255,255,0.15)', borderRadius: 10,
|
|
646
|
-
padding: '12px 16px', pointerEvents: 'none', zIndex: 1000, minWidth: 180,
|
|
647
|
-
}},
|
|
648
|
-
h('div', { style: { fontSize: 13, fontWeight: 600, color: '#fff', marginBottom: 6 } }, hoveredNode.name),
|
|
649
|
-
tooltipRow('Tasks', String((hoveredNode.children || []).length), ACCENT)
|
|
650
|
-
),
|
|
651
|
-
|
|
652
|
-
// Task detail modal
|
|
653
|
-
selectedTask && h(TaskDetail, { task: selectedTask, onClose: function() { setSelectedTask(null); }, onCancel: cancelTask })
|
|
859
|
+
// Detail modal (double-click)
|
|
860
|
+
selectedTask && h(TaskDetail, { task: selectedTask, chain: selectedChain, onClose: function() { setSelectedTask(null); setSelectedChain(null); }, onCancel: cancelTask })
|
|
654
861
|
);
|
|
655
862
|
}
|
|
656
863
|
|
|
657
|
-
// ─── Agent Task Pipeline (for agent-detail workforce tab) ─
|
|
658
|
-
// Reusable mini version scoped to a single agent
|
|
864
|
+
// ─── Agent Task Pipeline (reusable mini for agent-detail workforce tab) ─
|
|
659
865
|
export function AgentTaskPipeline(props) {
|
|
660
866
|
var agentId = props.agentId;
|
|
661
867
|
var _tasks = useState([]);
|
|
@@ -672,8 +878,6 @@ export function AgentTaskPipeline(props) {
|
|
|
672
878
|
engineCall('/task-pipeline/agent/' + agentId + '?completed=true').then(function(res) {
|
|
673
879
|
setTasks(res?.tasks || []);
|
|
674
880
|
}).catch(function() {}).finally(function() { setLoading(false); });
|
|
675
|
-
|
|
676
|
-
// SSE
|
|
677
881
|
var baseUrl = window.__ENGINE_BASE || '/api/engine';
|
|
678
882
|
var es;
|
|
679
883
|
try {
|
|
@@ -709,57 +913,46 @@ export function AgentTaskPipeline(props) {
|
|
|
709
913
|
var failed = tasks.filter(function(t) { return t.status === 'failed' || t.status === 'cancelled'; });
|
|
710
914
|
|
|
711
915
|
function renderTaskRow(t) {
|
|
712
|
-
var
|
|
713
|
-
var catIcon = CATEGORY_ICONS[t.category] || '\uD83D\uDCCB';
|
|
916
|
+
var sc = STATUS_COLORS[t.status] || '#6b7394';
|
|
714
917
|
return h('div', {
|
|
715
918
|
key: t.id,
|
|
716
919
|
onClick: function() { setSelectedTask(t); },
|
|
717
|
-
style: {
|
|
718
|
-
display: 'flex', alignItems: 'center', gap: 10, padding: '10px 12px',
|
|
719
|
-
borderBottom: '1px solid var(--border)', cursor: 'pointer',
|
|
720
|
-
transition: 'background 0.15s',
|
|
721
|
-
},
|
|
920
|
+
style: { display: 'flex', alignItems: 'center', gap: 8, padding: '8px 10px', borderBottom: '1px solid var(--border)', cursor: 'pointer', transition: 'background 0.15s' },
|
|
722
921
|
onMouseEnter: function(e) { e.currentTarget.style.background = 'var(--bg-secondary)'; },
|
|
723
922
|
onMouseLeave: function(e) { e.currentTarget.style.background = ''; },
|
|
724
923
|
},
|
|
725
|
-
h('
|
|
924
|
+
h('div', { style: { width: 6, height: 6, borderRadius: '50%', background: sc, flexShrink: 0 } }),
|
|
726
925
|
h('div', { style: { flex: 1, minWidth: 0 } },
|
|
727
|
-
h('div', { style: { fontSize:
|
|
728
|
-
h('div', { style: { fontSize:
|
|
926
|
+
h('div', { style: { fontSize: 12, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, t.title),
|
|
927
|
+
h('div', { style: { fontSize: 10, color: 'var(--text-muted)', marginTop: 2, display: 'flex', gap: 4, alignItems: 'center' } },
|
|
928
|
+
t.category,
|
|
929
|
+
t.delegationType && h('span', { style: { color: DELEGATION_COLORS[t.delegationType] || '#6b7394' } }, '\u2192 ' + t.delegationType),
|
|
930
|
+
h('span', null, '\u00B7 ' + timeAgo(t.createdAt))
|
|
931
|
+
)
|
|
729
932
|
),
|
|
730
|
-
t.status === 'in_progress' && t.progress > 0 && h('
|
|
731
|
-
h('span', { style: { padding: '2px
|
|
732
|
-
t.actualDurationMs && h('span', { style: { fontSize:
|
|
933
|
+
t.status === 'in_progress' && t.progress > 0 && h('span', { style: { fontSize: 10, color: sc, fontWeight: 600 } }, t.progress + '%'),
|
|
934
|
+
h('span', { style: { padding: '2px 6px', borderRadius: 8, fontSize: 9, fontWeight: 600, background: sc + '22', color: sc, flexShrink: 0 } }, t.status.replace('_', ' ')),
|
|
935
|
+
t.actualDurationMs && h('span', { style: { fontSize: 10, color: 'var(--text-muted)', flexShrink: 0 } }, formatDuration(t.actualDurationMs))
|
|
733
936
|
);
|
|
734
937
|
}
|
|
735
938
|
|
|
736
939
|
return h(Fragment, null,
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
h('div', { style: { width: 6, height: 6, borderRadius: '50%', background: STATUS_COLORS.in_progress, animation: 'pulse 2s infinite' } }),
|
|
940
|
+
active.length > 0 && h('div', { style: { marginBottom: 12 } },
|
|
941
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: STATUS_COLORS.in_progress, marginBottom: 6, display: 'flex', alignItems: 'center', gap: 4 } },
|
|
942
|
+
h('div', { style: { width: 6, height: 6, borderRadius: '50%', background: STATUS_COLORS.in_progress, animation: 'flowPulse 2s infinite' } }),
|
|
741
943
|
'Active (' + active.length + ')'
|
|
742
944
|
),
|
|
743
|
-
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } },
|
|
744
|
-
active.map(renderTaskRow)
|
|
745
|
-
)
|
|
945
|
+
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } }, active.map(renderTaskRow))
|
|
746
946
|
),
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
h('div', { style: {
|
|
750
|
-
h('div', { style: {
|
|
751
|
-
completed.slice(0, 10).map(renderTaskRow)
|
|
752
|
-
),
|
|
753
|
-
completed.length > 10 && h('div', { style: { padding: 8, textAlign: 'center', fontSize: 12, color: 'var(--text-muted)' } }, '+ ' + (completed.length - 10) + ' more')
|
|
947
|
+
completed.length > 0 && h('div', { style: { marginBottom: 12 } },
|
|
948
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: STATUS_COLORS.completed, marginBottom: 6 } }, 'Completed (' + completed.length + ')'),
|
|
949
|
+
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } }, completed.slice(0, 10).map(renderTaskRow)),
|
|
950
|
+
completed.length > 10 && h('div', { style: { padding: 6, textAlign: 'center', fontSize: 11, color: 'var(--text-muted)' } }, '+ ' + (completed.length - 10) + ' more')
|
|
754
951
|
),
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
h('div', { style: {
|
|
758
|
-
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } },
|
|
759
|
-
failed.slice(0, 5).map(renderTaskRow)
|
|
760
|
-
)
|
|
952
|
+
failed.length > 0 && h('div', { style: { marginBottom: 12 } },
|
|
953
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: STATUS_COLORS.failed, marginBottom: 6 } }, 'Failed (' + failed.length + ')'),
|
|
954
|
+
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } }, failed.slice(0, 5).map(renderTaskRow))
|
|
761
955
|
),
|
|
762
|
-
|
|
763
|
-
selectedTask && h(TaskDetail, { task: selectedTask, onClose: function() { setSelectedTask(null); }, onCancel: cancelTask })
|
|
956
|
+
selectedTask && h(TaskDetail, { task: selectedTask, chain: null, onClose: function() { setSelectedTask(null); }, onCancel: cancelTask })
|
|
764
957
|
);
|
|
765
958
|
}
|