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
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { WifiOff, RefreshCw, Zap } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
export function ConnectionStatus({ isConnected, error }) {
|
|
6
|
+
if (isConnected) {
|
|
7
|
+
return (
|
|
8
|
+
<div
|
|
9
|
+
className="inline-flex items-center gap-2.5 px-4 py-2 rounded-xl font-semibold text-sm transition-all duration-300 group"
|
|
10
|
+
role="status"
|
|
11
|
+
aria-live="polite"
|
|
12
|
+
aria-label="Connection status: Connected"
|
|
13
|
+
style={{
|
|
14
|
+
background: 'linear-gradient(135deg, rgba(34, 197, 94, 0.25) 0%, rgba(21, 128, 61, 0.15) 100%)',
|
|
15
|
+
color: '#4ade80',
|
|
16
|
+
border: '1px solid rgba(34, 197, 94, 0.4)',
|
|
17
|
+
boxShadow: '0 4px 12px rgba(34, 197, 94, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)',
|
|
18
|
+
textShadow: '0 0 10px rgba(34, 197, 94, 0.4)'
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
{/* Animated Pulse Indicator */}
|
|
22
|
+
<div className="relative">
|
|
23
|
+
<Zap
|
|
24
|
+
className="h-4 w-4 group-hover:scale-110 transition-transform duration-300"
|
|
25
|
+
style={{
|
|
26
|
+
filter: 'drop-shadow(0 0 6px rgba(34, 197, 94, 0.6))'
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
<div
|
|
30
|
+
className="absolute inset-0 rounded-full animate-ping"
|
|
31
|
+
style={{
|
|
32
|
+
background: 'rgba(34, 197, 94, 0.4)'
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<span className="tracking-wide">Connected</span>
|
|
38
|
+
|
|
39
|
+
{/* Live Indicator Dot */}
|
|
40
|
+
<div
|
|
41
|
+
className="h-2 w-2 rounded-full animate-pulse"
|
|
42
|
+
style={{
|
|
43
|
+
background: '#4ade80',
|
|
44
|
+
boxShadow: '0 0 8px rgba(34, 197, 94, 0.8), 0 0 16px rgba(34, 197, 94, 0.4)'
|
|
45
|
+
}}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (error) {
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
className="inline-flex items-center gap-2.5 px-4 py-2 rounded-xl font-semibold text-sm transition-all duration-300"
|
|
55
|
+
role="alert"
|
|
56
|
+
aria-live="assertive"
|
|
57
|
+
aria-label={`Connection error: ${error}`}
|
|
58
|
+
style={{
|
|
59
|
+
background: 'linear-gradient(135deg, rgba(239, 68, 68, 0.25) 0%, rgba(220, 38, 38, 0.15) 100%)',
|
|
60
|
+
color: '#f87171',
|
|
61
|
+
border: '1px solid rgba(239, 68, 68, 0.4)',
|
|
62
|
+
boxShadow: '0 4px 12px rgba(239, 68, 68, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)',
|
|
63
|
+
textShadow: '0 0 10px rgba(239, 68, 68, 0.4)',
|
|
64
|
+
animation: 'shake 0.5s ease-in-out'
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
<WifiOff
|
|
68
|
+
className="h-4 w-4"
|
|
69
|
+
style={{
|
|
70
|
+
filter: 'drop-shadow(0 0 6px rgba(239, 68, 68, 0.6))'
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
<span className="tracking-wide truncate max-w-xs">{error}</span>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div
|
|
80
|
+
className="inline-flex items-center gap-2.5 px-4 py-2 rounded-xl font-semibold text-sm transition-all duration-300"
|
|
81
|
+
role="status"
|
|
82
|
+
aria-live="polite"
|
|
83
|
+
aria-label="Connection status: Connecting"
|
|
84
|
+
aria-busy="true"
|
|
85
|
+
style={{
|
|
86
|
+
background: 'linear-gradient(135deg, rgba(234, 179, 8, 0.25) 0%, rgba(202, 138, 4, 0.15) 100%)',
|
|
87
|
+
color: '#facc15',
|
|
88
|
+
border: '1px solid rgba(234, 179, 8, 0.4)',
|
|
89
|
+
boxShadow: '0 4px 12px rgba(234, 179, 8, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)',
|
|
90
|
+
textShadow: '0 0 10px rgba(234, 179, 8, 0.4)'
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<RefreshCw
|
|
94
|
+
className="h-4 w-4 animate-spin"
|
|
95
|
+
style={{
|
|
96
|
+
filter: 'drop-shadow(0 0 6px rgba(234, 179, 8, 0.6))',
|
|
97
|
+
animationDuration: '2s'
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
<span className="tracking-wide">Connecting...</span>
|
|
101
|
+
|
|
102
|
+
{/* Pulsing Dots */}
|
|
103
|
+
<div className="flex gap-1">
|
|
104
|
+
{[0, 1, 2].map((i) => (
|
|
105
|
+
<div
|
|
106
|
+
key={i}
|
|
107
|
+
className="h-1.5 w-1.5 rounded-full"
|
|
108
|
+
style={{
|
|
109
|
+
background: '#facc15',
|
|
110
|
+
animation: 'pulse 1.4s ease-in-out infinite',
|
|
111
|
+
animationDelay: `${i * 0.2}s`,
|
|
112
|
+
boxShadow: '0 0 6px rgba(234, 179, 8, 0.6)'
|
|
113
|
+
}}
|
|
114
|
+
/>
|
|
115
|
+
))}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
ConnectionStatus.propTypes = {
|
|
122
|
+
isConnected: PropTypes.bool.isRequired,
|
|
123
|
+
error: PropTypes.string
|
|
124
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CheckCircle, Clock, AlertCircle, TrendingUp } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
export function DetailedTaskProgress({ tasks }) {
|
|
5
|
+
if (!tasks || tasks.length === 0) return null;
|
|
6
|
+
|
|
7
|
+
const pendingTasks = tasks.filter(t => t.status === 'pending');
|
|
8
|
+
const inProgressTasks = tasks.filter(t => t.status === 'in_progress');
|
|
9
|
+
const completedTasks = tasks.filter(t => t.status === 'completed');
|
|
10
|
+
const blockedTasks = tasks.filter(t => t.blockedBy && t.blockedBy.length > 0);
|
|
11
|
+
|
|
12
|
+
const completionPercentage = tasks.length > 0
|
|
13
|
+
? Math.round((completedTasks.length / tasks.length) * 100)
|
|
14
|
+
: 0;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="card">
|
|
18
|
+
<div className="flex items-center justify-between mb-4">
|
|
19
|
+
<div className="flex items-center gap-2">
|
|
20
|
+
<TrendingUp className="h-5 w-5 text-claude-orange" />
|
|
21
|
+
<h3 className="text-lg font-semibold text-white">Task Progress Details</h3>
|
|
22
|
+
</div>
|
|
23
|
+
<span className="text-2xl font-bold text-white">{completionPercentage}%</span>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
{/* Overall Progress Bar */}
|
|
27
|
+
<div className="mb-6">
|
|
28
|
+
<div className="flex items-center justify-between mb-2">
|
|
29
|
+
<span className="text-sm text-gray-400">Overall Completion</span>
|
|
30
|
+
<span className="text-sm text-white font-semibold">
|
|
31
|
+
{completedTasks.length} / {tasks.length} tasks
|
|
32
|
+
</span>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="w-full bg-gray-700 rounded-full h-3 overflow-hidden">
|
|
35
|
+
<div
|
|
36
|
+
className="h-3 rounded-full transition-all duration-500"
|
|
37
|
+
style={{
|
|
38
|
+
width: `${completionPercentage}%`,
|
|
39
|
+
background: 'linear-gradient(90deg, #10b981 0%, #34d399 50%, #6ee7b7 100%)',
|
|
40
|
+
boxShadow: '0 0 10px rgba(16, 185, 129, 0.5)'
|
|
41
|
+
}}
|
|
42
|
+
></div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{/* Detailed Breakdown */}
|
|
47
|
+
<div className="space-y-3">
|
|
48
|
+
{/* Pending */}
|
|
49
|
+
<div className="flex items-center justify-between p-3 rounded-lg bg-yellow-500/10 border border-yellow-500/30">
|
|
50
|
+
<div className="flex items-center gap-3">
|
|
51
|
+
<div className="p-2 rounded-lg bg-yellow-500/20">
|
|
52
|
+
<Clock className="h-4 w-4 text-yellow-400" />
|
|
53
|
+
</div>
|
|
54
|
+
<div>
|
|
55
|
+
<div className="text-sm font-semibold text-white">Pending Tasks</div>
|
|
56
|
+
<div className="text-xs text-gray-400">Waiting to start</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
<div className="text-right">
|
|
60
|
+
<div className="text-xl font-bold text-yellow-400">{pendingTasks.length}</div>
|
|
61
|
+
<div className="text-xs text-gray-400">
|
|
62
|
+
{tasks.length > 0 ? Math.round((pendingTasks.length / tasks.length) * 100) : 0}%
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
{/* In Progress */}
|
|
68
|
+
<div className="flex items-center justify-between p-3 rounded-lg bg-blue-500/10 border border-blue-500/30">
|
|
69
|
+
<div className="flex items-center gap-3">
|
|
70
|
+
<div className="p-2 rounded-lg bg-blue-500/20">
|
|
71
|
+
<div className="h-4 w-4 border-2 border-blue-400 border-t-transparent rounded-full animate-spin"></div>
|
|
72
|
+
</div>
|
|
73
|
+
<div>
|
|
74
|
+
<div className="text-sm font-semibold text-white">In Progress</div>
|
|
75
|
+
<div className="text-xs text-gray-400">Currently working</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
<div className="text-right">
|
|
79
|
+
<div className="text-xl font-bold text-blue-400">{inProgressTasks.length}</div>
|
|
80
|
+
<div className="text-xs text-gray-400">
|
|
81
|
+
{tasks.length > 0 ? Math.round((inProgressTasks.length / tasks.length) * 100) : 0}%
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
{/* Completed */}
|
|
87
|
+
<div className="flex items-center justify-between p-3 rounded-lg bg-green-500/10 border border-green-500/30">
|
|
88
|
+
<div className="flex items-center gap-3">
|
|
89
|
+
<div className="p-2 rounded-lg bg-green-500/20">
|
|
90
|
+
<CheckCircle className="h-4 w-4 text-green-400" />
|
|
91
|
+
</div>
|
|
92
|
+
<div>
|
|
93
|
+
<div className="text-sm font-semibold text-white">Completed</div>
|
|
94
|
+
<div className="text-xs text-gray-400">Successfully finished</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
<div className="text-right">
|
|
98
|
+
<div className="text-xl font-bold text-green-400">{completedTasks.length}</div>
|
|
99
|
+
<div className="text-xs text-gray-400">{completionPercentage}%</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
{/* Blocked */}
|
|
104
|
+
{blockedTasks.length > 0 && (
|
|
105
|
+
<div className="flex items-center justify-between p-3 rounded-lg bg-red-500/10 border border-red-500/30">
|
|
106
|
+
<div className="flex items-center gap-3">
|
|
107
|
+
<div className="p-2 rounded-lg bg-red-500/20">
|
|
108
|
+
<AlertCircle className="h-4 w-4 text-red-400" />
|
|
109
|
+
</div>
|
|
110
|
+
<div>
|
|
111
|
+
<div className="text-sm font-semibold text-white">Blocked</div>
|
|
112
|
+
<div className="text-xs text-gray-400">Waiting on dependencies</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
<div className="text-right">
|
|
116
|
+
<div className="text-xl font-bold text-red-400">{blockedTasks.length}</div>
|
|
117
|
+
<div className="text-xs text-gray-400">
|
|
118
|
+
{tasks.length > 0 ? Math.round((blockedTasks.length / tasks.length) * 100) : 0}%
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Activity, AlertCircle, RefreshCw } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
export class ErrorBoundary extends React.Component {
|
|
5
|
+
constructor(props) {
|
|
6
|
+
super(props);
|
|
7
|
+
this.state = {
|
|
8
|
+
hasError: false,
|
|
9
|
+
error: null,
|
|
10
|
+
errorInfo: null
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static getDerivedStateFromError(error) {
|
|
15
|
+
return { hasError: true };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
componentDidCatch(error, errorInfo) {
|
|
19
|
+
console.error('Error caught by boundary:', error, errorInfo);
|
|
20
|
+
this.setState({
|
|
21
|
+
error,
|
|
22
|
+
errorInfo
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
handleReload = () => {
|
|
27
|
+
window.location.reload();
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
handleReset = () => {
|
|
31
|
+
this.setState({
|
|
32
|
+
hasError: false,
|
|
33
|
+
error: null,
|
|
34
|
+
errorInfo: null
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
render() {
|
|
39
|
+
if (this.state.hasError) {
|
|
40
|
+
return (
|
|
41
|
+
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 flex items-center justify-center p-6">
|
|
42
|
+
<div className="max-w-2xl w-full">
|
|
43
|
+
{/* Error Card */}
|
|
44
|
+
<div className="card p-8 text-center">
|
|
45
|
+
{/* Icon */}
|
|
46
|
+
<div className="mb-6 flex justify-center">
|
|
47
|
+
<div className="relative">
|
|
48
|
+
<Activity className="h-20 w-20 text-claude-orange animate-pulse" />
|
|
49
|
+
<AlertCircle className="h-8 w-8 text-red-400 absolute -top-2 -right-2" />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
{/* Title */}
|
|
54
|
+
<h1 className="text-3xl font-bold text-white mb-3">
|
|
55
|
+
Oops! Something went wrong
|
|
56
|
+
</h1>
|
|
57
|
+
|
|
58
|
+
{/* Description */}
|
|
59
|
+
<p className="text-gray-400 mb-6">
|
|
60
|
+
The dashboard encountered an unexpected error. Don't worry, your data is safe.
|
|
61
|
+
</p>
|
|
62
|
+
|
|
63
|
+
{/* Error Details (Development) */}
|
|
64
|
+
{process.env.NODE_ENV === 'development' && this.state.error && (
|
|
65
|
+
<div className="mb-6 text-left bg-gray-800 rounded-lg p-4 border border-red-500/30">
|
|
66
|
+
<h3 className="text-sm font-semibold text-red-400 mb-2">Error Details:</h3>
|
|
67
|
+
<pre className="text-xs text-gray-300 overflow-x-auto">
|
|
68
|
+
{this.state.error.toString()}
|
|
69
|
+
</pre>
|
|
70
|
+
{this.state.errorInfo && (
|
|
71
|
+
<details className="mt-3">
|
|
72
|
+
<summary className="text-xs text-gray-400 cursor-pointer hover:text-white">
|
|
73
|
+
Component Stack
|
|
74
|
+
</summary>
|
|
75
|
+
<pre className="text-xs text-gray-300 mt-2 overflow-x-auto">
|
|
76
|
+
{this.state.errorInfo.componentStack}
|
|
77
|
+
</pre>
|
|
78
|
+
</details>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
{/* Action Buttons */}
|
|
84
|
+
<div className="flex gap-4 justify-center">
|
|
85
|
+
<button
|
|
86
|
+
onClick={this.handleReset}
|
|
87
|
+
className="flex items-center gap-2 px-6 py-3 bg-gray-700 hover:bg-gray-600 text-white rounded-lg font-medium transition-colors"
|
|
88
|
+
>
|
|
89
|
+
<RefreshCw className="h-4 w-4" />
|
|
90
|
+
Try Again
|
|
91
|
+
</button>
|
|
92
|
+
<button
|
|
93
|
+
onClick={this.handleReload}
|
|
94
|
+
className="flex items-center gap-2 px-6 py-3 bg-claude-orange hover:bg-orange-600 text-white rounded-lg font-medium transition-colors"
|
|
95
|
+
>
|
|
96
|
+
<RefreshCw className="h-4 w-4" />
|
|
97
|
+
Reload Dashboard
|
|
98
|
+
</button>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
{/* Help Text */}
|
|
102
|
+
<p className="text-sm text-gray-500 mt-6">
|
|
103
|
+
If this error persists, please{' '}
|
|
104
|
+
<a
|
|
105
|
+
href="https://github.com/mukul975/agentdashboard/issues"
|
|
106
|
+
target="_blank"
|
|
107
|
+
rel="noopener noreferrer"
|
|
108
|
+
className="text-claude-orange hover:text-orange-400 underline"
|
|
109
|
+
>
|
|
110
|
+
report it on GitHub
|
|
111
|
+
</a>
|
|
112
|
+
</p>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{/* Tips Card */}
|
|
116
|
+
<div className="mt-6 card p-6">
|
|
117
|
+
<h3 className="text-lg font-semibold text-white mb-3">Troubleshooting Tips:</h3>
|
|
118
|
+
<ul className="text-sm text-gray-400 space-y-2">
|
|
119
|
+
<li>• Check if the backend server is running on port 3001</li>
|
|
120
|
+
<li>• Verify that Claude Code agent teams are active</li>
|
|
121
|
+
<li>• Try clearing your browser cache and reloading</li>
|
|
122
|
+
<li>• Check the browser console for additional error details</li>
|
|
123
|
+
</ul>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return this.props.children;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { Activity, ExternalLink, Menu, X } from 'lucide-react';
|
|
4
|
+
import { ConnectionStatus } from './ConnectionStatus';
|
|
5
|
+
|
|
6
|
+
export function Header({ isConnected, error, onMenuToggle, isMenuOpen }) {
|
|
7
|
+
return (
|
|
8
|
+
<header
|
|
9
|
+
className="sticky top-0 z-50 border-b backdrop-blur-xl"
|
|
10
|
+
style={{
|
|
11
|
+
background: 'linear-gradient(135deg, rgba(17, 24, 39, 0.85) 0%, rgba(31, 41, 55, 0.9) 100%)',
|
|
12
|
+
borderColor: 'rgba(249, 115, 22, 0.15)',
|
|
13
|
+
boxShadow: '0 4px 24px rgba(0, 0, 0, 0.3), inset 0 -1px 0 rgba(249, 115, 22, 0.1)'
|
|
14
|
+
}}
|
|
15
|
+
role="banner"
|
|
16
|
+
>
|
|
17
|
+
<div className="container mx-auto px-6 py-4">
|
|
18
|
+
<div className="flex items-center justify-between">
|
|
19
|
+
{/* Logo and Title */}
|
|
20
|
+
<div className="flex items-center gap-4">
|
|
21
|
+
{/* Mobile Menu Button */}
|
|
22
|
+
<button
|
|
23
|
+
onClick={onMenuToggle}
|
|
24
|
+
className="lg:hidden p-2 rounded-lg transition-all duration-200 hover:bg-gray-700/50"
|
|
25
|
+
aria-label={isMenuOpen ? "Close menu" : "Open menu"}
|
|
26
|
+
>
|
|
27
|
+
{isMenuOpen ? (
|
|
28
|
+
<X className="h-6 w-6 text-gray-300" />
|
|
29
|
+
) : (
|
|
30
|
+
<Menu className="h-6 w-6 text-gray-300" />
|
|
31
|
+
)}
|
|
32
|
+
</button>
|
|
33
|
+
|
|
34
|
+
{/* Logo with Gradient Animation */}
|
|
35
|
+
<div
|
|
36
|
+
className="relative p-3 rounded-xl overflow-hidden group"
|
|
37
|
+
style={{
|
|
38
|
+
background: 'linear-gradient(135deg, rgba(249, 115, 22, 0.2) 0%, rgba(251, 146, 60, 0.15) 100%)',
|
|
39
|
+
boxShadow: '0 4px 12px rgba(249, 115, 22, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.1)',
|
|
40
|
+
border: '1px solid rgba(249, 115, 22, 0.3)'
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
{/* Animated Background Overlay */}
|
|
44
|
+
<div
|
|
45
|
+
className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500"
|
|
46
|
+
style={{
|
|
47
|
+
background: 'linear-gradient(135deg, rgba(249, 115, 22, 0.3), rgba(251, 146, 60, 0.25))',
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
<Activity
|
|
52
|
+
className="h-7 w-7 text-claude-orange relative z-10 group-hover:scale-110 transition-transform duration-300"
|
|
53
|
+
style={{
|
|
54
|
+
filter: 'drop-shadow(0 0 8px rgba(249, 115, 22, 0.5))'
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
|
|
58
|
+
{/* Rotating Glow */}
|
|
59
|
+
<div
|
|
60
|
+
className="absolute inset-0 opacity-50"
|
|
61
|
+
style={{
|
|
62
|
+
background: 'radial-gradient(circle at center, rgba(249, 115, 22, 0.4), transparent 70%)',
|
|
63
|
+
animation: 'spin 20s linear infinite'
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
{/* Title and Subtitle */}
|
|
69
|
+
<div>
|
|
70
|
+
<h1
|
|
71
|
+
className="text-2xl font-bold gradient-text"
|
|
72
|
+
style={{
|
|
73
|
+
background: 'linear-gradient(135deg, #fff 0%, #fb923c 100%)',
|
|
74
|
+
WebkitBackgroundClip: 'text',
|
|
75
|
+
WebkitTextFillColor: 'transparent',
|
|
76
|
+
backgroundClip: 'text',
|
|
77
|
+
letterSpacing: '-0.02em'
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
Claude Agent Dashboard
|
|
81
|
+
</h1>
|
|
82
|
+
<p
|
|
83
|
+
className="text-sm mt-0.5"
|
|
84
|
+
style={{
|
|
85
|
+
color: 'rgba(209, 213, 219, 0.8)',
|
|
86
|
+
letterSpacing: '0.01em'
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
Real-time agent team monitoring
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
{/* Right Side Actions */}
|
|
95
|
+
<div className="flex items-center gap-4">
|
|
96
|
+
{/* Connection Status */}
|
|
97
|
+
<ConnectionStatus isConnected={isConnected} error={error} />
|
|
98
|
+
|
|
99
|
+
{/* Documentation Link */}
|
|
100
|
+
<a
|
|
101
|
+
href="https://code.claude.com/docs/en/agent-teams"
|
|
102
|
+
target="_blank"
|
|
103
|
+
rel="noopener noreferrer"
|
|
104
|
+
className="hidden md:flex items-center gap-2 px-4 py-2 rounded-xl font-medium transition-all duration-300 group"
|
|
105
|
+
style={{
|
|
106
|
+
background: 'linear-gradient(135deg, rgba(55, 65, 81, 0.6) 0%, rgba(31, 41, 55, 0.6) 100%)',
|
|
107
|
+
border: '1px solid rgba(75, 85, 99, 0.5)',
|
|
108
|
+
color: '#e5e7eb',
|
|
109
|
+
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.05)'
|
|
110
|
+
}}
|
|
111
|
+
onMouseEnter={(e) => {
|
|
112
|
+
e.currentTarget.style.background = 'linear-gradient(135deg, rgba(249, 115, 22, 0.2) 0%, rgba(251, 146, 60, 0.15) 100%)';
|
|
113
|
+
e.currentTarget.style.borderColor = 'rgba(249, 115, 22, 0.4)';
|
|
114
|
+
e.currentTarget.style.transform = 'translateY(-2px)';
|
|
115
|
+
e.currentTarget.style.boxShadow = '0 4px 12px rgba(249, 115, 22, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)';
|
|
116
|
+
}}
|
|
117
|
+
onMouseLeave={(e) => {
|
|
118
|
+
e.currentTarget.style.background = 'linear-gradient(135deg, rgba(55, 65, 81, 0.6) 0%, rgba(31, 41, 55, 0.6) 100%)';
|
|
119
|
+
e.currentTarget.style.borderColor = 'rgba(75, 85, 99, 0.5)';
|
|
120
|
+
e.currentTarget.style.transform = 'translateY(0)';
|
|
121
|
+
e.currentTarget.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.05)';
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
<ExternalLink className="h-4 w-4 group-hover:rotate-12 transition-transform duration-300" />
|
|
125
|
+
<span className="text-sm">Documentation</span>
|
|
126
|
+
</a>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{/* Animated Bottom Border */}
|
|
132
|
+
<div
|
|
133
|
+
className="absolute bottom-0 left-0 right-0 h-px"
|
|
134
|
+
style={{
|
|
135
|
+
background: 'linear-gradient(90deg, transparent 0%, rgba(249, 115, 22, 0.5) 50%, transparent 100%)',
|
|
136
|
+
backgroundSize: '200% 100%',
|
|
137
|
+
animation: 'gradient-flow 3s ease infinite'
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
140
|
+
</header>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
Header.propTypes = {
|
|
145
|
+
isConnected: PropTypes.bool.isRequired,
|
|
146
|
+
error: PropTypes.string,
|
|
147
|
+
onMenuToggle: PropTypes.func,
|
|
148
|
+
isMenuOpen: PropTypes.bool
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
Header.defaultProps = {
|
|
152
|
+
onMenuToggle: () => {},
|
|
153
|
+
isMenuOpen: false
|
|
154
|
+
};
|