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.

Files changed (49) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/LICENSE +21 -0
  3. package/README.md +722 -0
  4. package/cleanup.js +73 -0
  5. package/config.js +50 -0
  6. package/dist/assets/icons-Ijf8rQIc.js +1 -0
  7. package/dist/assets/index-Cqc1m1x_.css +1 -0
  8. package/dist/assets/index-jGy3ms0W.js +9 -0
  9. package/dist/assets/react-vendor-DbmSkCAF.js +1 -0
  10. package/dist/index.html +16 -0
  11. package/index.html +13 -0
  12. package/package.json +93 -0
  13. package/server.js +953 -0
  14. package/src/App.jsx +372 -0
  15. package/src/animations-enhanced.css +929 -0
  16. package/src/animations.css +783 -0
  17. package/src/components/ActivityFeed.jsx +289 -0
  18. package/src/components/AgentActivity.jsx +104 -0
  19. package/src/components/AgentCard.jsx +163 -0
  20. package/src/components/AgentOutputViewer.jsx +334 -0
  21. package/src/components/ArchiveViewer.jsx +283 -0
  22. package/src/components/ConnectionStatus.jsx +124 -0
  23. package/src/components/DetailedTaskProgress.jsx +126 -0
  24. package/src/components/ErrorBoundary.jsx +132 -0
  25. package/src/components/Header.jsx +154 -0
  26. package/src/components/LiveAgentStream.jsx +176 -0
  27. package/src/components/LiveCommunication.jsx +326 -0
  28. package/src/components/LiveMetrics.jsx +100 -0
  29. package/src/components/RealTimeMessages.jsx +298 -0
  30. package/src/components/SkeletonLoader.jsx +384 -0
  31. package/src/components/StatsOverview.jsx +209 -0
  32. package/src/components/SystemStatus.jsx +57 -0
  33. package/src/components/TaskList.jsx +306 -0
  34. package/src/components/TeamCard.jsx +126 -0
  35. package/src/components/TeamHistory.jsx +204 -0
  36. package/src/components/__tests__/ConnectionStatus.test.jsx +54 -0
  37. package/src/components/__tests__/StatsOverview.test.jsx +66 -0
  38. package/src/config/constants.js +59 -0
  39. package/src/hooks/useCounterAnimation.js +219 -0
  40. package/src/hooks/useWebSocket.js +76 -0
  41. package/src/index.css +1818 -0
  42. package/src/main.jsx +17 -0
  43. package/src/polish-enhancements.css +303 -0
  44. package/src/premium-visual-polish.css +830 -0
  45. package/src/responsive-enhancements.css +666 -0
  46. package/src/styles/theme.css +395 -0
  47. package/src/test/setup.js +19 -0
  48. package/start.js +36 -0
  49. package/vite.config.js +37 -0
