EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
TabEvents.jsx
Go to the documentation of this file.
1import React from "react";
2import { apiFetch } from "../../lib/api";
3import MeshCentralEmbed, { VIEWMODE } from "../../components/MeshCentralEmbed";
4
5export default function TabEvents({ agentId, agentUuid }) {
6 const [events, setEvents] = React.useState([]);
7 const [loading, setLoading] = React.useState(true);
8 const [error, setError] = React.useState(null);
9 const [filter, setFilter] = React.useState('all'); // all, error, warning, information
10 const [useMeshCentral, setUseMeshCentral] = React.useState(true);
11 const [meshInfo, setMeshInfo] = React.useState(null);
12 const [meshLoading, setMeshLoading] = React.useState(false);
13 const [meshError, setMeshError] = React.useState(null);
14
15 // Check if MeshCentral is available
16 React.useEffect(() => {
17 if (!agentId && !agentUuid) return;
18
19 setMeshLoading(true);
20 apiFetch(`/agent/${agentUuid || agentId}/meshcentral-url`)
21 .then((res) => res.ok ? res.json() : null)
22 .then((data) => {
23 if (data?.meshcentralNodeId) {
24 setMeshInfo(data);
25 setUseMeshCentral(true);
26 } else {
27 setUseMeshCentral(false);
28 }
29 })
30 .catch(() => setUseMeshCentral(false))
31 .finally(() => setMeshLoading(false));
32 }, [agentId, agentUuid]);
33
34 React.useEffect(() => {
35 if (!agentId || useMeshCentral) return;
36 setLoading(true);
37 setError(null);
38
39 apiFetch(`/agent/${agentId}/events`)
40 .then((data) => {
41 setEvents(Array.isArray(data) ? data : []);
42 })
43 .catch((err) => {
44 setError(err.message || "Failed to load event logs");
45 setEvents([]);
46 })
47 .finally(() => setLoading(false));
48 }, [agentId, useMeshCentral]);
49
50 if (meshLoading) {
51 return (
52 <div className="tab-container">
53 <h2>Event Viewer</h2>
54 <div className="card">Checking MeshCentral availability...</div>
55 </div>
56 );
57 }
58
59 // Render MeshCentral Event Viewer if available
60 if (useMeshCentral && meshInfo?.meshcentralNodeId) {
61 return (
62 <div className="tab-container" style={{ height: 'calc(100vh - 200px)', display: 'flex', flexDirection: 'column' }}>
63 <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
64 <h2>Event Viewer</h2>
65 <button
66 className="btn btn-secondary"
67 onClick={() => setUseMeshCentral(false)}
68 style={{ fontSize: '0.9em' }}
69 >
70 Switch to Legacy View
71 </button>
72 </div>
73 <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
74 <MeshCentralEmbed
75 agentId={agentId || agentUuid}
76 nodeId={meshInfo.meshcentralNodeId}
77 viewMode={VIEWMODE.EVENTS}
78 title="Event Viewer"
79 />
80 </div>
81 </div>
82 );
83 }
84
85 if (loading) {
86 return (
87 <div className="tab-container">
88 <h2>Event Viewer</h2>
89 <div className="card">Loading event logs...</div>
90 </div>
91 );
92 }
93
94 if (error) {
95 return (
96 <div className="tab-container">
97 <h2>Event Viewer</h2>
98 <div className="card error">{error}</div>
99 </div>
100 );
101 }
102
103 // Filter events based on level
104 const filteredEvents = filter === 'all'
105 ? events
106 : events.filter(e => e.level?.toLowerCase() === filter.toLowerCase());
107
108 // Get event level color
109 const getLevelColor = (level) => {
110 const l = level?.toLowerCase();
111 if (l === 'error' || l === 'critical') return '#dc3545';
112 if (l === 'warning') return '#ffc107';
113 if (l === 'information' || l === 'info') return '#17a2b8';
114 return '#666';
115 };
116
117 // Get event level icon
118 const getLevelIcon = (level) => {
119 const l = level?.toLowerCase();
120 if (l === 'error' || l === 'critical') return 'error';
121 if (l === 'warning') return 'warning';
122 if (l === 'information' || l === 'info') return 'info';
123 return 'description';
124 };
125
126 return (
127 <div className="tab-container">
128 <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '16px' }}>
129 <h2 style={{ margin: 0 }}>Event Viewer</h2>
130 {meshInfo?.available && (
131 <button
132 className="btn-sm"
133 onClick={() => setUseMeshCentral(!useMeshCentral)}
134 style={{ display: 'flex', alignItems: 'center', gap: '4px' }}
135 >
136 <span className="material-symbols-outlined" style={{ fontSize: '18px' }}>
137 {useMeshCentral ? 'list' : 'desktop_windows'}
138 </span>
139 {useMeshCentral ? 'Switch to Legacy View' : 'Switch to MeshCentral'}
140 </button>
141 )}
142 </div>
143
144 {/* MeshCentral Event Viewer */}
145 {useMeshCentral && meshInfo?.available && (
146 <div className="card" style={{ padding: '16px', marginBottom: '16px' }}>
147 <MeshCentralEmbed
148 agentUuid={agent?.uuid}
149 viewMode={VIEWMODE.EVENTS}
150 title="Event Viewer (MeshCentral)"
151 />
152 </div>
153 )}
154
155 {/* Legacy Event Viewer */}
156 {(!useMeshCentral || !meshInfo?.available) && (
157 <>
158 {/* Filter Bar */}
159 <div className="card" style={{ marginBottom: '16px', padding: '12px', display: 'flex', gap: '8px', alignItems: 'center' }}>
160 <span style={{ fontSize: '14px', fontWeight: '500', marginRight: '8px' }}>Filter:</span>
161 <button
162 className={`btn-sm ${filter === 'all' ? 'btn-primary' : ''}`}
163 onClick={() => setFilter('all')}
164 >
165 All ({events.length})
166 </button>
167 <button
168 className={`btn-sm ${filter === 'error' ? 'btn-primary' : ''}`}
169 onClick={() => setFilter('error')}
170 style={{ color: filter !== 'error' ? '#dc3545' : undefined }}
171 >
172 Errors ({events.filter(e => e.level?.toLowerCase() === 'error' || e.level?.toLowerCase() === 'critical').length})
173 </button>
174 <button
175 className={`btn-sm ${filter === 'warning' ? 'btn-primary' : ''}`}
176 onClick={() => setFilter('warning')}
177 style={{ color: filter !== 'warning' ? '#ffc107' : undefined }}
178 >
179 Warnings ({events.filter(e => e.level?.toLowerCase() === 'warning').length})
180 </button>
181 <button
182 className={`btn-sm ${filter === 'information' ? 'btn-primary' : ''}`}
183 onClick={() => setFilter('information')}
184 style={{ color: filter !== 'information' ? '#17a2b8' : undefined }}
185 >
186 Information ({events.filter(e => e.level?.toLowerCase() === 'information' || e.level?.toLowerCase() === 'info').length})
187 </button>
188 </div>
189
190 {/* Events List */}
191 <div className="card">
192 {filteredEvents.length === 0 ? (
193 <div style={{ padding: '40px 20px', textAlign: 'center', color: '#888' }}>
194 <span className="material-symbols-outlined" style={{ fontSize: '48px', opacity: 0.3 }}>
195 event_note
196 </span>
197 <p style={{ marginTop: '16px', fontSize: '16px' }}>
198 {filter === 'all' ? 'No event logs available yet' : `No ${filter} events found`}
199 </p>
200 <p style={{ fontSize: '14px', opacity: 0.7 }}>
201 Event logs will be collected from Windows Event Viewer during the next agent update
202 </p>
203 </div>
204 ) : (
205 <div className="log-box" style={{ maxHeight: '700px', overflow: 'auto' }}>
206 {filteredEvents.map((e, i) => (
207 <div
208 key={i}
209 className="log-entry"
210 style={{
211 padding: '12px 16px',
212 borderLeft: `4px solid ${getLevelColor(e.level)}`,
213 marginBottom: '8px',
214 borderRadius: '4px',
215 background: '#f9f9f9'
216 }}
217 >
218 <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '4px' }}>
219 <span
220 className="material-symbols-outlined"
221 style={{
222 fontSize: '20px',
223 color: getLevelColor(e.level)
224 }}
225 >
226 {getLevelIcon(e.level)}
227 </span>
228 <strong style={{ color: getLevelColor(e.level) }}>
229 [{e.level?.toUpperCase() || 'INFO'}]
230 </strong>
231 <span style={{ color: '#888', fontSize: '13px', marginLeft: 'auto' }}>
232 {e.timestamp ? new Date(e.timestamp).toLocaleString() : ''}
233 </span>
234 </div>
235
236 <div style={{ fontSize: '14px', color: '#333', marginLeft: '28px', marginBottom: '4px' }}>
237 {e.message || 'No message'}
238 </div>
239
240 {e.source && (
241 <div style={{ fontSize: '12px', color: '#888', marginLeft: '28px' }}>
242 Source: {e.source}
243 </div>
244 )}
245
246 {e.event_id && (
247 <div style={{ fontSize: '12px', color: '#888', marginLeft: '28px' }}>
248 Event ID: {e.event_id}
249 </div>
250 )}
251 </div>
252 ))}
253 </div>
254 )}
255 </div>
256 </>
257 )}
258 </div>
259 );
260}