/
/
/
1// Status Display Component
2import {
3 Activity,
4 Clock,
5 MessageSquare,
6 Wifi,
7 WifiOff,
8 AlertCircle,
9 CheckCircle,
10 Loader
11} from 'lucide-react';
12import type { StatusDisplayProps } from '../api/types';
13import { ConnectionStatus } from '../api/types';
14import { formatDuration } from '../api/client';
15
16export function StatusDisplay({
17 status,
18 connectionStatus,
19 lastError,
20}: StatusDisplayProps) {
21 const getConnectionIcon = () => {
22 switch (connectionStatus) {
23 case ConnectionStatus.CONNECTED:
24 return <Wifi size={16} className="text-green-500" />;
25 case ConnectionStatus.CONNECTING:
26 return <Loader size={16} className="text-yellow-500 animate-spin" />;
27 case ConnectionStatus.ERROR:
28 return <WifiOff size={16} className="text-red-500" />;
29 case ConnectionStatus.DISCONNECTED:
30 default:
31 return <WifiOff size={16} className="text-gray-400" />;
32 }
33 };
34
35 const getConnectionStatus = () => {
36 switch (connectionStatus) {
37 case ConnectionStatus.CONNECTED:
38 return { text: 'Connected', color: 'text-green-600' };
39 case ConnectionStatus.CONNECTING:
40 return { text: 'Connecting...', color: 'text-yellow-600' };
41 case ConnectionStatus.ERROR:
42 return { text: 'Connection Error', color: 'text-red-600' };
43 case ConnectionStatus.DISCONNECTED:
44 default:
45 return { text: 'Disconnected', color: 'text-gray-600' };
46 }
47 };
48
49 const connectionInfo = getConnectionStatus();
50
51 return (
52 <div className="bg-white rounded-lg shadow-md p-6">
53 <div className="flex items-center justify-between mb-6">
54 <h2 className="text-xl font-semibold text-gray-800">System Status</h2>
55 <div className="flex items-center space-x-2">
56 {getConnectionIcon()}
57 <span className={`text-sm font-medium ${connectionInfo.color}`}>
58 {connectionInfo.text}
59 </span>
60 </div>
61 </div>
62
63 {/* Recording Status */}
64 <div className="space-y-4">
65 <div className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
66 <div className="flex items-center space-x-3">
67 <div className={`p-2 rounded-full ${
68 status?.is_recording
69 ? 'bg-red-100 text-red-600'
70 : 'bg-gray-100 text-gray-600'
71 }`}>
72 <Activity size={20} />
73 </div>
74 <div>
75 <div className="font-medium text-gray-900">
76 Recording Status
77 </div>
78 <div className={`text-sm ${
79 status?.is_recording ? 'text-red-600' : 'text-gray-500'
80 }`}>
81 {status?.is_recording ? 'Recording Active' : 'Stopped'}
82 </div>
83 </div>
84 </div>
85
86 {status?.is_recording && (
87 <div className="flex items-center space-x-1">
88 <div className="w-2 h-2 bg-red-500 rounded-full animate-pulse"></div>
89 <span className="text-sm font-medium text-red-600">LIVE</span>
90 </div>
91 )}
92 </div>
93
94 {/* Recording Metrics */}
95 <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
96 {/* Duration */}
97 <div className="p-4 bg-blue-50 rounded-lg">
98 <div className="flex items-center space-x-2 mb-2">
99 <Clock size={16} className="text-blue-600" />
100 <span className="text-sm font-medium text-blue-800">Duration</span>
101 </div>
102 <div className="text-2xl font-bold text-blue-900">
103 {formatDuration(status?.recording_duration || 0)}
104 </div>
105 </div>
106
107 {/* Message Count */}
108 <div className="p-4 bg-green-50 rounded-lg">
109 <div className="flex items-center space-x-2 mb-2">
110 <MessageSquare size={16} className="text-green-600" />
111 <span className="text-sm font-medium text-green-800">Messages</span>
112 </div>
113 <div className="text-2xl font-bold text-green-900">
114 {(status?.recorded_messages || 0).toLocaleString()}
115 </div>
116 </div>
117
118 {/* Active Topics */}
119 <div className="p-4 bg-purple-50 rounded-lg">
120 <div className="flex items-center space-x-2 mb-2">
121 <Activity size={16} className="text-purple-600" />
122 <span className="text-sm font-medium text-purple-800">Topics</span>
123 </div>
124 <div className="text-2xl font-bold text-purple-900">
125 {(status?.active_topics || []).length}
126 </div>
127 </div>
128 </div>
129
130 {/* Current Recording Path */}
131 {status?.is_recording && status?.current_bag_path && (
132 <div className="p-4 bg-gray-50 rounded-lg">
133 <div className="text-sm font-medium text-gray-700 mb-1">
134 Current Recording Path
135 </div>
136 <div className="text-sm font-mono text-gray-600 break-all">
137 {status.current_bag_path}
138 </div>
139 </div>
140 )}
141
142 {/* Active Topics List */}
143 {(status?.active_topics || []).length > 0 && (
144 <div className="p-4 bg-gray-50 rounded-lg">
145 <div className="text-sm font-medium text-gray-700 mb-3">
146 Active Topics ({(status?.active_topics || []).length})
147 </div>
148 <div className="space-y-1 max-h-32 overflow-y-auto">
149 {(status?.active_topics || []).map((topic, index) => (
150 <div
151 key={`${topic}-${index}`}
152 className="text-xs font-mono text-gray-600 py-1 px-2 bg-white rounded border"
153 >
154 {topic}
155 </div>
156 ))}
157 </div>
158 </div>
159 )}
160
161 {/* Error Display */}
162 {lastError && (
163 <div className="p-4 bg-red-50 border border-red-200 rounded-lg">
164 <div className="flex items-start space-x-2">
165 <AlertCircle size={16} className="text-red-500 mt-0.5 flex-shrink-0" />
166 <div>
167 <div className="text-sm font-medium text-red-800">Error</div>
168 <div className="text-sm text-red-700 mt-1">{lastError}</div>
169 </div>
170 </div>
171 </div>
172 )}
173
174 {/* Success State */}
175 {!lastError && connectionStatus === ConnectionStatus.CONNECTED && (
176 <div className="p-3 bg-green-50 border border-green-200 rounded-lg">
177 <div className="flex items-center space-x-2">
178 <CheckCircle size={16} className="text-green-500" />
179 <span className="text-sm text-green-700">
180 System operational and ready
181 </span>
182 </div>
183 </div>
184 )}
185 </div>
186 </div>
187 );
188}