@@ -0,0 +1,289 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Activity, Circle, Users, ListTodo, CheckCircle2 } from 'lucide-react';
4
+ import dayjs from 'dayjs';
5
+ import relativeTime from 'dayjs/plugin/relativeTime';
6
+ dayjs.extend(relativeTime);
7
+
8
+ export function ActivityFeed({ updates }) {
9
+ const [activities, setActivities] = useState([]);
10
+
11
+ useEffect(() => {
12
+ if (updates) {
13
+ const newActivity = {
14
+ id: Date.now(),
15
+ type: updates.type,
16
+ timestamp: new Date().toISOString(),
17
+ message: getActivityMessage(updates)
18
+ };
19
+
20
+ setActivities(prev => [newActivity, ...prev].slice(0, 50));
21
+ }
22
+ }, [updates]);
23
+
24
+ const getActivityMessage = (update) => {
25
+ switch (update.type) {
26
+ case 'initial_data':
27
+ return `Connected to dashboard - ${update.data?.length || 0} teams loaded`;
28
+ case 'teams_update':
29
+ return 'Team configuration updated';
30
+ case 'task_update':
31
+ return 'Task status changed';
32
+ default:
33
+ return 'System event';
34
+ }
35
+ };
36
+
37
+ const getActivityConfig = (type) => {
38
+ switch (type) {
39
+ case 'initial_data':
40
+ return {
41
+ icon: CheckCircle2,
42
+ color: '#4ade80',
43
+ bgGradient: 'linear-gradient(135deg, rgba(34, 197, 94, 0.25) 0%, rgba(21, 128, 61, 0.15) 100%)',
44
+ glowColor: 'rgba(34, 197, 94, 0.4)',
45
+ borderColor: 'rgba(34, 197, 94, 0.3)'
46
+ };
47
+ case 'teams_update':
48
+ return {
49
+ icon: Users,
50
+ color: '#60a5fa',
51
+ bgGradient: 'linear-gradient(135deg, rgba(59, 130, 246, 0.25) 0%, rgba(37, 99, 235, 0.15) 100%)',
52
+ glowColor: 'rgba(59, 130, 246, 0.4)',
53
+ borderColor: 'rgba(59, 130, 246, 0.3)'
54
+ };
55
+ case 'task_update':
56
+ return {
57
+ icon: ListTodo,
58
+ color: '#c084fc',
59
+ bgGradient: 'linear-gradient(135deg, rgba(168, 85, 247, 0.25) 0%, rgba(147, 51, 234, 0.15) 100%)',
60
+ glowColor: 'rgba(168, 85, 247, 0.4)',
61
+ borderColor: 'rgba(168, 85, 247, 0.3)'
62
+ };
63
+ default:
64
+ return {
65
+ icon: Activity,
66
+ color: '#9ca3af',
67
+ bgGradient: 'rgba(55, 65, 81, 0.3)',
68
+ glowColor: 'rgba(75, 85, 99, 0.3)',
69
+ borderColor: 'rgba(75, 85, 99, 0.3)'
70
+ };
71
+ }
72
+ };
73
+
74
+ return (
75
+ <div
76
+ className="rounded-2xl p-6"
77
+ style={{
78
+ background: 'linear-gradient(135deg, rgba(30, 41, 59, 0.95) 0%, rgba(15, 23, 42, 0.9) 100%)',
79
+ border: '1px solid rgba(249, 115, 22, 0.15)',
80
+ boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.05)',
81
+ backdropFilter: 'blur(16px)'
82
+ }}
83
+ >
84
+ {/* Header */}
85
+ <div className="flex items-center justify-between mb-6">
86
+ <div className="flex items-center gap-3">
87
+ <div
88
+ className="p-2.5 rounded-xl"
89
+ style={{
90
+ background: 'linear-gradient(135deg, rgba(249, 115, 22, 0.25) 0%, rgba(251, 146, 60, 0.15) 100%)',
91
+ boxShadow: '0 4px 12px rgba(249, 115, 22, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)',
92
+ border: '1px solid rgba(249, 115, 22, 0.3)'
93
+ }}
94
+ >
95
+ <Activity
96
+ className="h-5 w-5"
97
+ style={{
98
+ color: '#fb923c',
99
+ filter: 'drop-shadow(0 0 8px rgba(249, 115, 22, 0.5))'
100
+ }}
101
+ />
102
+ </div>
103
+ <h3
104
+ className="text-lg font-bold"
105
+ style={{
106
+ color: '#ffffff',
107
+ letterSpacing: '-0.01em',
108
+ textShadow: '0 1px 2px rgba(0, 0, 0, 0.3)'
109
+ }}
110
+ >
111
+ Activity Feed
112
+ </h3>
113
+ </div>
114
+
115
+ {/* Event Counter */}
116
+ <span
117
+ className="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold"
118
+ style={{
119
+ background: 'rgba(59, 130, 246, 0.15)',
120
+ color: '#93c5fd',
121
+ border: '1px solid rgba(59, 130, 246, 0.3)'
122
+ }}
123
+ >
124
+ {activities.length} events
125
+ </span>
126
+ </div>
127
+
128
+ {/* Timeline Container */}
129
+ <div
130
+ className="max-h-96 overflow-y-auto pr-2"
131
+ style={{
132
+ scrollbarWidth: 'thin'
133
+ }}
134
+ >
135
+ {activities.length === 0 ? (
136
+ <div
137
+ className="text-center py-12 rounded-xl"
138
+ style={{
139
+ background: 'rgba(30, 41, 59, 0.5)',
140
+ border: '1px dashed rgba(156, 163, 175, 0.2)'
141
+ }}
142
+ >
143
+ <Activity className="h-12 w-12 text-gray-600 mx-auto mb-3 opacity-50" />
144
+ <p className="text-gray-400 text-sm">No activity yet</p>
145
+ <p className="text-gray-500 text-xs mt-1">Events will appear here in real-time</p>
146
+ </div>
147
+ ) : (
148
+ <div className="relative">
149
+ {/* Timeline Line */}
150
+ <div
151
+ className="absolute left-5 top-0 bottom-0 w-0.5"
152
+ style={{
153
+ background: 'linear-gradient(180deg, rgba(249, 115, 22, 0.3) 0%, rgba(249, 115, 22, 0.1) 100%)'
154
+ }}
155
+ />
156
+
157
+ <div className="space-y-4">
158
+ {activities.map((activity, index) => {
159
+ const config = getActivityConfig(activity.type);
160
+ const Icon = config.icon;
161
+
162
+ return (
163
+ <div
164
+ key={activity.id}
165
+ className="relative pl-14 group"
166
+ style={{
167
+ animation: 'slideInRight 0.4s cubic-bezier(0.4, 0.0, 0.2, 1)',
168
+ animationDelay: `${index * 60}ms`,
169
+ animationFillMode: 'both'
170
+ }}
171
+ >
172
+ {/* Timeline Node */}
173
+ <div
174
+ className="absolute left-0 top-1 p-2 rounded-xl transition-all duration-300"
175
+ style={{
176
+ background: config.bgGradient,
177
+ border: `2px solid ${config.borderColor}`,
178
+ boxShadow: `0 4px 12px ${config.glowColor}, inset 0 1px 0 rgba(255, 255, 255, 0.1)`,
179
+ zIndex: 10
180
+ }}
181
+ >
182
+ <Icon
183
+ className="h-5 w-5"
184
+ style={{
185
+ color: config.color,
186
+ filter: `drop-shadow(0 0 6px ${config.glowColor})`
187
+ }}
188
+ />
189
+
190
+ {/* Pulsing Ring */}
191
+ {index === 0 && (
192
+ <div
193
+ className="absolute inset-0 rounded-xl animate-ping"
194
+ style={{
195
+ background: config.bgGradient,
196
+ opacity: 0.4
197
+ }}
198
+ />
199
+ )}
200
+ </div>
201
+
202
+ {/* Activity Card */}
203
+ <div
204
+ className="rounded-xl p-4 transition-all duration-300"
205
+ style={{
206
+ background: 'linear-gradient(135deg, rgba(30, 41, 59, 0.6) 0%, rgba(15, 23, 42, 0.5) 100%)',
207
+ border: '1px solid rgba(75, 85, 99, 0.3)',
208
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.05)'
209
+ }}
210
+ onMouseEnter={(e) => {
211
+ e.currentTarget.style.borderColor = config.borderColor;
212
+ e.currentTarget.style.boxShadow = `0 4px 16px ${config.glowColor}, inset 0 1px 0 rgba(255, 255, 255, 0.08)`;
213
+ e.currentTarget.style.transform = 'translateY(-2px)';
214
+ }}
215
+ onMouseLeave={(e) => {
216
+ e.currentTarget.style.borderColor = 'rgba(75, 85, 99, 0.3)';
217
+ e.currentTarget.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.05)';
218
+ e.currentTarget.style.transform = 'translateY(0)';
219
+ }}
220
+ >
221
+ <p
222
+ className="text-sm font-medium mb-1.5"
223
+ style={{
224
+ color: 'rgba(255, 255, 255, 0.95)',
225
+ letterSpacing: '-0.01em'
226
+ }}
227
+ >
228
+ {activity.message}
229
+ </p>
230
+
231
+ <div className="flex items-center gap-2">
232
+ <Circle
233
+ className="h-1.5 w-1.5"
234
+ style={{
235
+ color: config.color,
236
+ fill: config.color
237
+ }}
238
+ />
239
+ <p
240
+ className="text-xs"
241
+ style={{
242
+ color: 'rgba(156, 163, 175, 0.8)'
243
+ }}
244
+ >
245
+ {dayjs(activity.timestamp).fromNow()}
246
+ </p>
247
+ </div>
248
+
249
+ {/* Live indicator for latest activity */}
250
+ {index === 0 && (
251
+ <div className="absolute top-4 right-4">
252
+ <span
253
+ className="inline-flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-semibold"
254
+ style={{
255
+ background: 'rgba(34, 197, 94, 0.15)',
256
+ color: '#4ade80',
257
+ border: '1px solid rgba(34, 197, 94, 0.3)'
258
+ }}
259
+ >
260
+ <span
261
+ className="h-1.5 w-1.5 rounded-full animate-pulse"
262
+ style={{
263
+ background: '#4ade80',
264
+ boxShadow: '0 0 8px rgba(34, 197, 94, 0.6)'
265
+ }}
266
+ />
267
+ LIVE
268
+ </span>
269
+ </div>
270
+ )}
271
+ </div>
272
+ </div>
273
+ );
274
+ })}
275
+ </div>
276
+ </div>
277
+ )}
278
+ </div>
279
+ </div>
280
+ );
281
+ }
282
+
283
+ ActivityFeed.propTypes = {
284
+ updates: PropTypes.shape({
285
+ type: PropTypes.string,
286
+ data: PropTypes.array,
287
+ stats: PropTypes.object
288
+ })
289
+ };
@@ -0,0 +1,104 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Users, Zap, Brain, CheckSquare } from 'lucide-react';
3
+
4
+ export function AgentActivity({ teams }) {
5
+ const [activeAgents, setActiveAgents] = useState([]);
6
+
7
+ useEffect(() => {
8
+ if (teams && teams.length > 0) {
9
+ const agents = [];
10
+ teams.forEach(team => {
11
+ if (team.config && team.config.members) {
12
+ team.config.members.forEach(member => {
13
+ const agentTasks = team.tasks?.filter(t =>
14
+ t.owner === member.name && t.status === 'in_progress'
15
+ ) || [];
16
+
17
+ agents.push({
18
+ name: member.name,
19
+ team: team.name,
20
+ agentType: member.agentType,
21
+ activeTasks: agentTasks.length,
22
+ currentTask: agentTasks[0]?.subject || 'Idle',
23
+ color: getAgentColor(member.name)
24
+ });
25
+ });
26
+ }
27
+ });
28
+ setActiveAgents(agents.slice(0, 10)); // Show top 10
29
+ }
30
+ }, [teams]);
31
+
32
+ const getAgentColor = (name) => {
33
+ const colors = [
34
+ '#60a5fa', '#34d399', '#fbbf24', '#f87171',
35
+ '#c084fc', '#22d3ee', '#fb923c', '#a78bfa'
36
+ ];
37
+ const hash = name.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
38
+ return colors[hash % colors.length];
39
+ };
40
+
41
+ return (
42
+ <div className="card">
43
+ <div className="flex items-center justify-between mb-4">
44
+ <div className="flex items-center gap-2">
45
+ <Users className="h-5 w-5 text-claude-orange" />
46
+ <h3 className="text-lg font-semibold text-white">Agent Activity</h3>
47
+ </div>
48
+ <span className="text-sm text-gray-400">{activeAgents.length} agents</span>
49
+ </div>
50
+
51
+ <div className="space-y-2 max-h-96 overflow-y-auto">
52
+ {activeAgents.length === 0 ? (
53
+ <div className="text-center py-8 text-gray-400">
54
+ <Users className="h-12 w-12 mx-auto mb-2 opacity-50" />
55
+ <p className="text-sm">No active agents</p>
56
+ </div>
57
+ ) : (
58
+ activeAgents.map((agent, index) => (
59
+ <div
60
+ key={`${agent.team}-${agent.name}-${index}`}
61
+ className="p-3 rounded-lg bg-gray-700/50 border border-gray-600 hover:border-claude-orange transition-all"
62
+ >
63
+ <div className="flex items-start gap-3">
64
+ <div
65
+ className="p-2 rounded-lg"
66
+ style={{ backgroundColor: `${agent.color}20` }}
67
+ >
68
+ <Brain className="h-4 w-4" style={{ color: agent.color }} />
69
+ </div>
70
+ <div className="flex-1 min-w-0">
71
+ <div className="flex items-center justify-between mb-1">
72
+ <span className="text-sm font-semibold text-white truncate">
73
+ {agent.name}
74
+ </span>
75
+ {agent.activeTasks > 0 && (
76
+ <span className="flex items-center gap-1 text-xs text-green-400">
77
+ <Zap className="h-3 w-3" />
78
+ Active
79
+ </span>
80
+ )}
81
+ </div>
82
+ <div className="text-xs text-gray-400 mb-1">{agent.agentType}</div>
83
+ <div className="flex items-center gap-1 text-xs text-gray-500">
84
+ <CheckSquare className="h-3 w-3" />
85
+ <span className="truncate">{agent.currentTask}</span>
86
+ </div>
87
+ </div>
88
+ <div className="text-right">
89
+ <div
90
+ className="text-lg font-bold"
91
+ style={{ color: agent.color }}
92
+ >
93
+ {agent.activeTasks}
94
+ </div>
95
+ <div className="text-xs text-gray-400">tasks</div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ ))
100
+ )}
101
+ </div>
102
+ </div>
103
+ );
104
+ }
@@ -0,0 +1,163 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Bot, Crown, Cpu, Zap } from 'lucide-react';
4
+
5
+ export function AgentCard({ agent, isLead }) {
6
+ const [isHovered, setIsHovered] = useState(false);
7
+
8
+ return (
9
+ <div
10
+ className="relative group rounded-2xl p-5 transition-all duration-300"
11
+ style={{
12
+ background: isLead
13
+ ? 'linear-gradient(135deg, rgba(234, 179, 8, 0.12) 0%, rgba(202, 138, 4, 0.08) 100%)'
14
+ : 'linear-gradient(135deg, rgba(59, 130, 246, 0.12) 0%, rgba(37, 99, 235, 0.08) 100%)',
15
+ border: `2px solid ${isLead ? 'rgba(234, 179, 8, 0.3)' : 'rgba(59, 130, 246, 0.25)'}`,
16
+ boxShadow: isHovered
17
+ ? `0 8px 24px ${isLead ? 'rgba(234, 179, 8, 0.25)' : 'rgba(59, 130, 246, 0.2)'}, inset 0 1px 0 rgba(255, 255, 255, 0.1)`
18
+ : '0 4px 12px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.05)',
19
+ transform: isHovered ? 'translateY(-4px) scale(1.02)' : 'translateY(0) scale(1)',
20
+ }}
21
+ onMouseEnter={() => setIsHovered(true)}
22
+ onMouseLeave={() => setIsHovered(false)}
23
+ >
24
+ {/* Gradient Border Animation */}
25
+ <div
26
+ className="absolute inset-0 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"
27
+ style={{
28
+ background: isLead
29
+ ? 'linear-gradient(135deg, rgba(234, 179, 8, 0.2), transparent 50%, rgba(234, 179, 8, 0.1))'
30
+ : 'linear-gradient(135deg, rgba(59, 130, 246, 0.2), transparent 50%, rgba(59, 130, 246, 0.1))',
31
+ pointerEvents: 'none'
32
+ }}
33
+ />
34
+
35
+ <div className="flex items-start justify-between relative z-10">
36
+ <div className="flex items-start gap-4 flex-1">
37
+ {/* Icon Container with Glow */}
38
+ <div
39
+ className="relative p-3 rounded-xl transition-all duration-300"
40
+ style={{
41
+ background: isLead
42
+ ? 'linear-gradient(135deg, rgba(234, 179, 8, 0.25) 0%, rgba(202, 138, 4, 0.15) 100%)'
43
+ : 'linear-gradient(135deg, rgba(59, 130, 246, 0.25) 0%, rgba(37, 99, 235, 0.15) 100%)',
44
+ boxShadow: isHovered
45
+ ? `0 4px 16px ${isLead ? 'rgba(234, 179, 8, 0.4)' : 'rgba(59, 130, 246, 0.35)'}, inset 0 1px 0 rgba(255, 255, 255, 0.15)`
46
+ : `0 2px 8px ${isLead ? 'rgba(234, 179, 8, 0.3)' : 'rgba(59, 130, 246, 0.25)'}, inset 0 1px 0 rgba(255, 255, 255, 0.1)`,
47
+ border: `1px solid ${isLead ? 'rgba(234, 179, 8, 0.4)' : 'rgba(59, 130, 246, 0.35)'}`,
48
+ transform: isHovered ? 'scale(1.1) rotate(-5deg)' : 'scale(1) rotate(0deg)'
49
+ }}
50
+ >
51
+ {isLead ? (
52
+ <Crown
53
+ className="h-6 w-6"
54
+ style={{
55
+ color: '#facc15',
56
+ filter: 'drop-shadow(0 0 8px rgba(234, 179, 8, 0.6))'
57
+ }}
58
+ />
59
+ ) : (
60
+ <Bot
61
+ className="h-6 w-6"
62
+ style={{
63
+ color: '#60a5fa',
64
+ filter: 'drop-shadow(0 0 8px rgba(59, 130, 246, 0.5))'
65
+ }}
66
+ />
67
+ )}
68
+
69
+ {/* Pulsing Glow Effect */}
70
+ {isHovered && (
71
+ <div
72
+ className="absolute inset-0 rounded-xl animate-pulse"
73
+ style={{
74
+ background: isLead
75
+ ? 'radial-gradient(circle, rgba(234, 179, 8, 0.3), transparent 70%)'
76
+ : 'radial-gradient(circle, rgba(59, 130, 246, 0.3), transparent 70%)',
77
+ zIndex: -1
78
+ }}
79
+ />
80
+ )}
81
+ </div>
82
+
83
+ {/* Agent Details */}
84
+ <div className="flex-1 min-w-0">
85
+ <div className="flex items-center gap-2 mb-2">
86
+ <h5
87
+ className="text-white font-bold text-lg truncate"
88
+ style={{
89
+ letterSpacing: '-0.01em',
90
+ textShadow: '0 1px 2px rgba(0, 0, 0, 0.3)'
91
+ }}
92
+ >
93
+ {agent.name}
94
+ </h5>
95
+
96
+ {isLead && (
97
+ <span
98
+ className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold uppercase tracking-wider"
99
+ style={{
100
+ background: 'linear-gradient(135deg, rgba(234, 179, 8, 0.3) 0%, rgba(202, 138, 4, 0.2) 100%)',
101
+ color: '#facc15',
102
+ border: '1px solid rgba(234, 179, 8, 0.5)',
103
+ boxShadow: '0 2px 8px rgba(234, 179, 8, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15)',
104
+ textShadow: '0 0 10px rgba(234, 179, 8, 0.4)'
105
+ }}
106
+ >
107
+ <Zap className="h-3 w-3" />
108
+ Lead
109
+ </span>
110
+ )}
111
+ </div>
112
+
113
+ {agent.agentType && (
114
+ <div
115
+ className="flex items-center gap-2 mb-2 px-2.5 py-1 rounded-lg inline-flex"
116
+ style={{
117
+ background: 'rgba(55, 65, 81, 0.4)',
118
+ border: '1px solid rgba(75, 85, 99, 0.3)'
119
+ }}
120
+ >
121
+ <Cpu className="h-4 w-4 text-gray-400" />
122
+ <span className="text-sm text-gray-300 truncate font-medium">
123
+ {agent.agentType}
124
+ </span>
125
+ </div>
126
+ )}
127
+
128
+ <p
129
+ className="text-xs font-mono truncate"
130
+ style={{
131
+ color: 'rgba(156, 163, 175, 0.8)'
132
+ }}
133
+ title={agent.agentId}
134
+ >
135
+ ID: {agent.agentId?.substring(0, 12)}...
136
+ </p>
137
+ </div>
138
+ </div>
139
+ </div>
140
+
141
+ {/* Shine Effect on Hover */}
142
+ {isHovered && (
143
+ <div
144
+ className="absolute inset-0 rounded-2xl pointer-events-none"
145
+ style={{
146
+ background: 'linear-gradient(135deg, transparent 0%, rgba(255, 255, 255, 0.05) 50%, transparent 100%)',
147
+ animation: 'shimmer 2s ease-in-out infinite'
148
+ }}
149
+ />
150
+ )}
151
+ </div>
152
+ );
153
+ }
154
+
155
+ AgentCard.propTypes = {
156
+ agent: PropTypes.shape({
157
+ name: PropTypes.string.isRequired,
158
+ agentId: PropTypes.string.isRequired,
159
+ agentType: PropTypes.string,
160
+ model: PropTypes.string
161
+ }).isRequired,
162
+ isLead: PropTypes.bool
163
+ };