@agenticmail/enterprise 0.5.211 → 0.5.212
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 +598 -429
- 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 +28 -33
|
@@ -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:
|
|
172
|
-
);
|
|
173
|
-
}
|
|
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)
|
|
182
|
+
h('span', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 11 } }, label)
|
|
179
183
|
);
|
|
180
184
|
}
|
|
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,147 @@ 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
|
+
// ─── Inline Chain Flowchart (appears below canvas when task expanded) ─
|
|
348
|
+
function ChainFlowInline(props) {
|
|
349
|
+
var chain = props.chain;
|
|
350
|
+
var taskId = props.taskId;
|
|
351
|
+
var onClose = props.onClose;
|
|
352
|
+
var onClickTask = props.onClickTask;
|
|
353
|
+
if (!chain || chain.length === 0) return null;
|
|
354
|
+
|
|
355
|
+
var MINI_W = 180;
|
|
356
|
+
var MINI_H = 56;
|
|
357
|
+
var MINI_GAP = 40;
|
|
358
|
+
var totalW = chain.length * MINI_W + (chain.length - 1) * MINI_GAP + 40;
|
|
359
|
+
|
|
360
|
+
return h('div', { style: { borderTop: '1px solid rgba(255,255,255,0.08)', background: 'rgba(0,0,0,0.25)', padding: '12px 16px', flexShrink: 0, overflow: 'auto' } },
|
|
361
|
+
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 } },
|
|
362
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
363
|
+
h('span', { style: { fontSize: 11, fontWeight: 600, color: 'rgba(255,255,255,0.5)', letterSpacing: '0.06em' } }, 'TASK FLOW'),
|
|
364
|
+
h('span', { style: { fontSize: 10, color: 'rgba(255,255,255,0.3)', fontFamily: 'var(--font-mono)' } }, chain[0].chainId ? '#' + chain[0].chainId.slice(0, 8) : ''),
|
|
365
|
+
h('span', { style: { fontSize: 10, color: 'rgba(255,255,255,0.3)' } }, chain.length + ' step' + (chain.length > 1 ? 's' : ''))
|
|
366
|
+
),
|
|
367
|
+
h('button', { onClick: onClose, style: { background: 'none', border: 'none', color: 'rgba(255,255,255,0.3)', cursor: 'pointer', fontSize: 16, padding: '0 4px' } }, '\u00D7')
|
|
368
|
+
),
|
|
369
|
+
h('div', { style: { position: 'relative', height: MINI_H + 20, minWidth: totalW } },
|
|
370
|
+
// SVG arrows
|
|
371
|
+
h('svg', { width: totalW, height: MINI_H + 20, style: { position: 'absolute', top: 0, left: 0, pointerEvents: 'none' } },
|
|
372
|
+
h('defs', null,
|
|
373
|
+
h('marker', { id: 'cf-arr', markerWidth: 6, markerHeight: 4, refX: 6, refY: 2, orient: 'auto' },
|
|
374
|
+
h('polygon', { points: '0 0, 6 2, 0 4', fill: 'rgba(99,102,241,0.5)' })
|
|
375
|
+
)
|
|
376
|
+
),
|
|
377
|
+
chain.map(function(ct, i) {
|
|
378
|
+
if (i === 0) return null;
|
|
379
|
+
var x1 = 20 + (i - 1) * (MINI_W + MINI_GAP) + MINI_W;
|
|
380
|
+
var x2 = 20 + i * (MINI_W + MINI_GAP);
|
|
381
|
+
var y = 10 + MINI_H / 2;
|
|
382
|
+
var dType = ct.delegationType || 'delegation';
|
|
383
|
+
var color = DELEGATION_COLORS[dType] || 'rgba(99,102,241,0.5)';
|
|
384
|
+
var isActive = chain[i - 1].status === 'in_progress' || ct.status === 'in_progress';
|
|
385
|
+
return h(Fragment, { key: 'e' + i },
|
|
386
|
+
h('line', { x1: x1, y1: y, x2: x2, y2: y, stroke: color + '88', strokeWidth: 2, markerEnd: 'url(#cf-arr)' }),
|
|
387
|
+
isActive && 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 } }),
|
|
388
|
+
dType !== 'delegation' && h('text', { x: (x1 + x2) / 2, y: y - 6, fill: color, fontSize: 8, textAnchor: 'middle', fontWeight: 600 }, dType)
|
|
389
|
+
);
|
|
390
|
+
})
|
|
391
|
+
),
|
|
392
|
+
// Task nodes
|
|
393
|
+
chain.map(function(ct, i) {
|
|
394
|
+
var x = 20 + i * (MINI_W + MINI_GAP);
|
|
395
|
+
var sc = STATUS_COLORS[ct.status] || '#6b7394';
|
|
396
|
+
var isMe = ct.id === taskId;
|
|
397
|
+
var isActive = ct.status === 'in_progress';
|
|
398
|
+
return h('div', {
|
|
399
|
+
key: ct.id,
|
|
400
|
+
onClick: function() { if (onClickTask) onClickTask(ct); },
|
|
401
|
+
style: {
|
|
402
|
+
position: 'absolute', left: x, top: 10, width: MINI_W, height: MINI_H,
|
|
403
|
+
background: isMe ? sc + '15' : 'rgba(255,255,255,0.02)',
|
|
404
|
+
border: '1.5px solid ' + (isMe ? sc : 'rgba(255,255,255,0.1)'),
|
|
405
|
+
borderLeft: '3px solid ' + sc,
|
|
406
|
+
borderRadius: 8, padding: '6px 10px', cursor: 'pointer',
|
|
407
|
+
display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 3,
|
|
408
|
+
transition: 'all 0.15s',
|
|
409
|
+
},
|
|
410
|
+
onMouseEnter: function(e) { e.currentTarget.style.background = 'rgba(255,255,255,0.05)'; },
|
|
411
|
+
onMouseLeave: function(e) { e.currentTarget.style.background = isMe ? sc + '15' : 'rgba(255,255,255,0.02)'; },
|
|
412
|
+
},
|
|
413
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 5 } },
|
|
414
|
+
h('div', { style: { width: 16, height: 16, borderRadius: '50%', background: sc + '33', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 7, fontWeight: 700, color: sc, flexShrink: 0 } },
|
|
415
|
+
(ct.assignedToName || '?').charAt(0).toUpperCase()
|
|
416
|
+
),
|
|
417
|
+
h('span', { style: { fontSize: 10, fontWeight: 600, color: '#fff', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 } }, ct.assignedToName || ct.assignedTo)
|
|
418
|
+
),
|
|
419
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 4 } },
|
|
420
|
+
tag(sc, ct.status.replace('_', ' ')),
|
|
421
|
+
h('span', { style: { fontSize: 8, color: 'rgba(255,255,255,0.3)' } }, timeAgo(ct.createdAt)),
|
|
422
|
+
ct.actualDurationMs && h('span', { style: { fontSize: 8, color: 'rgba(255,255,255,0.3)', marginLeft: 'auto' } }, formatDuration(ct.actualDurationMs))
|
|
423
|
+
),
|
|
424
|
+
isActive && ct.progress > 0 && h('div', { style: { height: 2, background: 'rgba(255,255,255,0.08)', borderRadius: 1, overflow: 'hidden' } },
|
|
425
|
+
h('div', { style: { height: '100%', width: ct.progress + '%', background: sc, borderRadius: 1 } })
|
|
426
|
+
)
|
|
427
|
+
);
|
|
428
|
+
})
|
|
429
|
+
)
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
237
433
|
// ─── Main Page ───────────────────────────────────────────
|
|
238
434
|
export function TaskPipelinePage() {
|
|
435
|
+
injectCSS();
|
|
239
436
|
var app = useApp();
|
|
240
437
|
var toast = app.toast;
|
|
241
438
|
var _tasks = useState([]);
|
|
242
439
|
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 });
|
|
440
|
+
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
441
|
var stats = _stats[0]; var setStats = _stats[1];
|
|
442
|
+
var _expandedTaskId = useState(null);
|
|
443
|
+
var expandedTaskId = _expandedTaskId[0]; var setExpandedTaskId = _expandedTaskId[1];
|
|
245
444
|
var _loading = useState(true);
|
|
246
445
|
var loading = _loading[0]; var setLoading = _loading[1];
|
|
247
446
|
var _selectedTask = useState(null);
|
|
248
447
|
var selectedTask = _selectedTask[0]; var setSelectedTask = _selectedTask[1];
|
|
448
|
+
var _selectedChain = useState(null);
|
|
449
|
+
var selectedChain = _selectedChain[0]; var setSelectedChain = _selectedChain[1];
|
|
249
450
|
var _hoveredId = useState(null);
|
|
250
451
|
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
452
|
var _zoom = useState(1);
|
|
254
453
|
var zoom = _zoom[0]; var setZoom = _zoom[1];
|
|
255
454
|
var _pan = useState({ x: 0, y: 0 });
|
|
@@ -260,6 +459,8 @@ export function TaskPipelinePage() {
|
|
|
260
459
|
var dragStart = _dragStart[0]; var setDragStart = _dragStart[1];
|
|
261
460
|
var _filter = useState('active');
|
|
262
461
|
var filter = _filter[0]; var setFilter = _filter[1];
|
|
462
|
+
var _mousePos = useState({ x: 0, y: 0 });
|
|
463
|
+
var mousePos = _mousePos[0]; var setMousePos = _mousePos[1];
|
|
263
464
|
var containerRef = useRef(null);
|
|
264
465
|
|
|
265
466
|
var loadData = useCallback(function() {
|
|
@@ -270,9 +471,8 @@ export function TaskPipelinePage() {
|
|
|
270
471
|
]).then(function(res) {
|
|
271
472
|
setTasks(res[0]?.tasks || []);
|
|
272
473
|
setStats(res[1] || stats);
|
|
273
|
-
}).catch(function(err) {
|
|
274
|
-
|
|
275
|
-
}).finally(function() { setLoading(false); });
|
|
474
|
+
}).catch(function(err) { console.error('[TaskPipeline]', err); })
|
|
475
|
+
.finally(function() { setLoading(false); });
|
|
276
476
|
}, []);
|
|
277
477
|
|
|
278
478
|
// SSE
|
|
@@ -299,10 +499,9 @@ export function TaskPipelinePage() {
|
|
|
299
499
|
if (idx >= 0) { var next = prev.slice(); next[idx] = event.task; return next; }
|
|
300
500
|
return [event.task].concat(prev);
|
|
301
501
|
});
|
|
302
|
-
setSelectedTask(function(prev) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
});
|
|
502
|
+
setSelectedTask(function(prev) { return prev && prev.id === event.task.id ? event.task : prev; });
|
|
503
|
+
// Refresh stats on every task event for real-time metrics
|
|
504
|
+
engineCall('/task-pipeline/stats').then(function(s) { if (s) setStats(s); }).catch(function() {});
|
|
306
505
|
}
|
|
307
506
|
} catch (err) {}
|
|
308
507
|
};
|
|
@@ -310,11 +509,10 @@ export function TaskPipelinePage() {
|
|
|
310
509
|
return function() { if (es) es.close(); };
|
|
311
510
|
}, []);
|
|
312
511
|
|
|
313
|
-
// Periodic stats
|
|
314
512
|
useEffect(function() {
|
|
315
513
|
var iv = setInterval(function() {
|
|
316
514
|
engineCall('/task-pipeline/stats').then(function(s) { if (s) setStats(s); }).catch(function() {});
|
|
317
|
-
},
|
|
515
|
+
}, 15000);
|
|
318
516
|
return function() { clearInterval(iv); };
|
|
319
517
|
}, []);
|
|
320
518
|
|
|
@@ -326,6 +524,34 @@ export function TaskPipelinePage() {
|
|
|
326
524
|
}).catch(function(err) { toast(err.message || 'Failed', 'error'); });
|
|
327
525
|
}, []);
|
|
328
526
|
|
|
527
|
+
function openTaskDetail(t) {
|
|
528
|
+
setSelectedTask(t);
|
|
529
|
+
if (t.chainId) {
|
|
530
|
+
var chainTasks = tasks.filter(function(ct) { return ct.chainId === t.chainId; });
|
|
531
|
+
chainTasks.sort(function(a, b) { return (a.chainSeq || 0) - (b.chainSeq || 0); });
|
|
532
|
+
setSelectedChain(chainTasks.length > 1 ? chainTasks : null);
|
|
533
|
+
} else {
|
|
534
|
+
setSelectedChain(null);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Toggle inline chain flowchart (single click on node)
|
|
539
|
+
function toggleExpand(t) {
|
|
540
|
+
if (expandedTaskId === t.id) {
|
|
541
|
+
setExpandedTaskId(null);
|
|
542
|
+
setSelectedChain(null);
|
|
543
|
+
} else {
|
|
544
|
+
setExpandedTaskId(t.id);
|
|
545
|
+
if (t.chainId) {
|
|
546
|
+
var chainTasks = tasks.filter(function(ct) { return ct.chainId === t.chainId; });
|
|
547
|
+
chainTasks.sort(function(a, b) { return (a.chainSeq || 0) - (b.chainSeq || 0); });
|
|
548
|
+
setSelectedChain(chainTasks.length > 0 ? chainTasks : [t]);
|
|
549
|
+
} else {
|
|
550
|
+
setSelectedChain([t]);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
329
555
|
// Filter
|
|
330
556
|
var filtered = tasks.filter(function(t) {
|
|
331
557
|
if (filter === 'active') return t.status === 'created' || t.status === 'assigned' || t.status === 'in_progress';
|
|
@@ -335,34 +561,28 @@ export function TaskPipelinePage() {
|
|
|
335
561
|
});
|
|
336
562
|
|
|
337
563
|
// Layout
|
|
338
|
-
var layout =
|
|
339
|
-
var
|
|
564
|
+
var layout = layoutChains(filtered);
|
|
565
|
+
var nodes = layout.nodes;
|
|
340
566
|
var edges = layout.edges;
|
|
341
567
|
var treeW = layout.width;
|
|
342
568
|
var treeH = layout.height;
|
|
569
|
+
var chainInfos = layout.chains;
|
|
343
570
|
|
|
344
|
-
// Zoom
|
|
571
|
+
// Zoom/Pan handlers
|
|
345
572
|
var handleWheel = useCallback(function(e) {
|
|
346
573
|
e.preventDefault();
|
|
347
|
-
|
|
348
|
-
setZoom(function(z) { return Math.min(3, Math.max(0.15, z + delta)); });
|
|
574
|
+
setZoom(function(z) { return Math.min(3, Math.max(0.15, z + (e.deltaY > 0 ? -0.08 : 0.08))); });
|
|
349
575
|
}, []);
|
|
350
|
-
|
|
351
|
-
// Pan
|
|
352
576
|
var handleMouseDown = useCallback(function(e) {
|
|
353
|
-
if (e.button !== 0) return;
|
|
354
|
-
if (e.target.closest('.task-node')) return;
|
|
577
|
+
if (e.button !== 0 || e.target.closest('.tp-node')) return;
|
|
355
578
|
setDragging(true);
|
|
356
579
|
setDragStart({ x: e.clientX - pan.x, y: e.clientY - pan.y });
|
|
357
580
|
}, [pan]);
|
|
358
|
-
|
|
359
581
|
var handleMouseMove = useCallback(function(e) {
|
|
360
582
|
if (!dragging) return;
|
|
361
583
|
setPan({ x: e.clientX - dragStart.x, y: e.clientY - dragStart.y });
|
|
362
584
|
}, [dragging, dragStart]);
|
|
363
|
-
|
|
364
585
|
var handleMouseUp = useCallback(function() { setDragging(false); }, []);
|
|
365
|
-
|
|
366
586
|
useEffect(function() {
|
|
367
587
|
if (dragging) {
|
|
368
588
|
window.addEventListener('mousemove', handleMouseMove);
|
|
@@ -371,7 +591,6 @@ export function TaskPipelinePage() {
|
|
|
371
591
|
}
|
|
372
592
|
}, [dragging, handleMouseMove, handleMouseUp]);
|
|
373
593
|
|
|
374
|
-
// Fit
|
|
375
594
|
var fitToView = useCallback(function() {
|
|
376
595
|
if (!containerRef.current || !treeW || !treeH) return;
|
|
377
596
|
var rect = containerRef.current.getBoundingClientRect();
|
|
@@ -379,128 +598,96 @@ export function TaskPipelinePage() {
|
|
|
379
598
|
var scaleY = (rect.height - 40) / treeH;
|
|
380
599
|
var scale = Math.min(scaleX, scaleY, 1.5);
|
|
381
600
|
setZoom(scale);
|
|
382
|
-
setPan({ x: (rect.width - treeW * scale) / 2, y:
|
|
601
|
+
setPan({ x: (rect.width - treeW * scale) / 2, y: 20 });
|
|
383
602
|
}, [treeW, treeH]);
|
|
603
|
+
useEffect(function() { if (nodes.length > 0) fitToView(); }, [nodes.length]);
|
|
384
604
|
|
|
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;
|
|
605
|
+
// Highlight connected chain on hover
|
|
606
|
+
var hoveredChainId = null;
|
|
607
|
+
if (hoveredId) {
|
|
608
|
+
var hn = nodes.find(function(n) { return n.id === hoveredId; });
|
|
609
|
+
if (hn) hoveredChainId = hn.chainId;
|
|
610
|
+
}
|
|
408
611
|
|
|
409
|
-
|
|
410
|
-
|
|
612
|
+
var hoveredNode = hoveredId ? nodes.find(function(n) { return n.id === hoveredId; }) : null;
|
|
613
|
+
|
|
614
|
+
// ─── Toolbar ─────────────────────────────────────────
|
|
615
|
+
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' } },
|
|
616
|
+
h('div', { style: { fontWeight: 700, fontSize: 14, color: '#fff', display: 'flex', alignItems: 'center', gap: 6 } },
|
|
617
|
+
I.workflow(), 'Task Pipeline',
|
|
618
|
+
h(HelpButton, { label: 'Task Pipeline' },
|
|
619
|
+
h('p', null, 'Visual flow of all agent tasks. Tasks flow left-to-right showing delegation chains, multi-agent handoffs, and circular review loops.'),
|
|
620
|
+
h('h4', { style: _h4 }, 'Features'),
|
|
621
|
+
h('ul', { style: _ul },
|
|
622
|
+
h('li', null, h('strong', null, 'Horizontal flow'), ' \u2014 Tasks move left to right through agents'),
|
|
623
|
+
h('li', null, h('strong', null, 'Chain tracking'), ' \u2014 When a manager delegates to a junior, the chain shows the full flow'),
|
|
624
|
+
h('li', null, h('strong', null, 'Circular flows'), ' \u2014 If a task returns (revision/review), the arc curves back'),
|
|
625
|
+
h('li', null, h('strong', null, 'Animated lines'), ' \u2014 Active tasks show flowing dashes on their connections'),
|
|
626
|
+
h('li', null, h('strong', null, 'Customer profiles'), ' \u2014 Support tasks show the customer\'s info'),
|
|
627
|
+
h('li', null, h('strong', null, 'Real-time SSE'), ' \u2014 Updates stream live, no refresh needed')
|
|
628
|
+
),
|
|
629
|
+
h('h4', { style: _h4 }, 'Interactions'),
|
|
630
|
+
h('ul', { style: _ul },
|
|
631
|
+
h('li', null, h('strong', null, 'Hover'), ' \u2014 Highlights the entire chain'),
|
|
632
|
+
h('li', null, h('strong', null, 'Click'), ' \u2014 Opens detail modal with chain timeline, activity log, customer context'),
|
|
633
|
+
h('li', null, h('strong', null, 'Scroll'), ' \u2014 Zoom'),
|
|
634
|
+
h('li', null, h('strong', null, 'Drag'), ' \u2014 Pan')
|
|
635
|
+
),
|
|
636
|
+
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.')
|
|
637
|
+
)
|
|
638
|
+
),
|
|
639
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 4 } },
|
|
640
|
+
h('div', { style: { width: 8, height: 8, borderRadius: '50%', background: '#22c55e', animation: 'flowPulse 2s infinite' } }),
|
|
641
|
+
h('span', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 11 } }, 'Live')
|
|
642
|
+
),
|
|
643
|
+
h('div', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 11 } },
|
|
644
|
+
(stats.inProgress || 0) + ' active \u00B7 ' + (stats.completed || 0) + ' done \u00B7 ' + (stats.total || 0) + ' total'
|
|
645
|
+
),
|
|
646
|
+
h('div', { style: { flex: 1 } }),
|
|
647
|
+
// Filters
|
|
648
|
+
['active', 'all', 'completed', 'failed'].map(function(f) {
|
|
649
|
+
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));
|
|
650
|
+
}),
|
|
651
|
+
h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.12)' } }),
|
|
652
|
+
legendDot(STATUS_COLORS.in_progress, 'Active'),
|
|
653
|
+
legendDot(STATUS_COLORS.completed, 'Done'),
|
|
654
|
+
legendDot(STATUS_COLORS.failed, 'Failed'),
|
|
655
|
+
h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.12)' } }),
|
|
656
|
+
h('button', { onClick: function() { setZoom(function(z) { return Math.min(3, z + 0.2); }); }, style: toolbarBtnStyle }, '+'),
|
|
657
|
+
h('div', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 11, minWidth: 36, textAlign: 'center' } }, Math.round(zoom * 100) + '%'),
|
|
658
|
+
h('button', { onClick: function() { setZoom(function(z) { return Math.max(0.15, z - 0.2); }); }, style: toolbarBtnStyle }, '\u2212'),
|
|
659
|
+
h('button', { onClick: fitToView, style: toolbarBtnStyle }, 'Fit'),
|
|
660
|
+
h('button', { onClick: loadData, style: toolbarBtnStyle }, 'Refresh'),
|
|
661
|
+
);
|
|
411
662
|
|
|
412
663
|
if (loading) return h('div', { style: { padding: 40, textAlign: 'center', color: 'var(--text-muted)' } }, 'Loading task pipeline...');
|
|
413
664
|
|
|
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
|
-
),
|
|
665
|
+
if (nodes.length === 0) return h('div', { style: { height: '100%', display: 'flex', flexDirection: 'column', background: BG, borderRadius: 'var(--radius-lg)', overflow: 'hidden' } },
|
|
666
|
+
toolbar,
|
|
667
|
+
h(MetricsBar, { stats: stats }),
|
|
439
668
|
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.')
|
|
669
|
+
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()),
|
|
670
|
+
h('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 6, color: '#fff' } }, 'No Tasks in Pipeline'),
|
|
671
|
+
h('div', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 13 } }, 'Tasks will appear here as agents are assigned work.')
|
|
443
672
|
)
|
|
444
673
|
);
|
|
445
674
|
|
|
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
|
-
),
|
|
675
|
+
// Build expanded chain for inline flowchart
|
|
676
|
+
var expandedChain = null;
|
|
677
|
+
if (expandedTaskId) {
|
|
678
|
+
var et = tasks.find(function(t) { return t.id === expandedTaskId; });
|
|
679
|
+
if (et && et.chainId) {
|
|
680
|
+
expandedChain = tasks.filter(function(ct) { return ct.chainId === et.chainId; });
|
|
681
|
+
expandedChain.sort(function(a, b) { return (a.chainSeq || 0) - (b.chainSeq || 0); });
|
|
682
|
+
} else if (et) {
|
|
683
|
+
expandedChain = [et];
|
|
684
|
+
}
|
|
685
|
+
}
|
|
503
686
|
|
|
687
|
+
return h('div', { style: { height: '100%', display: 'flex', flexDirection: 'column', background: BG, borderRadius: 'var(--radius-lg)', overflow: 'hidden' } },
|
|
688
|
+
toolbar,
|
|
689
|
+
// Metrics bar
|
|
690
|
+
h(MetricsBar, { stats: stats }),
|
|
504
691
|
// Canvas
|
|
505
692
|
h('div', {
|
|
506
693
|
ref: containerRef,
|
|
@@ -509,109 +696,110 @@ export function TaskPipelinePage() {
|
|
|
509
696
|
onWheel: handleWheel,
|
|
510
697
|
},
|
|
511
698
|
h('div', { style: { transform: 'translate(' + pan.x + 'px, ' + pan.y + 'px) scale(' + zoom + ')', transformOrigin: '0 0', position: 'absolute', top: 0, left: 0 } },
|
|
512
|
-
|
|
513
|
-
|
|
699
|
+
|
|
700
|
+
// Chain labels (left side)
|
|
701
|
+
chainInfos.map(function(ci, i) {
|
|
702
|
+
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' } },
|
|
703
|
+
ci.customer && h(CustomerBadge, { customer: ci.customer })
|
|
704
|
+
);
|
|
705
|
+
}),
|
|
706
|
+
|
|
707
|
+
// SVG edges with animated flow
|
|
708
|
+
h('svg', { width: treeW + 100, height: treeH + 100, style: { position: 'absolute', top: 0, left: 0, pointerEvents: 'none', overflow: 'visible' } },
|
|
514
709
|
h('defs', null,
|
|
515
|
-
h('marker', { id: '
|
|
516
|
-
h('polygon', { points: '0 0,
|
|
710
|
+
h('marker', { id: 'tp-arr', markerWidth: 7, markerHeight: 5, refX: 7, refY: 2.5, orient: 'auto' },
|
|
711
|
+
h('polygon', { points: '0 0, 7 2.5, 0 5', fill: EDGE_COLOR })
|
|
712
|
+
),
|
|
713
|
+
h('marker', { id: 'tp-arr-hl', markerWidth: 7, markerHeight: 5, refX: 7, refY: 2.5, orient: 'auto' },
|
|
714
|
+
h('polygon', { points: '0 0, 7 2.5, 0 5', fill: EDGE_HL })
|
|
517
715
|
),
|
|
518
|
-
|
|
519
|
-
|
|
716
|
+
// Animated glow filter
|
|
717
|
+
h('filter', { id: 'tp-glow' },
|
|
718
|
+
h('feGaussianBlur', { stdDeviation: 2, result: 'blur' }),
|
|
719
|
+
h('feMerge', null, h('feMergeNode', { in: 'blur' }), h('feMergeNode', { in: 'SourceGraphic' }))
|
|
520
720
|
)
|
|
521
721
|
),
|
|
522
722
|
edges.map(function(e, i) {
|
|
523
|
-
var
|
|
524
|
-
var
|
|
525
|
-
var isHl =
|
|
526
|
-
var dim =
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
723
|
+
var fromId = e.from.id;
|
|
724
|
+
var toId = e.to.id;
|
|
725
|
+
var isHl = hoveredChainId && e.from.chainId === hoveredChainId;
|
|
726
|
+
var dim = hoveredChainId && !isHl;
|
|
727
|
+
var dType = e.delegationType || 'delegation';
|
|
728
|
+
var edgeColor = isHl ? EDGE_HL : dim ? 'rgba(255,255,255,0.06)' : (DELEGATION_COLORS[dType] || EDGE_COLOR) + '88';
|
|
729
|
+
var d = e.isCircular ? circularPath(e.from, e.to) : horizontalPath(e.from, e.to);
|
|
730
|
+
|
|
731
|
+
return h(Fragment, { key: i },
|
|
732
|
+
// Base path
|
|
733
|
+
h('path', {
|
|
734
|
+
d: d, stroke: edgeColor, strokeWidth: isHl ? 2.5 : 1.5, fill: 'none',
|
|
735
|
+
markerEnd: isHl ? 'url(#tp-arr-hl)' : 'url(#tp-arr)',
|
|
736
|
+
style: { transition: 'stroke 0.2s, opacity 0.2s', opacity: dim ? 0.2 : 1 },
|
|
737
|
+
}),
|
|
738
|
+
// Animated flow dash overlay for active tasks
|
|
739
|
+
e.isActive && h('path', {
|
|
740
|
+
d: d, stroke: STATUS_COLORS.in_progress, strokeWidth: 2, fill: 'none',
|
|
741
|
+
strokeDasharray: '6 18',
|
|
742
|
+
className: 'tp-flow-active',
|
|
743
|
+
filter: 'url(#tp-glow)',
|
|
744
|
+
style: { opacity: dim ? 0.1 : 0.7 },
|
|
745
|
+
}),
|
|
746
|
+
// Delegation type label on edge
|
|
747
|
+
!dim && dType !== 'delegation' && h('text', {
|
|
748
|
+
x: (e.from.x + e.from.w + e.to.x) / 2,
|
|
749
|
+
y: (e.from.y + e.to.y) / 2 + (e.from.h / 2) - (e.isCircular ? 20 : 6),
|
|
750
|
+
fill: (DELEGATION_COLORS[dType] || 'rgba(255,255,255,0.3)') + (dim ? '33' : ''),
|
|
751
|
+
fontSize: 8, textAnchor: 'middle', fontWeight: 600,
|
|
752
|
+
}, dType)
|
|
753
|
+
);
|
|
536
754
|
})
|
|
537
755
|
),
|
|
538
756
|
|
|
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
|
|
757
|
+
// Task nodes
|
|
758
|
+
nodes.map(function(node) {
|
|
578
759
|
var t = node.task;
|
|
579
|
-
var
|
|
580
|
-
var
|
|
581
|
-
var
|
|
760
|
+
var sc = STATUS_COLORS[t.status] || '#6b7394';
|
|
761
|
+
var isHovered = hoveredId === node.id;
|
|
762
|
+
var isChainHl = hoveredChainId && node.chainId === hoveredChainId;
|
|
763
|
+
var dim = hoveredChainId && !isChainHl;
|
|
764
|
+
var isActive = t.status === 'in_progress';
|
|
582
765
|
|
|
766
|
+
var isExpanded = expandedTaskId === node.id;
|
|
583
767
|
return h('div', {
|
|
584
|
-
key:
|
|
585
|
-
className: '
|
|
586
|
-
onClick: function() {
|
|
587
|
-
|
|
588
|
-
|
|
768
|
+
key: node.id,
|
|
769
|
+
className: 'tp-node' + (isActive ? ' tp-node-active' : ''),
|
|
770
|
+
onClick: function() { toggleExpand(t); },
|
|
771
|
+
onDoubleClick: function() { openTaskDetail(t); },
|
|
772
|
+
onMouseEnter: function(ev) { setHoveredId(node.id); setMousePos({ x: ev.clientX, y: ev.clientY }); },
|
|
773
|
+
onMouseMove: function(ev) { setMousePos({ x: ev.clientX, y: ev.clientY }); },
|
|
589
774
|
onMouseLeave: function() { setHoveredId(null); },
|
|
590
775
|
style: {
|
|
591
|
-
position: 'absolute', left: node.x, top: node.y, width:
|
|
776
|
+
position: 'absolute', left: node.x, top: node.y, width: node.w, height: node.h,
|
|
592
777
|
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:
|
|
778
|
+
border: '1px solid ' + (isExpanded ? sc : isHovered || isChainHl ? sc + '66' : 'rgba(255,255,255,0.1)'),
|
|
779
|
+
borderLeft: '3px solid ' + sc,
|
|
780
|
+
borderRadius: 10, padding: '6px 10px', cursor: 'pointer',
|
|
781
|
+
transition: 'all 0.15s', opacity: dim ? 0.15 : 1,
|
|
782
|
+
backdropFilter: 'blur(6px)',
|
|
783
|
+
display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 3, userSelect: 'none',
|
|
599
784
|
},
|
|
600
785
|
},
|
|
601
|
-
//
|
|
602
|
-
h('div', { style: { display: 'flex', alignItems: 'center', gap:
|
|
603
|
-
h('
|
|
604
|
-
|
|
786
|
+
// Agent + title row
|
|
787
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 5 } },
|
|
788
|
+
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' } },
|
|
789
|
+
(t.assignedToName || t.assignedTo || '?').charAt(0).toUpperCase()
|
|
790
|
+
),
|
|
791
|
+
h('span', { style: { fontSize: 11, fontWeight: 600, color: '#fff', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, t.title)
|
|
605
792
|
),
|
|
606
|
-
// Status +
|
|
793
|
+
// Status + agent name + time
|
|
607
794
|
h('div', { style: { display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' } },
|
|
608
|
-
|
|
609
|
-
h('span', { style:
|
|
610
|
-
|
|
795
|
+
tag(sc, t.status.replace('_', ' ')),
|
|
796
|
+
h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.4)' } }, t.assignedToName || t.assignedTo),
|
|
797
|
+
t.delegationType && tag(DELEGATION_COLORS[t.delegationType] || '#6b7394', t.delegationType),
|
|
798
|
+
h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.3)', marginLeft: 'auto' } }, timeAgo(t.createdAt))
|
|
611
799
|
),
|
|
612
800
|
// Progress bar
|
|
613
|
-
|
|
614
|
-
h('div', { style: { height: '100%', width: t.progress + '%', background:
|
|
801
|
+
isActive && t.progress > 0 && h('div', { style: { height: 2, background: 'rgba(255,255,255,0.08)', borderRadius: 1, overflow: 'hidden', marginTop: 1 } },
|
|
802
|
+
h('div', { style: { height: '100%', width: t.progress + '%', background: sc, borderRadius: 1, transition: 'width 0.3s' } })
|
|
615
803
|
)
|
|
616
804
|
);
|
|
617
805
|
})
|
|
@@ -619,43 +807,37 @@ export function TaskPipelinePage() {
|
|
|
619
807
|
),
|
|
620
808
|
|
|
621
809
|
// Hover tooltip
|
|
622
|
-
hoveredNode &&
|
|
810
|
+
hoveredNode && hoveredNode.task && h('div', { style: {
|
|
623
811
|
position: 'fixed', left: mousePos.x + 16, top: mousePos.y - 10,
|
|
624
812
|
background: 'rgba(15,17,23,0.95)', backdropFilter: 'blur(12px)',
|
|
625
813
|
border: '1px solid rgba(255,255,255,0.15)', borderRadius: 10,
|
|
626
|
-
padding: '
|
|
814
|
+
padding: '10px 14px', pointerEvents: 'none', zIndex: 1000, minWidth: 180, maxWidth: 280,
|
|
627
815
|
}},
|
|
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)
|
|
816
|
+
hoveredNode.task.customerContext && h(CustomerBadge, { customer: hoveredNode.task.customerContext }),
|
|
817
|
+
h('div', { style: { fontSize: 12, fontWeight: 600, color: '#fff', marginBottom: 6 } }, hoveredNode.task.title),
|
|
818
|
+
h('div', { style: { display: 'flex', flexDirection: 'column', gap: 3, fontSize: 11 } },
|
|
819
|
+
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 || '-')),
|
|
820
|
+
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('_', ' '))),
|
|
821
|
+
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))),
|
|
822
|
+
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)),
|
|
823
|
+
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
824
|
)
|
|
639
825
|
),
|
|
640
826
|
|
|
641
|
-
//
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
h('div', { style: { fontSize: 13, fontWeight: 600, color: '#fff', marginBottom: 6 } }, hoveredNode.name),
|
|
649
|
-
tooltipRow('Tasks', String((hoveredNode.children || []).length), ACCENT)
|
|
650
|
-
),
|
|
827
|
+
// Inline chain flowchart (shown when a task is clicked)
|
|
828
|
+
expandedChain && expandedChain.length > 0 && h(ChainFlowInline, {
|
|
829
|
+
chain: expandedChain,
|
|
830
|
+
taskId: expandedTaskId,
|
|
831
|
+
onClose: function() { setExpandedTaskId(null); },
|
|
832
|
+
onClickTask: function(t) { openTaskDetail(t); }
|
|
833
|
+
}),
|
|
651
834
|
|
|
652
|
-
//
|
|
653
|
-
selectedTask && h(TaskDetail, { task: selectedTask, onClose: function() { setSelectedTask(null); }, onCancel: cancelTask })
|
|
835
|
+
// Detail modal (double-click)
|
|
836
|
+
selectedTask && h(TaskDetail, { task: selectedTask, chain: selectedChain, onClose: function() { setSelectedTask(null); setSelectedChain(null); }, onCancel: cancelTask })
|
|
654
837
|
);
|
|
655
838
|
}
|
|
656
839
|
|
|
657
|
-
// ─── Agent Task Pipeline (for agent-detail workforce tab) ─
|
|
658
|
-
// Reusable mini version scoped to a single agent
|
|
840
|
+
// ─── Agent Task Pipeline (reusable mini for agent-detail workforce tab) ─
|
|
659
841
|
export function AgentTaskPipeline(props) {
|
|
660
842
|
var agentId = props.agentId;
|
|
661
843
|
var _tasks = useState([]);
|
|
@@ -672,8 +854,6 @@ export function AgentTaskPipeline(props) {
|
|
|
672
854
|
engineCall('/task-pipeline/agent/' + agentId + '?completed=true').then(function(res) {
|
|
673
855
|
setTasks(res?.tasks || []);
|
|
674
856
|
}).catch(function() {}).finally(function() { setLoading(false); });
|
|
675
|
-
|
|
676
|
-
// SSE
|
|
677
857
|
var baseUrl = window.__ENGINE_BASE || '/api/engine';
|
|
678
858
|
var es;
|
|
679
859
|
try {
|
|
@@ -709,57 +889,46 @@ export function AgentTaskPipeline(props) {
|
|
|
709
889
|
var failed = tasks.filter(function(t) { return t.status === 'failed' || t.status === 'cancelled'; });
|
|
710
890
|
|
|
711
891
|
function renderTaskRow(t) {
|
|
712
|
-
var
|
|
713
|
-
var catIcon = CATEGORY_ICONS[t.category] || '\uD83D\uDCCB';
|
|
892
|
+
var sc = STATUS_COLORS[t.status] || '#6b7394';
|
|
714
893
|
return h('div', {
|
|
715
894
|
key: t.id,
|
|
716
895
|
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
|
-
},
|
|
896
|
+
style: { display: 'flex', alignItems: 'center', gap: 8, padding: '8px 10px', borderBottom: '1px solid var(--border)', cursor: 'pointer', transition: 'background 0.15s' },
|
|
722
897
|
onMouseEnter: function(e) { e.currentTarget.style.background = 'var(--bg-secondary)'; },
|
|
723
898
|
onMouseLeave: function(e) { e.currentTarget.style.background = ''; },
|
|
724
899
|
},
|
|
725
|
-
h('
|
|
900
|
+
h('div', { style: { width: 6, height: 6, borderRadius: '50%', background: sc, flexShrink: 0 } }),
|
|
726
901
|
h('div', { style: { flex: 1, minWidth: 0 } },
|
|
727
|
-
h('div', { style: { fontSize:
|
|
728
|
-
h('div', { style: { fontSize:
|
|
902
|
+
h('div', { style: { fontSize: 12, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, t.title),
|
|
903
|
+
h('div', { style: { fontSize: 10, color: 'var(--text-muted)', marginTop: 2, display: 'flex', gap: 4, alignItems: 'center' } },
|
|
904
|
+
t.category,
|
|
905
|
+
t.delegationType && h('span', { style: { color: DELEGATION_COLORS[t.delegationType] || '#6b7394' } }, '\u2192 ' + t.delegationType),
|
|
906
|
+
h('span', null, '\u00B7 ' + timeAgo(t.createdAt))
|
|
907
|
+
)
|
|
729
908
|
),
|
|
730
|
-
t.status === 'in_progress' && t.progress > 0 && h('
|
|
731
|
-
h('span', { style: { padding: '2px
|
|
732
|
-
t.actualDurationMs && h('span', { style: { fontSize:
|
|
909
|
+
t.status === 'in_progress' && t.progress > 0 && h('span', { style: { fontSize: 10, color: sc, fontWeight: 600 } }, t.progress + '%'),
|
|
910
|
+
h('span', { style: { padding: '2px 6px', borderRadius: 8, fontSize: 9, fontWeight: 600, background: sc + '22', color: sc, flexShrink: 0 } }, t.status.replace('_', ' ')),
|
|
911
|
+
t.actualDurationMs && h('span', { style: { fontSize: 10, color: 'var(--text-muted)', flexShrink: 0 } }, formatDuration(t.actualDurationMs))
|
|
733
912
|
);
|
|
734
913
|
}
|
|
735
914
|
|
|
736
915
|
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' } }),
|
|
916
|
+
active.length > 0 && h('div', { style: { marginBottom: 12 } },
|
|
917
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: STATUS_COLORS.in_progress, marginBottom: 6, display: 'flex', alignItems: 'center', gap: 4 } },
|
|
918
|
+
h('div', { style: { width: 6, height: 6, borderRadius: '50%', background: STATUS_COLORS.in_progress, animation: 'flowPulse 2s infinite' } }),
|
|
741
919
|
'Active (' + active.length + ')'
|
|
742
920
|
),
|
|
743
|
-
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } },
|
|
744
|
-
active.map(renderTaskRow)
|
|
745
|
-
)
|
|
921
|
+
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } }, active.map(renderTaskRow))
|
|
746
922
|
),
|
|
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')
|
|
923
|
+
completed.length > 0 && h('div', { style: { marginBottom: 12 } },
|
|
924
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: STATUS_COLORS.completed, marginBottom: 6 } }, 'Completed (' + completed.length + ')'),
|
|
925
|
+
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } }, completed.slice(0, 10).map(renderTaskRow)),
|
|
926
|
+
completed.length > 10 && h('div', { style: { padding: 6, textAlign: 'center', fontSize: 11, color: 'var(--text-muted)' } }, '+ ' + (completed.length - 10) + ' more')
|
|
754
927
|
),
|
|
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
|
-
)
|
|
928
|
+
failed.length > 0 && h('div', { style: { marginBottom: 12 } },
|
|
929
|
+
h('div', { style: { fontSize: 11, fontWeight: 600, color: STATUS_COLORS.failed, marginBottom: 6 } }, 'Failed (' + failed.length + ')'),
|
|
930
|
+
h('div', { style: { border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } }, failed.slice(0, 5).map(renderTaskRow))
|
|
761
931
|
),
|
|
762
|
-
|
|
763
|
-
selectedTask && h(TaskDetail, { task: selectedTask, onClose: function() { setSelectedTask(null); }, onCancel: cancelTask })
|
|
932
|
+
selectedTask && h(TaskDetail, { task: selectedTask, chain: null, onClose: function() { setSelectedTask(null); }, onCancel: cancelTask })
|
|
764
933
|
);
|
|
765
934
|
}
|