@agenticmail/enterprise 0.5.209 → 0.5.210
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.
|
@@ -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
|
-
function flatten(node, parent) {
|
|
108
|
-
allNodes.push(node);
|
|
109
|
-
var w = NODE_W;
|
|
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); });
|
|
116
130
|
|
|
117
|
-
return {
|
|
131
|
+
return { nodes: allNodes, edges: allEdges, width: maxX + PAD, height: y + PAD, chains: chainInfos };
|
|
118
132
|
}
|
|
119
133
|
|
|
120
|
-
// ─── SVG Edge
|
|
121
|
-
function
|
|
122
|
-
var x1 =
|
|
123
|
-
var y1 =
|
|
124
|
-
var x2 =
|
|
125
|
-
var y2 =
|
|
126
|
-
var
|
|
127
|
-
return 'M ' + x1 + ' ' + y1 + ' C ' +
|
|
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;
|
|
142
|
+
}
|
|
143
|
+
|
|
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)
|
|
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
|
+
)
|
|
206
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
|
)
|
|
@@ -236,6 +310,7 @@ function TaskDetail(props) {
|
|
|
236
310
|
|
|
237
311
|
// ─── Main Page ───────────────────────────────────────────
|
|
238
312
|
export function TaskPipelinePage() {
|
|
313
|
+
injectCSS();
|
|
239
314
|
var app = useApp();
|
|
240
315
|
var toast = app.toast;
|
|
241
316
|
var _tasks = useState([]);
|
|
@@ -246,10 +321,10 @@ export function TaskPipelinePage() {
|
|
|
246
321
|
var loading = _loading[0]; var setLoading = _loading[1];
|
|
247
322
|
var _selectedTask = useState(null);
|
|
248
323
|
var selectedTask = _selectedTask[0]; var setSelectedTask = _selectedTask[1];
|
|
324
|
+
var _selectedChain = useState(null);
|
|
325
|
+
var selectedChain = _selectedChain[0]; var setSelectedChain = _selectedChain[1];
|
|
249
326
|
var _hoveredId = useState(null);
|
|
250
327
|
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
328
|
var _zoom = useState(1);
|
|
254
329
|
var zoom = _zoom[0]; var setZoom = _zoom[1];
|
|
255
330
|
var _pan = useState({ x: 0, y: 0 });
|
|
@@ -260,6 +335,8 @@ export function TaskPipelinePage() {
|
|
|
260
335
|
var dragStart = _dragStart[0]; var setDragStart = _dragStart[1];
|
|
261
336
|
var _filter = useState('active');
|
|
262
337
|
var filter = _filter[0]; var setFilter = _filter[1];
|
|
338
|
+
var _mousePos = useState({ x: 0, y: 0 });
|
|
339
|
+
var mousePos = _mousePos[0]; var setMousePos = _mousePos[1];
|
|
263
340
|
var containerRef = useRef(null);
|
|
264
341
|
|
|
265
342
|
var loadData = useCallback(function() {
|
|
@@ -270,9 +347,8 @@ export function TaskPipelinePage() {
|
|
|
270
347
|
]).then(function(res) {
|
|
271
348
|
setTasks(res[0]?.tasks || []);
|
|
272
349
|
setStats(res[1] || stats);
|
|
273
|
-
}).catch(function(err) {
|
|
274
|
-
|
|
275
|
-
}).finally(function() { setLoading(false); });
|
|
350
|
+
}).catch(function(err) { console.error('[TaskPipeline]', err); })
|
|
351
|
+
.finally(function() { setLoading(false); });
|
|
276
352
|
}, []);
|
|
277
353
|
|
|
278
354
|
// SSE
|
|
@@ -299,10 +375,7 @@ export function TaskPipelinePage() {
|
|
|
299
375
|
if (idx >= 0) { var next = prev.slice(); next[idx] = event.task; return next; }
|
|
300
376
|
return [event.task].concat(prev);
|
|
301
377
|
});
|
|
302
|
-
setSelectedTask(function(prev) {
|
|
303
|
-
if (prev && prev.id === event.task.id) return event.task;
|
|
304
|
-
return prev;
|
|
305
|
-
});
|
|
378
|
+
setSelectedTask(function(prev) { return prev && prev.id === event.task.id ? event.task : prev; });
|
|
306
379
|
}
|
|
307
380
|
} catch (err) {}
|
|
308
381
|
};
|
|
@@ -310,7 +383,6 @@ export function TaskPipelinePage() {
|
|
|
310
383
|
return function() { if (es) es.close(); };
|
|
311
384
|
}, []);
|
|
312
385
|
|
|
313
|
-
// Periodic stats
|
|
314
386
|
useEffect(function() {
|
|
315
387
|
var iv = setInterval(function() {
|
|
316
388
|
engineCall('/task-pipeline/stats').then(function(s) { if (s) setStats(s); }).catch(function() {});
|
|
@@ -326,6 +398,18 @@ export function TaskPipelinePage() {
|
|
|
326
398
|
}).catch(function(err) { toast(err.message || 'Failed', 'error'); });
|
|
327
399
|
}, []);
|
|
328
400
|
|
|
401
|
+
function openTaskDetail(t) {
|
|
402
|
+
setSelectedTask(t);
|
|
403
|
+
// Load full chain if task has one
|
|
404
|
+
if (t.chainId) {
|
|
405
|
+
var chainTasks = tasks.filter(function(ct) { return ct.chainId === t.chainId; });
|
|
406
|
+
chainTasks.sort(function(a, b) { return (a.chainSeq || 0) - (b.chainSeq || 0); });
|
|
407
|
+
setSelectedChain(chainTasks.length > 1 ? chainTasks : null);
|
|
408
|
+
} else {
|
|
409
|
+
setSelectedChain(null);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
329
413
|
// Filter
|
|
330
414
|
var filtered = tasks.filter(function(t) {
|
|
331
415
|
if (filter === 'active') return t.status === 'created' || t.status === 'assigned' || t.status === 'in_progress';
|
|
@@ -335,34 +419,28 @@ export function TaskPipelinePage() {
|
|
|
335
419
|
});
|
|
336
420
|
|
|
337
421
|
// Layout
|
|
338
|
-
var layout =
|
|
339
|
-
var
|
|
422
|
+
var layout = layoutChains(filtered);
|
|
423
|
+
var nodes = layout.nodes;
|
|
340
424
|
var edges = layout.edges;
|
|
341
425
|
var treeW = layout.width;
|
|
342
426
|
var treeH = layout.height;
|
|
427
|
+
var chainInfos = layout.chains;
|
|
343
428
|
|
|
344
|
-
// Zoom
|
|
429
|
+
// Zoom/Pan handlers
|
|
345
430
|
var handleWheel = useCallback(function(e) {
|
|
346
431
|
e.preventDefault();
|
|
347
|
-
|
|
348
|
-
setZoom(function(z) { return Math.min(3, Math.max(0.15, z + delta)); });
|
|
432
|
+
setZoom(function(z) { return Math.min(3, Math.max(0.15, z + (e.deltaY > 0 ? -0.08 : 0.08))); });
|
|
349
433
|
}, []);
|
|
350
|
-
|
|
351
|
-
// Pan
|
|
352
434
|
var handleMouseDown = useCallback(function(e) {
|
|
353
|
-
if (e.button !== 0) return;
|
|
354
|
-
if (e.target.closest('.task-node')) return;
|
|
435
|
+
if (e.button !== 0 || e.target.closest('.tp-node')) return;
|
|
355
436
|
setDragging(true);
|
|
356
437
|
setDragStart({ x: e.clientX - pan.x, y: e.clientY - pan.y });
|
|
357
438
|
}, [pan]);
|
|
358
|
-
|
|
359
439
|
var handleMouseMove = useCallback(function(e) {
|
|
360
440
|
if (!dragging) return;
|
|
361
441
|
setPan({ x: e.clientX - dragStart.x, y: e.clientY - dragStart.y });
|
|
362
442
|
}, [dragging, dragStart]);
|
|
363
|
-
|
|
364
443
|
var handleMouseUp = useCallback(function() { setDragging(false); }, []);
|
|
365
|
-
|
|
366
444
|
useEffect(function() {
|
|
367
445
|
if (dragging) {
|
|
368
446
|
window.addEventListener('mousemove', handleMouseMove);
|
|
@@ -371,7 +449,6 @@ export function TaskPipelinePage() {
|
|
|
371
449
|
}
|
|
372
450
|
}, [dragging, handleMouseMove, handleMouseUp]);
|
|
373
451
|
|
|
374
|
-
// Fit
|
|
375
452
|
var fitToView = useCallback(function() {
|
|
376
453
|
if (!containerRef.current || !treeW || !treeH) return;
|
|
377
454
|
var rect = containerRef.current.getBoundingClientRect();
|
|
@@ -379,128 +456,81 @@ export function TaskPipelinePage() {
|
|
|
379
456
|
var scaleY = (rect.height - 40) / treeH;
|
|
380
457
|
var scale = Math.min(scaleX, scaleY, 1.5);
|
|
381
458
|
setZoom(scale);
|
|
382
|
-
setPan({ x: (rect.width - treeW * scale) / 2, y:
|
|
459
|
+
setPan({ x: (rect.width - treeW * scale) / 2, y: 20 });
|
|
383
460
|
}, [treeW, treeH]);
|
|
461
|
+
useEffect(function() { if (nodes.length > 0) fitToView(); }, [nodes.length]);
|
|
384
462
|
|
|
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;
|
|
463
|
+
// Highlight connected chain on hover
|
|
464
|
+
var hoveredChainId = null;
|
|
465
|
+
if (hoveredId) {
|
|
466
|
+
var hn = nodes.find(function(n) { return n.id === hoveredId; });
|
|
467
|
+
if (hn) hoveredChainId = hn.chainId;
|
|
468
|
+
}
|
|
408
469
|
|
|
409
|
-
|
|
410
|
-
|
|
470
|
+
var hoveredNode = hoveredId ? nodes.find(function(n) { return n.id === hoveredId; }) : null;
|
|
471
|
+
|
|
472
|
+
// ─── Toolbar ─────────────────────────────────────────
|
|
473
|
+
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' } },
|
|
474
|
+
h('div', { style: { fontWeight: 700, fontSize: 14, color: '#fff', display: 'flex', alignItems: 'center', gap: 6 } },
|
|
475
|
+
I.workflow(), 'Task Pipeline',
|
|
476
|
+
h(HelpButton, { label: 'Task Pipeline' },
|
|
477
|
+
h('p', null, 'Visual flow of all agent tasks. Tasks flow left-to-right showing delegation chains, multi-agent handoffs, and circular review loops.'),
|
|
478
|
+
h('h4', { style: _h4 }, 'Features'),
|
|
479
|
+
h('ul', { style: _ul },
|
|
480
|
+
h('li', null, h('strong', null, 'Horizontal flow'), ' \u2014 Tasks move left to right through agents'),
|
|
481
|
+
h('li', null, h('strong', null, 'Chain tracking'), ' \u2014 When a manager delegates to a junior, the chain shows the full flow'),
|
|
482
|
+
h('li', null, h('strong', null, 'Circular flows'), ' \u2014 If a task returns (revision/review), the arc curves back'),
|
|
483
|
+
h('li', null, h('strong', null, 'Animated lines'), ' \u2014 Active tasks show flowing dashes on their connections'),
|
|
484
|
+
h('li', null, h('strong', null, 'Customer profiles'), ' \u2014 Support tasks show the customer\'s info'),
|
|
485
|
+
h('li', null, h('strong', null, 'Real-time SSE'), ' \u2014 Updates stream live, no refresh needed')
|
|
486
|
+
),
|
|
487
|
+
h('h4', { style: _h4 }, 'Interactions'),
|
|
488
|
+
h('ul', { style: _ul },
|
|
489
|
+
h('li', null, h('strong', null, 'Hover'), ' \u2014 Highlights the entire chain'),
|
|
490
|
+
h('li', null, h('strong', null, 'Click'), ' \u2014 Opens detail modal with chain timeline, activity log, customer context'),
|
|
491
|
+
h('li', null, h('strong', null, 'Scroll'), ' \u2014 Zoom'),
|
|
492
|
+
h('li', null, h('strong', null, 'Drag'), ' \u2014 Pan')
|
|
493
|
+
),
|
|
494
|
+
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.')
|
|
495
|
+
)
|
|
496
|
+
),
|
|
497
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 4 } },
|
|
498
|
+
h('div', { style: { width: 8, height: 8, borderRadius: '50%', background: '#22c55e', animation: 'flowPulse 2s infinite' } }),
|
|
499
|
+
h('span', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 11 } }, 'Live')
|
|
500
|
+
),
|
|
501
|
+
h('div', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 11 } },
|
|
502
|
+
(stats.inProgress || 0) + ' active \u00B7 ' + (stats.completed || 0) + ' done \u00B7 ' + (stats.total || 0) + ' total'
|
|
503
|
+
),
|
|
504
|
+
h('div', { style: { flex: 1 } }),
|
|
505
|
+
// Filters
|
|
506
|
+
['active', 'all', 'completed', 'failed'].map(function(f) {
|
|
507
|
+
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));
|
|
508
|
+
}),
|
|
509
|
+
h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.12)' } }),
|
|
510
|
+
legendDot(STATUS_COLORS.in_progress, 'Active'),
|
|
511
|
+
legendDot(STATUS_COLORS.completed, 'Done'),
|
|
512
|
+
legendDot(STATUS_COLORS.failed, 'Failed'),
|
|
513
|
+
h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.12)' } }),
|
|
514
|
+
h('button', { onClick: function() { setZoom(function(z) { return Math.min(3, z + 0.2); }); }, style: toolbarBtnStyle }, '+'),
|
|
515
|
+
h('div', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 11, minWidth: 36, textAlign: 'center' } }, Math.round(zoom * 100) + '%'),
|
|
516
|
+
h('button', { onClick: function() { setZoom(function(z) { return Math.max(0.15, z - 0.2); }); }, style: toolbarBtnStyle }, '\u2212'),
|
|
517
|
+
h('button', { onClick: fitToView, style: toolbarBtnStyle }, 'Fit'),
|
|
518
|
+
h('button', { onClick: loadData, style: toolbarBtnStyle }, 'Refresh'),
|
|
519
|
+
);
|
|
411
520
|
|
|
412
521
|
if (loading) return h('div', { style: { padding: 40, textAlign: 'center', color: 'var(--text-muted)' } }, 'Loading task pipeline...');
|
|
413
522
|
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
h('div', { style: { display: 'flex', alignItems: 'center', gap: 12, padding: '12px 20px', borderBottom: '1px solid rgba(255,255,255,0.08)', background: 'rgba(0,0,0,0.3)' } },
|
|
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
|
-
),
|
|
523
|
+
if (nodes.length === 0) return h('div', { style: { height: '100%', display: 'flex', flexDirection: 'column', background: BG, borderRadius: 'var(--radius-lg)', overflow: 'hidden' } },
|
|
524
|
+
toolbar,
|
|
439
525
|
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.')
|
|
526
|
+
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()),
|
|
527
|
+
h('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 6, color: '#fff' } }, 'No Tasks in Pipeline'),
|
|
528
|
+
h('div', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 13 } }, 'Tasks will appear here as agents are assigned work.')
|
|
443
529
|
)
|
|
444
530
|
);
|
|
445
531
|
|
|
446
532
|
return h('div', { style: { height: '100%', display: 'flex', flexDirection: 'column', background: BG, borderRadius: 'var(--radius-lg)', overflow: 'hidden' } },
|
|
447
|
-
|
|
448
|
-
h('div', { style: { display: 'flex', alignItems: 'center', gap: 12, padding: '12px 20px', borderBottom: '1px solid rgba(255,255,255,0.08)', background: 'rgba(0,0,0,0.3)', flexShrink: 0, flexWrap: 'wrap' } },
|
|
449
|
-
h('div', { style: { fontWeight: 700, fontSize: 16, color: '#fff', display: 'flex', alignItems: 'center', gap: 8 } },
|
|
450
|
-
I.workflow(), ' Task Pipeline',
|
|
451
|
-
h(HelpButton, { label: 'Task Pipeline' },
|
|
452
|
-
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.'),
|
|
453
|
-
h('h4', { style: _h4 }, 'Interactions'),
|
|
454
|
-
h('ul', { style: _ul },
|
|
455
|
-
h('li', null, h('strong', null, 'Hover'), ' — Highlights the task chain and shows detail tooltip'),
|
|
456
|
-
h('li', null, h('strong', null, 'Click'), ' — Opens full task detail modal'),
|
|
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
|
-
),
|
|
503
|
-
|
|
533
|
+
toolbar,
|
|
504
534
|
// Canvas
|
|
505
535
|
h('div', {
|
|
506
536
|
ref: containerRef,
|
|
@@ -509,109 +539,108 @@ export function TaskPipelinePage() {
|
|
|
509
539
|
onWheel: handleWheel,
|
|
510
540
|
},
|
|
511
541
|
h('div', { style: { transform: 'translate(' + pan.x + 'px, ' + pan.y + 'px) scale(' + zoom + ')', transformOrigin: '0 0', position: 'absolute', top: 0, left: 0 } },
|
|
512
|
-
|
|
513
|
-
|
|
542
|
+
|
|
543
|
+
// Chain labels (left side)
|
|
544
|
+
chainInfos.map(function(ci, i) {
|
|
545
|
+
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' } },
|
|
546
|
+
ci.customer && h(CustomerBadge, { customer: ci.customer })
|
|
547
|
+
);
|
|
548
|
+
}),
|
|
549
|
+
|
|
550
|
+
// SVG edges with animated flow
|
|
551
|
+
h('svg', { width: treeW + 100, height: treeH + 100, style: { position: 'absolute', top: 0, left: 0, pointerEvents: 'none', overflow: 'visible' } },
|
|
514
552
|
h('defs', null,
|
|
515
|
-
h('marker', { id: '
|
|
516
|
-
h('polygon', { points: '0 0,
|
|
553
|
+
h('marker', { id: 'tp-arr', markerWidth: 7, markerHeight: 5, refX: 7, refY: 2.5, orient: 'auto' },
|
|
554
|
+
h('polygon', { points: '0 0, 7 2.5, 0 5', fill: EDGE_COLOR })
|
|
555
|
+
),
|
|
556
|
+
h('marker', { id: 'tp-arr-hl', markerWidth: 7, markerHeight: 5, refX: 7, refY: 2.5, orient: 'auto' },
|
|
557
|
+
h('polygon', { points: '0 0, 7 2.5, 0 5', fill: EDGE_HL })
|
|
517
558
|
),
|
|
518
|
-
|
|
519
|
-
|
|
559
|
+
// Animated glow filter
|
|
560
|
+
h('filter', { id: 'tp-glow' },
|
|
561
|
+
h('feGaussianBlur', { stdDeviation: 2, result: 'blur' }),
|
|
562
|
+
h('feMerge', null, h('feMergeNode', { in: 'blur' }), h('feMergeNode', { in: 'SourceGraphic' }))
|
|
520
563
|
)
|
|
521
564
|
),
|
|
522
565
|
edges.map(function(e, i) {
|
|
523
|
-
var
|
|
524
|
-
var
|
|
525
|
-
var isHl =
|
|
526
|
-
var dim =
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
566
|
+
var fromId = e.from.id;
|
|
567
|
+
var toId = e.to.id;
|
|
568
|
+
var isHl = hoveredChainId && e.from.chainId === hoveredChainId;
|
|
569
|
+
var dim = hoveredChainId && !isHl;
|
|
570
|
+
var dType = e.delegationType || 'delegation';
|
|
571
|
+
var edgeColor = isHl ? EDGE_HL : dim ? 'rgba(255,255,255,0.06)' : (DELEGATION_COLORS[dType] || EDGE_COLOR) + '88';
|
|
572
|
+
var d = e.isCircular ? circularPath(e.from, e.to) : horizontalPath(e.from, e.to);
|
|
573
|
+
|
|
574
|
+
return h(Fragment, { key: i },
|
|
575
|
+
// Base path
|
|
576
|
+
h('path', {
|
|
577
|
+
d: d, stroke: edgeColor, strokeWidth: isHl ? 2.5 : 1.5, fill: 'none',
|
|
578
|
+
markerEnd: isHl ? 'url(#tp-arr-hl)' : 'url(#tp-arr)',
|
|
579
|
+
style: { transition: 'stroke 0.2s, opacity 0.2s', opacity: dim ? 0.2 : 1 },
|
|
580
|
+
}),
|
|
581
|
+
// Animated flow dash overlay for active tasks
|
|
582
|
+
e.isActive && h('path', {
|
|
583
|
+
d: d, stroke: STATUS_COLORS.in_progress, strokeWidth: 2, fill: 'none',
|
|
584
|
+
strokeDasharray: '6 18',
|
|
585
|
+
className: 'tp-flow-active',
|
|
586
|
+
filter: 'url(#tp-glow)',
|
|
587
|
+
style: { opacity: dim ? 0.1 : 0.7 },
|
|
588
|
+
}),
|
|
589
|
+
// Delegation type label on edge
|
|
590
|
+
!dim && dType !== 'delegation' && h('text', {
|
|
591
|
+
x: (e.from.x + e.from.w + e.to.x) / 2,
|
|
592
|
+
y: (e.from.y + e.to.y) / 2 + (e.from.h / 2) - (e.isCircular ? 20 : 6),
|
|
593
|
+
fill: (DELEGATION_COLORS[dType] || 'rgba(255,255,255,0.3)') + (dim ? '33' : ''),
|
|
594
|
+
fontSize: 8, textAnchor: 'middle', fontWeight: 600,
|
|
595
|
+
}, dType)
|
|
596
|
+
);
|
|
536
597
|
})
|
|
537
598
|
),
|
|
538
599
|
|
|
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
|
|
600
|
+
// Task nodes
|
|
601
|
+
nodes.map(function(node) {
|
|
578
602
|
var t = node.task;
|
|
579
|
-
var
|
|
580
|
-
var
|
|
581
|
-
var
|
|
603
|
+
var sc = STATUS_COLORS[t.status] || '#6b7394';
|
|
604
|
+
var isHovered = hoveredId === node.id;
|
|
605
|
+
var isChainHl = hoveredChainId && node.chainId === hoveredChainId;
|
|
606
|
+
var dim = hoveredChainId && !isChainHl;
|
|
607
|
+
var isActive = t.status === 'in_progress';
|
|
582
608
|
|
|
583
609
|
return h('div', {
|
|
584
|
-
key:
|
|
585
|
-
className: '
|
|
586
|
-
onClick: function() {
|
|
587
|
-
onMouseEnter: function(ev) { setHoveredId(
|
|
588
|
-
onMouseMove: function(ev) {
|
|
610
|
+
key: node.id,
|
|
611
|
+
className: 'tp-node' + (isActive ? ' tp-node-active' : ''),
|
|
612
|
+
onClick: function() { openTaskDetail(t); },
|
|
613
|
+
onMouseEnter: function(ev) { setHoveredId(node.id); setMousePos({ x: ev.clientX, y: ev.clientY }); },
|
|
614
|
+
onMouseMove: function(ev) { setMousePos({ x: ev.clientX, y: ev.clientY }); },
|
|
589
615
|
onMouseLeave: function() { setHoveredId(null); },
|
|
590
616
|
style: {
|
|
591
|
-
position: 'absolute', left: node.x, top: node.y, width:
|
|
617
|
+
position: 'absolute', left: node.x, top: node.y, width: node.w, height: node.h,
|
|
592
618
|
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:
|
|
619
|
+
border: '1px solid ' + (isHovered || isChainHl ? sc + '66' : 'rgba(255,255,255,0.1)'),
|
|
620
|
+
borderLeft: '3px solid ' + sc,
|
|
621
|
+
borderRadius: 10, padding: '6px 10px', cursor: 'pointer',
|
|
622
|
+
transition: 'all 0.15s', opacity: dim ? 0.15 : 1,
|
|
623
|
+
backdropFilter: 'blur(6px)',
|
|
624
|
+
display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 3, userSelect: 'none',
|
|
599
625
|
},
|
|
600
626
|
},
|
|
601
|
-
//
|
|
602
|
-
h('div', { style: { display: 'flex', alignItems: 'center', gap:
|
|
603
|
-
h('
|
|
604
|
-
|
|
627
|
+
// Agent + title row
|
|
628
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 5 } },
|
|
629
|
+
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' } },
|
|
630
|
+
(t.assignedToName || t.assignedTo || '?').charAt(0).toUpperCase()
|
|
631
|
+
),
|
|
632
|
+
h('span', { style: { fontSize: 11, fontWeight: 600, color: '#fff', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, t.title)
|
|
605
633
|
),
|
|
606
|
-
// Status +
|
|
634
|
+
// Status + agent name + time
|
|
607
635
|
h('div', { style: { display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' } },
|
|
608
|
-
|
|
609
|
-
h('span', { style:
|
|
610
|
-
|
|
636
|
+
tag(sc, t.status.replace('_', ' ')),
|
|
637
|
+
h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.4)' } }, t.assignedToName || t.assignedTo),
|
|
638
|
+
t.delegationType && tag(DELEGATION_COLORS[t.delegationType] || '#6b7394', t.delegationType),
|
|
639
|
+
h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.3)', marginLeft: 'auto' } }, timeAgo(t.createdAt))
|
|
611
640
|
),
|
|
612
641
|
// Progress bar
|
|
613
|
-
|
|
614
|
-
h('div', { style: { height: '100%', width: t.progress + '%', background:
|
|
642
|
+
isActive && t.progress > 0 && h('div', { style: { height: 2, background: 'rgba(255,255,255,0.08)', borderRadius: 1, overflow: 'hidden', marginTop: 1 } },
|
|
643
|
+
h('div', { style: { height: '100%', width: t.progress + '%', background: sc, borderRadius: 1, transition: 'width 0.3s' } })
|
|
615
644
|
)
|
|
616
645
|
);
|
|
617
646
|
})
|
|
@@ -619,43 +648,29 @@ export function TaskPipelinePage() {
|
|
|
619
648
|
),
|
|
620
649
|
|
|
621
650
|
// Hover tooltip
|
|
622
|
-
hoveredNode &&
|
|
651
|
+
hoveredNode && hoveredNode.task && h('div', { style: {
|
|
623
652
|
position: 'fixed', left: mousePos.x + 16, top: mousePos.y - 10,
|
|
624
653
|
background: 'rgba(15,17,23,0.95)', backdropFilter: 'blur(12px)',
|
|
625
654
|
border: '1px solid rgba(255,255,255,0.15)', borderRadius: 10,
|
|
626
|
-
padding: '
|
|
655
|
+
padding: '10px 14px', pointerEvents: 'none', zIndex: 1000, minWidth: 180, maxWidth: 280,
|
|
627
656
|
}},
|
|
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)
|
|
657
|
+
hoveredNode.task.customerContext && h(CustomerBadge, { customer: hoveredNode.task.customerContext }),
|
|
658
|
+
h('div', { style: { fontSize: 12, fontWeight: 600, color: '#fff', marginBottom: 6 } }, hoveredNode.task.title),
|
|
659
|
+
h('div', { style: { display: 'flex', flexDirection: 'column', gap: 3, fontSize: 11 } },
|
|
660
|
+
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 || '-')),
|
|
661
|
+
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('_', ' '))),
|
|
662
|
+
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))),
|
|
663
|
+
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)),
|
|
664
|
+
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
665
|
)
|
|
639
666
|
),
|
|
640
667
|
|
|
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 })
|
|
668
|
+
// Detail modal
|
|
669
|
+
selectedTask && h(TaskDetail, { task: selectedTask, chain: selectedChain, onClose: function() { setSelectedTask(null); setSelectedChain(null); }, onCancel: cancelTask })
|
|
654
670
|
);
|
|
655
671
|
}
|
|
656
672
|
|
|
657
|
-
// ─── Agent Task Pipeline (for agent-detail workforce tab) ─
|
|
658
|
-
// Reusable mini version scoped to a single agent
|
|
673
|
+
// ─── Agent Task Pipeline (reusable mini for agent-detail workforce tab) ─
|
|
659
674
|
export function AgentTaskPipeline(props) {
|
|
660
675
|
var agentId = props.agentId;
|
|
661
676
|
var _tasks = useState([]);
|
|
@@ -672,8 +687,6 @@ export function AgentTaskPipeline(props) {
|
|
|
672
687
|
engineCall('/task-pipeline/agent/' + agentId + '?completed=true').then(function(res) {
|
|
673
688
|
setTasks(res?.tasks || []);
|
|
674
689
|
}).catch(function() {}).finally(function() { setLoading(false); });
|
|
675
|
-
|
|
676
|
-
// SSE
|
|
677
690
|
var baseUrl = window.__ENGINE_BASE || '/api/engine';
|
|
678
691
|
var es;
|
|
679
692
|
try {
|
|
@@ -709,57 +722,46 @@ export function AgentTaskPipeline(props) {
|
|
|
709
722
|
var failed = tasks.filter(function(t) { return t.status === 'failed' || t.status === 'cancelled'; });
|
|
710
723
|
|
|
711
724
|
function renderTaskRow(t) {
|
|
712
|
-
var
|
|
713
|
-
var catIcon = CATEGORY_ICONS[t.category] || '\uD83D\uDCCB';
|
|
725
|
+
var sc = STATUS_COLORS[t.status] || '#6b7394';
|
|
714
726
|
return h('div', {
|
|
715
727
|
key: t.id,
|
|
716
728
|
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
|
-
},
|
|
729
|
+
style: { display: 'flex', alignItems: 'center', gap: 8, padding: '8px 10px', borderBottom: '1px solid var(--border)', cursor: 'pointer', transition: 'background 0.15s' },
|
|
722
730
|
onMouseEnter: function(e) { e.currentTarget.style.background = 'var(--bg-secondary)'; },
|
|
723
731
|
onMouseLeave: function(e) { e.currentTarget.style.background = ''; },
|
|
724
732
|
},
|
|
725
|
-
h('
|
|
733
|
+
h('div', { style: { width: 6, height: 6, borderRadius: '50%', background: sc, flexShrink: 0 } }),
|
|
726
734
|
h('div', { style: { flex: 1, minWidth: 0 } },
|
|
727
|
-
h('div', { style: { fontSize:
|
|
728
|
-
h('div', { style: { fontSize:
|
|
735
|
+
h('div', { style: { fontSize: 12, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, t.title),
|
|
736
|
+
h('div', { style: { fontSize: 10, color: 'var(--text-muted)', marginTop: 2, display: 'flex', gap: 4, alignItems: 'center' } },
|
|
737
|
+
t.category,
|
|
738
|
+
t.delegationType && h('span', { style: { color: DELEGATION_COLORS[t.delegationType] || '#6b7394' } }, '\u2192 ' + t.delegationType),
|
|
739
|
+
h('span', null, '\u00B7 ' + timeAgo(t.createdAt))
|
|
740
|
+
)
|
|
729
741
|
),
|
|
730
|
-
t.status === 'in_progress' && t.progress > 0 && h('
|
|
731
|
-
h('span', { style: { padding: '2px
|
|
732
|
-
t.actualDurationMs && h('span', { style: { fontSize:
|
|
742
|
+
t.status === 'in_progress' && t.progress > 0 && h('span', { style: { fontSize: 10, color: sc, fontWeight: 600 } }, t.progress + '%'),
|
|
743
|
+
h('span', { style: { padding: '2px 6px', borderRadius: 8, fontSize: 9, fontWeight: 600, background: sc + '22', color: sc, flexShrink: 0 } }, t.status.replace('_', ' ')),
|
|
744
|
+
t.actualDurationMs && h('span', { style: { fontSize: 10, color: 'var(--text-muted)', flexShrink: 0 } }, formatDuration(t.actualDurationMs))
|
|
733
745
|
);
|
|
734
746
|
}
|
|
735
747
|
|
|
736
748
|
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' } }),
|
|
749
|
+
active.length > 0 && h('div', { style: { marginBottom: 12 } },
|
|
750
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: STATUS_COLORS.in_progress, marginBottom: 6, display: 'flex', alignItems: 'center', gap: 4 } },
|
|
751
|
+
h('div', { style: { width: 6, height: 6, borderRadius: '50%', background: STATUS_COLORS.in_progress, animation: 'flowPulse 2s infinite' } }),
|
|
741
752
|
'Active (' + active.length + ')'
|
|
742
753
|
),
|
|
743
|
-
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } },
|
|
744
|
-
active.map(renderTaskRow)
|
|
745
|
-
)
|
|
754
|
+
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } }, active.map(renderTaskRow))
|
|
746
755
|
),
|
|
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')
|
|
756
|
+
completed.length > 0 && h('div', { style: { marginBottom: 12 } },
|
|
757
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: STATUS_COLORS.completed, marginBottom: 6 } }, 'Completed (' + completed.length + ')'),
|
|
758
|
+
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } }, completed.slice(0, 10).map(renderTaskRow)),
|
|
759
|
+
completed.length > 10 && h('div', { style: { padding: 6, textAlign: 'center', fontSize: 11, color: 'var(--text-muted)' } }, '+ ' + (completed.length - 10) + ' more')
|
|
754
760
|
),
|
|
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
|
-
)
|
|
761
|
+
failed.length > 0 && h('div', { style: { marginBottom: 12 } },
|
|
762
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: STATUS_COLORS.failed, marginBottom: 6 } }, 'Failed (' + failed.length + ')'),
|
|
763
|
+
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } }, failed.slice(0, 5).map(renderTaskRow))
|
|
761
764
|
),
|
|
762
|
-
|
|
763
|
-
selectedTask && h(TaskDetail, { task: selectedTask, onClose: function() { setSelectedTask(null); }, onCancel: cancelTask })
|
|
765
|
+
selectedTask && h(TaskDetail, { task: selectedTask, chain: null, onClose: function() { setSelectedTask(null); }, onCancel: cancelTask })
|
|
764
766
|
);
|
|
765
767
|
}
|