claude-team-dashboard 1.2.2
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.
Potentially problematic release.
This version of claude-team-dashboard might be problematic. Click here for more details.
- package/CHANGELOG.md +76 -0
- package/LICENSE +21 -0
- package/README.md +722 -0
- package/cleanup.js +73 -0
- package/config.js +50 -0
- package/dist/assets/icons-Ijf8rQIc.js +1 -0
- package/dist/assets/index-Cqc1m1x_.css +1 -0
- package/dist/assets/index-jGy3ms0W.js +9 -0
- package/dist/assets/react-vendor-DbmSkCAF.js +1 -0
- package/dist/index.html +16 -0
- package/index.html +13 -0
- package/package.json +93 -0
- package/server.js +953 -0
- package/src/App.jsx +372 -0
- package/src/animations-enhanced.css +929 -0
- package/src/animations.css +783 -0
- package/src/components/ActivityFeed.jsx +289 -0
- package/src/components/AgentActivity.jsx +104 -0
- package/src/components/AgentCard.jsx +163 -0
- package/src/components/AgentOutputViewer.jsx +334 -0
- package/src/components/ArchiveViewer.jsx +283 -0
- package/src/components/ConnectionStatus.jsx +124 -0
- package/src/components/DetailedTaskProgress.jsx +126 -0
- package/src/components/ErrorBoundary.jsx +132 -0
- package/src/components/Header.jsx +154 -0
- package/src/components/LiveAgentStream.jsx +176 -0
- package/src/components/LiveCommunication.jsx +326 -0
- package/src/components/LiveMetrics.jsx +100 -0
- package/src/components/RealTimeMessages.jsx +298 -0
- package/src/components/SkeletonLoader.jsx +384 -0
- package/src/components/StatsOverview.jsx +209 -0
- package/src/components/SystemStatus.jsx +57 -0
- package/src/components/TaskList.jsx +306 -0
- package/src/components/TeamCard.jsx +126 -0
- package/src/components/TeamHistory.jsx +204 -0
- package/src/components/__tests__/ConnectionStatus.test.jsx +54 -0
- package/src/components/__tests__/StatsOverview.test.jsx +66 -0
- package/src/config/constants.js +59 -0
- package/src/hooks/useCounterAnimation.js +219 -0
- package/src/hooks/useWebSocket.js +76 -0
- package/src/index.css +1818 -0
- package/src/main.jsx +17 -0
- package/src/polish-enhancements.css +303 -0
- package/src/premium-visual-polish.css +830 -0
- package/src/responsive-enhancements.css +666 -0
- package/src/styles/theme.css +395 -0
- package/src/test/setup.js +19 -0
- package/start.js +36 -0
- package/vite.config.js +37 -0
package/src/App.jsx
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
2
|
+
import { Activity, Github, ExternalLink, BarChart3, MessageSquare, Users, Settings, History as HistoryIcon, Archive } from 'lucide-react';
|
|
3
|
+
import { useWebSocket } from './hooks/useWebSocket';
|
|
4
|
+
import { Header } from './components/Header';
|
|
5
|
+
import { StatsOverview } from './components/StatsOverview';
|
|
6
|
+
import { TeamCard } from './components/TeamCard';
|
|
7
|
+
import { ActivityFeed } from './components/ActivityFeed';
|
|
8
|
+
import { LiveMetrics } from './components/LiveMetrics';
|
|
9
|
+
import { SystemStatus } from './components/SystemStatus';
|
|
10
|
+
import { DetailedTaskProgress } from './components/DetailedTaskProgress';
|
|
11
|
+
import { AgentActivity } from './components/AgentActivity';
|
|
12
|
+
import { RealTimeMessages } from './components/RealTimeMessages';
|
|
13
|
+
import { LiveAgentStream } from './components/LiveAgentStream';
|
|
14
|
+
import { TeamHistory } from './components/TeamHistory';
|
|
15
|
+
import { AgentOutputViewer } from './components/AgentOutputViewer';
|
|
16
|
+
import { ArchiveViewer } from './components/ArchiveViewer';
|
|
17
|
+
|
|
18
|
+
function App() {
|
|
19
|
+
const [teams, setTeams] = useState([]);
|
|
20
|
+
const [stats, setStats] = useState(null);
|
|
21
|
+
const [lastUpdate, setLastUpdate] = useState(null);
|
|
22
|
+
const [activeTab, setActiveTab] = useState('overview');
|
|
23
|
+
const [teamHistory, setTeamHistory] = useState([]);
|
|
24
|
+
const [agentOutputs, setAgentOutputs] = useState([]);
|
|
25
|
+
|
|
26
|
+
const wsUrl = `ws://${window.location.hostname}:3001`;
|
|
27
|
+
// lgtm[js/invocation-of-non-function] useWebSocket is a valid React hook
|
|
28
|
+
const { data, isConnected, error } = useWebSocket(wsUrl);
|
|
29
|
+
|
|
30
|
+
// Memoize expensive computations
|
|
31
|
+
const allTasks = useMemo(
|
|
32
|
+
() => teams.flatMap(t => t.tasks || []),
|
|
33
|
+
[teams]
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (data) {
|
|
38
|
+
setLastUpdate(data);
|
|
39
|
+
|
|
40
|
+
if (data.data) {
|
|
41
|
+
setTeams(data.data);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (data.stats) {
|
|
45
|
+
setStats(data.stats);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (data.teamHistory) {
|
|
49
|
+
setTeamHistory(data.teamHistory);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (data.agentOutputs) {
|
|
53
|
+
setAgentOutputs(data.agentOutputs);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (data.outputs) {
|
|
57
|
+
setAgentOutputs(data.outputs);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}, [data]);
|
|
61
|
+
|
|
62
|
+
// Keyboard navigation handler for tabs
|
|
63
|
+
const handleTabKeyDown = (e) => {
|
|
64
|
+
const tabs = ['overview', 'teams', 'communication', 'monitoring', 'history', 'archive'];
|
|
65
|
+
const currentIndex = tabs.indexOf(activeTab);
|
|
66
|
+
|
|
67
|
+
if (e.key === 'ArrowRight') {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
const nextIndex = (currentIndex + 1) % tabs.length;
|
|
70
|
+
setActiveTab(tabs[nextIndex]);
|
|
71
|
+
// Focus the new tab button
|
|
72
|
+
document.getElementById(`tab-${tabs[nextIndex]}`)?.focus();
|
|
73
|
+
} else if (e.key === 'ArrowLeft') {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
const prevIndex = (currentIndex - 1 + tabs.length) % tabs.length;
|
|
76
|
+
setActiveTab(tabs[prevIndex]);
|
|
77
|
+
document.getElementById(`tab-${tabs[prevIndex]}`)?.focus();
|
|
78
|
+
} else if (e.key === 'Home') {
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
setActiveTab(tabs[0]);
|
|
81
|
+
document.getElementById(`tab-${tabs[0]}`)?.focus();
|
|
82
|
+
} else if (e.key === 'End') {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
setActiveTab(tabs[tabs.length - 1]);
|
|
85
|
+
document.getElementById(`tab-${tabs[tabs.length - 1]}`)?.focus();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
|
|
91
|
+
{/* Skip Navigation Link */}
|
|
92
|
+
<a
|
|
93
|
+
href="#main-content"
|
|
94
|
+
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-claude-orange focus:text-white focus:rounded-lg"
|
|
95
|
+
>
|
|
96
|
+
Skip to main content
|
|
97
|
+
</a>
|
|
98
|
+
|
|
99
|
+
{/* Header - New Glassmorphism Design */}
|
|
100
|
+
<Header isConnected={isConnected} error={error} />
|
|
101
|
+
|
|
102
|
+
{/* Main Content */}
|
|
103
|
+
<main id="main-content" className="container mx-auto px-6 py-6" role="main">
|
|
104
|
+
{/* Statistics Overview - Always Visible */}
|
|
105
|
+
<div className="mb-6">
|
|
106
|
+
<StatsOverview stats={stats} />
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
{/* Navigation Tabs */}
|
|
110
|
+
<nav className="mb-6" role="tablist" aria-label="Dashboard sections">
|
|
111
|
+
<div className="flex gap-2 overflow-x-auto pb-2">
|
|
112
|
+
<button
|
|
113
|
+
id="tab-overview"
|
|
114
|
+
onClick={() => setActiveTab('overview')}
|
|
115
|
+
onKeyDown={handleTabKeyDown}
|
|
116
|
+
role="tab"
|
|
117
|
+
aria-selected={activeTab === 'overview'}
|
|
118
|
+
aria-controls="tab-panel-overview"
|
|
119
|
+
className={`flex items-center gap-2 px-4 py-2 rounded-lg font-medium transition-all whitespace-nowrap ${
|
|
120
|
+
activeTab === 'overview'
|
|
121
|
+
? 'bg-claude-orange text-white shadow-lg'
|
|
122
|
+
: 'bg-gray-700/50 text-gray-300 hover:bg-gray-700'
|
|
123
|
+
}`}
|
|
124
|
+
>
|
|
125
|
+
<BarChart3 className="h-4 w-4" aria-hidden="true" />
|
|
126
|
+
Live Metrics
|
|
127
|
+
</button>
|
|
128
|
+
<button
|
|
129
|
+
id="tab-teams"
|
|
130
|
+
onClick={() => setActiveTab('teams')}
|
|
131
|
+
onKeyDown={handleTabKeyDown}
|
|
132
|
+
role="tab"
|
|
133
|
+
aria-selected={activeTab === 'teams'}
|
|
134
|
+
aria-controls="tab-panel-teams"
|
|
135
|
+
className={`flex items-center gap-2 px-4 py-2 rounded-lg font-medium transition-all whitespace-nowrap ${
|
|
136
|
+
activeTab === 'teams'
|
|
137
|
+
? 'bg-claude-orange text-white shadow-lg'
|
|
138
|
+
: 'bg-gray-700/50 text-gray-300 hover:bg-gray-700'
|
|
139
|
+
}`}
|
|
140
|
+
>
|
|
141
|
+
<Users className="h-4 w-4" aria-hidden="true" />
|
|
142
|
+
Teams & Tasks
|
|
143
|
+
</button>
|
|
144
|
+
<button
|
|
145
|
+
id="tab-communication"
|
|
146
|
+
onClick={() => setActiveTab('communication')}
|
|
147
|
+
onKeyDown={handleTabKeyDown}
|
|
148
|
+
role="tab"
|
|
149
|
+
aria-selected={activeTab === 'communication'}
|
|
150
|
+
aria-controls="tab-panel-communication"
|
|
151
|
+
className={`flex items-center gap-2 px-4 py-2 rounded-lg font-medium transition-all whitespace-nowrap ${
|
|
152
|
+
activeTab === 'communication'
|
|
153
|
+
? 'bg-claude-orange text-white shadow-lg'
|
|
154
|
+
: 'bg-gray-700/50 text-gray-300 hover:bg-gray-700'
|
|
155
|
+
}`}
|
|
156
|
+
>
|
|
157
|
+
<MessageSquare className="h-4 w-4" aria-hidden="true" />
|
|
158
|
+
Communication
|
|
159
|
+
</button>
|
|
160
|
+
<button
|
|
161
|
+
id="tab-monitoring"
|
|
162
|
+
onClick={() => setActiveTab('monitoring')}
|
|
163
|
+
onKeyDown={handleTabKeyDown}
|
|
164
|
+
role="tab"
|
|
165
|
+
aria-selected={activeTab === 'monitoring'}
|
|
166
|
+
aria-controls="tab-panel-monitoring"
|
|
167
|
+
className={`flex items-center gap-2 px-4 py-2 rounded-lg font-medium transition-all whitespace-nowrap ${
|
|
168
|
+
activeTab === 'monitoring'
|
|
169
|
+
? 'bg-claude-orange text-white shadow-lg'
|
|
170
|
+
: 'bg-gray-700/50 text-gray-300 hover:bg-gray-700'
|
|
171
|
+
}`}
|
|
172
|
+
>
|
|
173
|
+
<Settings className="h-4 w-4" aria-hidden="true" />
|
|
174
|
+
Monitoring
|
|
175
|
+
</button>
|
|
176
|
+
<button
|
|
177
|
+
id="tab-history"
|
|
178
|
+
onClick={() => setActiveTab('history')}
|
|
179
|
+
onKeyDown={handleTabKeyDown}
|
|
180
|
+
role="tab"
|
|
181
|
+
aria-selected={activeTab === 'history'}
|
|
182
|
+
aria-controls="tab-panel-history"
|
|
183
|
+
className={`flex items-center gap-2 px-4 py-2 rounded-lg font-medium transition-all whitespace-nowrap ${
|
|
184
|
+
activeTab === 'history'
|
|
185
|
+
? 'bg-claude-orange text-white shadow-lg'
|
|
186
|
+
: 'bg-gray-700/50 text-gray-300 hover:bg-gray-700'
|
|
187
|
+
}`}
|
|
188
|
+
>
|
|
189
|
+
<HistoryIcon className="h-4 w-4" aria-hidden="true" />
|
|
190
|
+
History & Outputs
|
|
191
|
+
</button>
|
|
192
|
+
<button
|
|
193
|
+
id="tab-archive"
|
|
194
|
+
onClick={() => setActiveTab('archive')}
|
|
195
|
+
onKeyDown={handleTabKeyDown}
|
|
196
|
+
role="tab"
|
|
197
|
+
aria-selected={activeTab === 'archive'}
|
|
198
|
+
aria-controls="tab-panel-archive"
|
|
199
|
+
className={`flex items-center gap-2 px-4 py-2 rounded-lg font-medium transition-all whitespace-nowrap ${
|
|
200
|
+
activeTab === 'archive'
|
|
201
|
+
? 'bg-claude-orange text-white shadow-lg'
|
|
202
|
+
: 'bg-gray-700/50 text-gray-300 hover:bg-gray-700'
|
|
203
|
+
}`}
|
|
204
|
+
>
|
|
205
|
+
<Archive className="h-4 w-4" aria-hidden="true" />
|
|
206
|
+
Archive
|
|
207
|
+
</button>
|
|
208
|
+
</div>
|
|
209
|
+
</nav>
|
|
210
|
+
|
|
211
|
+
{/* Tab Content */}
|
|
212
|
+
<div className="tab-content">
|
|
213
|
+
{activeTab === 'overview' && (
|
|
214
|
+
<div
|
|
215
|
+
role="tabpanel"
|
|
216
|
+
id="tab-panel-overview"
|
|
217
|
+
aria-labelledby="tab-overview"
|
|
218
|
+
className="space-y-6 animate-fadeIn"
|
|
219
|
+
>
|
|
220
|
+
{/* Live Metrics */}
|
|
221
|
+
<LiveMetrics stats={stats} />
|
|
222
|
+
|
|
223
|
+
{/* Quick Stats Grid */}
|
|
224
|
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
225
|
+
<DetailedTaskProgress tasks={allTasks} />
|
|
226
|
+
<AgentActivity teams={teams} />
|
|
227
|
+
<SystemStatus isConnected={isConnected} lastUpdate={lastUpdate} />
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
)}
|
|
231
|
+
|
|
232
|
+
{activeTab === 'teams' && (
|
|
233
|
+
<div
|
|
234
|
+
role="tabpanel"
|
|
235
|
+
id="tab-panel-teams"
|
|
236
|
+
aria-labelledby="tab-teams"
|
|
237
|
+
className="grid grid-cols-1 lg:grid-cols-3 gap-6 animate-fadeIn"
|
|
238
|
+
>
|
|
239
|
+
{/* Teams Section */}
|
|
240
|
+
<div className="lg:col-span-2 space-y-6">
|
|
241
|
+
<div className="flex items-center justify-between mb-4">
|
|
242
|
+
<h2 className="text-xl font-bold text-white">Active Teams</h2>
|
|
243
|
+
{teams.length > 0 && (
|
|
244
|
+
<span className="text-sm text-gray-400">
|
|
245
|
+
{teams.length} team{teams.length !== 1 ? 's' : ''}
|
|
246
|
+
</span>
|
|
247
|
+
)}
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
{teams.length === 0 ? (
|
|
251
|
+
<div className="card text-center py-12">
|
|
252
|
+
<Activity className="h-16 w-16 text-gray-600 mx-auto mb-4" />
|
|
253
|
+
<h3 className="text-xl font-semibold text-white mb-2">
|
|
254
|
+
No Active Teams
|
|
255
|
+
</h3>
|
|
256
|
+
<p className="text-gray-400 mb-4">
|
|
257
|
+
Start a Claude Code agent team to see it appear here
|
|
258
|
+
</p>
|
|
259
|
+
<a
|
|
260
|
+
href="https://code.claude.com/docs/en/agent-teams#start-your-first-agent-team"
|
|
261
|
+
target="_blank"
|
|
262
|
+
rel="noopener noreferrer"
|
|
263
|
+
className="inline-flex items-center gap-2 bg-claude-orange hover:bg-orange-600 text-white px-6 py-3 rounded-lg font-medium transition-colors"
|
|
264
|
+
>
|
|
265
|
+
<ExternalLink className="h-4 w-4" />
|
|
266
|
+
Learn How to Start a Team
|
|
267
|
+
</a>
|
|
268
|
+
</div>
|
|
269
|
+
) : (
|
|
270
|
+
teams.map((team, index) => (
|
|
271
|
+
<TeamCard key={team.name || index} team={team} />
|
|
272
|
+
))
|
|
273
|
+
)}
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
{/* Activity Feed Section */}
|
|
277
|
+
<div className="lg:col-span-1">
|
|
278
|
+
<ActivityFeed updates={lastUpdate} />
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
)}
|
|
282
|
+
|
|
283
|
+
{activeTab === 'communication' && (
|
|
284
|
+
<div
|
|
285
|
+
role="tabpanel"
|
|
286
|
+
id="tab-panel-communication"
|
|
287
|
+
aria-labelledby="tab-communication"
|
|
288
|
+
className="grid grid-cols-1 lg:grid-cols-2 gap-6 animate-fadeIn"
|
|
289
|
+
>
|
|
290
|
+
<RealTimeMessages teams={teams} />
|
|
291
|
+
<LiveAgentStream teams={teams} />
|
|
292
|
+
</div>
|
|
293
|
+
)}
|
|
294
|
+
|
|
295
|
+
{activeTab === 'monitoring' && (
|
|
296
|
+
<div
|
|
297
|
+
role="tabpanel"
|
|
298
|
+
id="tab-panel-monitoring"
|
|
299
|
+
aria-labelledby="tab-monitoring"
|
|
300
|
+
className="grid grid-cols-1 lg:grid-cols-2 gap-6 animate-fadeIn"
|
|
301
|
+
>
|
|
302
|
+
<SystemStatus isConnected={isConnected} lastUpdate={lastUpdate} />
|
|
303
|
+
<ActivityFeed updates={lastUpdate} />
|
|
304
|
+
</div>
|
|
305
|
+
)}
|
|
306
|
+
|
|
307
|
+
{activeTab === 'history' && (
|
|
308
|
+
<div
|
|
309
|
+
role="tabpanel"
|
|
310
|
+
id="tab-panel-history"
|
|
311
|
+
aria-labelledby="tab-history"
|
|
312
|
+
className="space-y-6 animate-fadeIn"
|
|
313
|
+
>
|
|
314
|
+
{/* Agent Output Viewer - Full Width */}
|
|
315
|
+
<AgentOutputViewer agentOutputs={agentOutputs} />
|
|
316
|
+
|
|
317
|
+
{/* Team History */}
|
|
318
|
+
<TeamHistory teamHistory={teamHistory} />
|
|
319
|
+
</div>
|
|
320
|
+
)}
|
|
321
|
+
|
|
322
|
+
{activeTab === 'archive' && (
|
|
323
|
+
<div
|
|
324
|
+
role="tabpanel"
|
|
325
|
+
id="tab-panel-archive"
|
|
326
|
+
aria-labelledby="tab-archive"
|
|
327
|
+
className="animate-fadeIn"
|
|
328
|
+
>
|
|
329
|
+
{/* Archive Viewer - Full Width */}
|
|
330
|
+
<ArchiveViewer />
|
|
331
|
+
</div>
|
|
332
|
+
)}
|
|
333
|
+
</div>
|
|
334
|
+
</main>
|
|
335
|
+
|
|
336
|
+
{/* Footer */}
|
|
337
|
+
<footer className="bg-gray-800/50 border-t border-gray-700 mt-12">
|
|
338
|
+
<div className="container mx-auto px-6 py-6">
|
|
339
|
+
<div className="flex items-center justify-between text-sm text-gray-400">
|
|
340
|
+
<div className="flex items-center gap-2">
|
|
341
|
+
<Activity className="h-4 w-4 text-claude-orange" />
|
|
342
|
+
<span>Claude Code Agent Dashboard</span>
|
|
343
|
+
<span className="text-gray-600">•</span>
|
|
344
|
+
<span className="text-gray-500">Built by <a href="https://mahipal.engineer" target="_blank" rel="noopener noreferrer" className="text-claude-orange hover:text-orange-400 transition-colors">mahipal.engineer</a></span>
|
|
345
|
+
</div>
|
|
346
|
+
<div className="flex items-center gap-4">
|
|
347
|
+
<a
|
|
348
|
+
href="https://github.com/anthropics/claude-code"
|
|
349
|
+
target="_blank"
|
|
350
|
+
rel="noopener noreferrer"
|
|
351
|
+
className="flex items-center gap-2 hover:text-white transition-colors"
|
|
352
|
+
>
|
|
353
|
+
<Github className="h-4 w-4" />
|
|
354
|
+
<span>GitHub</span>
|
|
355
|
+
</a>
|
|
356
|
+
<a
|
|
357
|
+
href="https://code.claude.com"
|
|
358
|
+
target="_blank"
|
|
359
|
+
rel="noopener noreferrer"
|
|
360
|
+
className="hover:text-white transition-colors"
|
|
361
|
+
>
|
|
362
|
+
Documentation
|
|
363
|
+
</a>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
</footer>
|
|
368
|
+
</div>
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export default App;
|