slakwik commited on
Commit
2856bc2
·
verified ·
1 Parent(s): 1e20f4c

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +347 -42
index.html CHANGED
@@ -1,5 +1,6 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -116,8 +117,15 @@
116
  }
117
 
118
  @keyframes fadeIn {
119
- from { opacity: 0; transform: translateY(10px); }
120
- to { opacity: 1; transform: translateY(0); }
 
 
 
 
 
 
 
121
  }
122
 
123
  .loading-spinner {
@@ -130,7 +138,9 @@
130
  }
131
 
132
  @keyframes spin {
133
- to { transform: rotate(360deg); }
 
 
134
  }
135
 
136
  .sidebar {
@@ -146,8 +156,45 @@
146
  transform: translateX(0);
147
  }
148
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  </style>
150
  </head>
 
151
  <body class="min-h-screen">
152
  <!-- Header -->
153
  <header class="bg-white shadow-sm border-b">
@@ -158,7 +205,8 @@
158
  <i class="fas fa-bars text-xl"></i>
159
  </button>
160
  <div class="flex items-center space-x-3">
161
- <div class="w-8 h-8 bg-gradient-to-r from-blue-500 to-green-500 rounded-md flex items-center justify-center">
 
162
  <i class="fas fa-chart-line text-white text-sm"></i>
163
  </div>
164
  <span class="text-xl font-bold text-gray-800">Zabbix Event Analyzer</span>
@@ -170,7 +218,8 @@
170
  <i class="fas fa-robot text-blue-500"></i>
171
  <span>Powered by Ollama</span>
172
  </div>
173
- <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="text-sm text-gray-500 hover:text-gray-700 flex items-center space-x-1">
 
174
  <i class="fas fa-code"></i>
175
  <span>Built with anycoder</span>
176
  </a>
@@ -248,13 +297,66 @@
248
  <!-- Main Content -->
249
  <main class="flex-1 overflow-y-auto">
250
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  <!-- Stats Cards -->
252
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
253
  <div class="card p-6">
254
  <div class="flex items-center justify-between">
255
  <div>
256
  <p class="text-sm font-medium text-gray-500">Total Events</p>
257
- <p class="text-3xl font-bold text-gray-800 mt-1">1,248</p>
258
  </div>
259
  <div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center">
260
  <i class="fas fa-bell text-blue-600 text-xl"></i>
@@ -270,7 +372,7 @@
270
  <div class="flex items-center justify-between">
271
  <div>
272
  <p class="text-sm font-medium text-gray-500">Critical Events</p>
273
- <p class="text-3xl font-bold text-gray-800 mt-1">42</p>
274
  </div>
275
  <div class="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center">
276
  <i class="fas fa-exclamation-triangle text-red-600 text-xl"></i>
@@ -286,7 +388,7 @@
286
  <div class="flex items-center justify-between">
287
  <div>
288
  <p class="text-sm font-medium text-gray-500">Analyzed Events</p>
289
- <p class="text-3xl font-bold text-gray-800 mt-1">876</p>
290
  </div>
291
  <div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center">
292
  <i class="fas fa-brain text-green-600 text-xl"></i>
@@ -302,7 +404,7 @@
302
  <div class="flex items-center justify-between">
303
  <div>
304
  <p class="text-sm font-medium text-gray-500">Active Triggers</p>
305
- <p class="text-3xl font-bold text-gray-800 mt-1">18</p>
306
  </div>
307
  <div class="w-12 h-12 bg-purple-100 rounded-full flex items-center justify-center">
308
  <i class="fas fa-bolt text-purple-600 text-xl"></i>
@@ -323,10 +425,16 @@
323
  <div class="p-6 border-b">
324
  <div class="flex items-center justify-between">
325
  <h2 class="text-lg font-semibold text-gray-800">Recent Events</h2>
326
- <button id="refresh-events" class="text-blue-600 hover:text-blue-800 flex items-center space-x-1">
327
- <i class="fas fa-sync-alt"></i>
328
- <span>Refresh</span>
329
- </button>
 
 
 
 
 
 
330
  </div>
331
  </div>
332
 
@@ -346,8 +454,10 @@
346
  </span>
347
  <span class="text-sm text-gray-500">2023-11-15 14:32:17</span>
348
  </div>
349
- <h3 class="font-semibold text-gray-800 mt-2">High CPU load on web-server-01</h3>
350
- <p class="text-sm text-gray-600 mt-1">CPU load has been over 90% for the last 5 minutes</p>
 
 
351
  <div class="mt-3 flex flex-wrap gap-2">
352
  <span class="px-2 py-1 bg-gray-100 text-xs rounded-full">web-server-01</span>
353
  <span class="px-2 py-1 bg-gray-100 text-xs rounded-full">CPU</span>
@@ -382,11 +492,13 @@
382
 
383
  <div class="p-6">
384
  <div id="analysis-placeholder" class="text-center py-12">
385
- <div class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
 
386
  <i class="fas fa-brain text-blue-600 text-2xl"></i>
387
  </div>
388
  <h3 class="text-gray-800 font-medium">Select an event to analyze</h3>
389
- <p class="text-gray-500 text-sm mt-1">Click the "Analyze" button on any event to get AI-powered insights</p>
 
390
  </div>
391
 
392
  <div id="analysis-result" class="hidden">
@@ -398,7 +510,9 @@
398
  <div class="space-y-4">
399
  <div>
400
  <p class="text-sm font-medium text-gray-500 mb-1">Root Cause</p>
401
- <p class="text-gray-800" id="analysis-root-cause">The high CPU load is likely caused by a sudden spike in web traffic combined with inefficient database queries in the application.</p>
 
 
402
  </div>
403
 
404
  <div>
@@ -432,8 +546,10 @@
432
  <div class="pt-4 border-t">
433
  <p class="text-sm font-medium text-gray-500 mb-2">Additional Context</p>
434
  <div class="space-y-2 text-sm text-gray-600" id="analysis-context">
435
- <p>Similar events occurred 3 times in the last week during peak hours.</p>
436
- <p>The database server shows correlated high load during these events.</p>
 
 
437
  <p>No recent deployment changes that would explain this behavior.</p>
438
  </div>
439
  </div>
@@ -459,9 +575,24 @@
459
  </div>
460
  </div>
461
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462
  <script>
463
  // Mock data for events
464
- const mockEvents = [
465
  {
466
  id: 1,
467
  timestamp: "2023-11-15 14:32:17",
@@ -545,23 +676,64 @@
545
  const analysisPlaceholder = document.getElementById('analysis-placeholder');
546
  const analysisResult = document.getElementById('analysis-result');
547
  const loadingOverlay = document.getElementById('loading-overlay');
 
548
  const menuBtn = document.getElementById('menu-btn');
549
  const sidebar = document.getElementById('sidebar');
550
  const refreshBtn = document.getElementById('refresh-events');
551
  const loadMoreBtn = document.getElementById('load-more');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552
 
553
  // Initialize the app
554
  function init() {
555
  renderEvents();
556
  setupEventListeners();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
  }
558
 
559
  // Render events to the timeline
560
  function renderEvents() {
561
  eventsContainer.innerHTML = '';
562
 
 
 
 
 
 
563
  // For demo purposes, we'll show all events
564
- mockEvents.forEach(event => {
565
  const eventElement = createEventElement(event);
566
  eventsContainer.appendChild(eventElement);
567
  });
@@ -623,21 +795,166 @@
623
 
624
  // Refresh events button
625
  refreshBtn.addEventListener('click', () => {
626
- // In a real app, this would fetch new data
627
  showNotification("Events refreshed", "success");
628
  });
629
 
630
  // Load more events button
631
  loadMoreBtn.addEventListener('click', () => {
632
- // In a real app, this would load more events
633
  showNotification("No more events to load", "info");
634
  });
635
 
636
  // Apply filters button
637
  document.getElementById('apply-filters').addEventListener('click', () => {
638
- // In a real app, this would apply the filters
639
  showNotification("Filters applied", "success");
640
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
  }
642
 
643
  // Simulate analyzing an event with Ollama
@@ -648,7 +965,7 @@
648
  // Hide placeholder and show analysis after a delay (simulating API call)
649
  setTimeout(() => {
650
  // Find the event
651
- const event = mockEvents.find(e => e.id === eventId);
652
 
653
  // Update the analysis result with mock data
654
  document.getElementById('analysis-root-cause').textContent = mockAnalysis.rootCause;
@@ -686,6 +1003,9 @@
686
  analyzeBtn.classList.add('text-green-600', 'cursor-default');
687
  analyzeBtn.removeAttribute('data-event-id');
688
 
 
 
 
689
  showNotification("Event analyzed successfully", "success");
690
  }, 1500);
691
  }
@@ -697,23 +1017,8 @@
697
 
698
  // Show notification
699
  function showNotification(message, type) {
700
- // In a real app, you might use a proper notification system
701
  const notification = document.createElement('div');
702
  notification.className = `fixed bottom-4 right-4 px-4 py-2 rounded-md shadow-lg text-white ${
703
  type === 'success' ? 'bg-green-600' :
704
  type === 'error' ? 'bg-red-600' :
705
- type === 'warning' ? 'bg-yellow-600' : 'bg-blue-600'
706
- }`;
707
- notification.textContent = message;
708
- document.body.appendChild(notification);
709
-
710
- setTimeout(() => {
711
- notification.remove();
712
- }, 3000);
713
- }
714
-
715
- // Initialize the app when DOM is loaded
716
- document.addEventListener('DOMContentLoaded', init);
717
- </script>
718
- </body>
719
- </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
+
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
117
  }
118
 
119
  @keyframes fadeIn {
120
+ from {
121
+ opacity: 0;
122
+ transform: translateY(10px);
123
+ }
124
+
125
+ to {
126
+ opacity: 1;
127
+ transform: translateY(0);
128
+ }
129
  }
130
 
131
  .loading-spinner {
 
138
  }
139
 
140
  @keyframes spin {
141
+ to {
142
+ transform: rotate(360deg);
143
+ }
144
  }
145
 
146
  .sidebar {
 
156
  transform: translateX(0);
157
  }
158
  }
159
+
160
+ /* CSV Upload Styles */
161
+ .file-upload-area {
162
+ border: 2px dashed #d1d5db;
163
+ border-radius: 8px;
164
+ transition: all 0.3s ease;
165
+ }
166
+
167
+ .file-upload-area:hover {
168
+ border-color: var(--primary);
169
+ background-color: #f0f9ff;
170
+ }
171
+
172
+ .file-upload-area.active {
173
+ border-color: var(--secondary);
174
+ background-color: #d1fae5;
175
+ }
176
+
177
+ .file-list-item {
178
+ animation: slideIn 0.3s ease-out;
179
+ }
180
+
181
+ @keyframes slideIn {
182
+ from {
183
+ opacity: 0;
184
+ transform: translateX(-20px);
185
+ }
186
+ to {
187
+ opacity: 1;
188
+ transform: translateX(0);
189
+ }
190
+ }
191
+
192
+ .progress-bar {
193
+ transition: width 0.3s ease;
194
+ }
195
  </style>
196
  </head>
197
+
198
  <body class="min-h-screen">
199
  <!-- Header -->
200
  <header class="bg-white shadow-sm border-b">
 
205
  <i class="fas fa-bars text-xl"></i>
206
  </button>
207
  <div class="flex items-center space-x-3">
208
+ <div
209
+ class="w-8 h-8 bg-gradient-to-r from-blue-500 to-green-500 rounded-md flex items-center justify-center">
210
  <i class="fas fa-chart-line text-white text-sm"></i>
211
  </div>
212
  <span class="text-xl font-bold text-gray-800">Zabbix Event Analyzer</span>
 
218
  <i class="fas fa-robot text-blue-500"></i>
219
  <span>Powered by Ollama</span>
220
  </div>
221
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank"
222
+ class="text-sm text-gray-500 hover:text-gray-700 flex items-center space-x-1">
223
  <i class="fas fa-code"></i>
224
  <span>Built with anycoder</span>
225
  </a>
 
297
  <!-- Main Content -->
298
  <main class="flex-1 overflow-y-auto">
299
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
300
+ <!-- CSV Upload Section -->
301
+ <div class="card mb-8">
302
+ <div class="p-6">
303
+ <div class="flex items-center justify-between mb-4">
304
+ <h2 class="text-lg font-semibold text-gray-800">Import Zabbix Data</h2>
305
+ <button id="upload-csv-btn" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition-colors flex items-center space-x-2">
306
+ <i class="fas fa-file-upload"></i>
307
+ <span>Upload CSV</span>
308
+ </button>
309
+ </div>
310
+
311
+ <div id="csv-upload-area" class="file-upload-area p-8 text-center cursor-pointer">
312
+ <input type="file" id="csv-file-input" accept=".csv" class="hidden">
313
+ <div class="mb-4">
314
+ <i class="fas fa-cloud-upload-alt text-3xl text-gray-400 mb-2"></i>
315
+ <p class="text-gray-600">Drag & drop CSV file here or click to browse</p>
316
+ <p class="text-sm text-gray-500 mt-1">Supports Zabbix event export format</p>
317
+ </div>
318
+ </div>
319
+
320
+ <div id="file-preview" class="hidden mt-6">
321
+ <h3 class="text-sm font-medium text-gray-700 mb-3">Selected File:</h3>
322
+ <div class="flex items-center justify-between p-3 bg-gray-50 rounded-md">
323
+ <div class="flex items-center">
324
+ <i class="fas fa-file-csv text-blue-600 text-xl mr-3"></i>
325
+ <div>
326
+ <p id="file-name" class="font-medium text-gray-800"></p>
327
+ <p id="file-size" class="text-sm text-gray-500"></p>
328
+ </div>
329
+ </div>
330
+ <div class="flex items-center space-x-3">
331
+ <button id="parse-csv-btn" class="text-blue-600 hover:text-blue-800">
332
+ <i class="fas fa-play mr-1"></i> Parse
333
+ </button>
334
+ <button id="cancel-upload-btn" class="text-gray-500 hover:text-gray-700">
335
+ <i class="fas fa-times"></i>
336
+ </button>
337
+ </div>
338
+ </div>
339
+
340
+ <div id="parse-progress" class="mt-4 hidden">
341
+ <div class="flex items-center justify-between mb-1">
342
+ <span class="text-sm font-medium text-gray-700">Parsing CSV...</span>
343
+ <span id="parse-percentage" class="text-sm font-medium text-gray-700">0%</span>
344
+ </div>
345
+ <div class="w-full bg-gray-200 rounded-full h-2">
346
+ <div id="progress-bar" class="progress-bar bg-blue-600 h-2 rounded-full" style="width: 0%"></div>
347
+ </div>
348
+ </div>
349
+ </div>
350
+ </div>
351
+ </div>
352
+
353
  <!-- Stats Cards -->
354
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
355
  <div class="card p-6">
356
  <div class="flex items-center justify-between">
357
  <div>
358
  <p class="text-sm font-medium text-gray-500">Total Events</p>
359
+ <p class="text-3xl font-bold text-gray-800 mt-1" id="total-events">1,248</p>
360
  </div>
361
  <div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center">
362
  <i class="fas fa-bell text-blue-600 text-xl"></i>
 
372
  <div class="flex items-center justify-between">
373
  <div>
374
  <p class="text-sm font-medium text-gray-500">Critical Events</p>
375
+ <p class="text-3xl font-bold text-gray-800 mt-1" id="critical-events">42</p>
376
  </div>
377
  <div class="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center">
378
  <i class="fas fa-exclamation-triangle text-red-600 text-xl"></i>
 
388
  <div class="flex items-center justify-between">
389
  <div>
390
  <p class="text-sm font-medium text-gray-500">Analyzed Events</p>
391
+ <p class="text-3xl font-bold text-gray-800 mt-1" id="analyzed-events">876</p>
392
  </div>
393
  <div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center">
394
  <i class="fas fa-brain text-green-600 text-xl"></i>
 
404
  <div class="flex items-center justify-between">
405
  <div>
406
  <p class="text-sm font-medium text-gray-500">Active Triggers</p>
407
+ <p class="text-3xl font-bold text-gray-800 mt-1" id="active-triggers">18</p>
408
  </div>
409
  <div class="w-12 h-12 bg-purple-100 rounded-full flex items-center justify-center">
410
  <i class="fas fa-bolt text-purple-600 text-xl"></i>
 
425
  <div class="p-6 border-b">
426
  <div class="flex items-center justify-between">
427
  <h2 class="text-lg font-semibold text-gray-800">Recent Events</h2>
428
+ <div class="flex items-center space-x-3">
429
+ <button id="refresh-events" class="text-blue-600 hover:text-blue-800 flex items-center space-x-1">
430
+ <i class="fas fa-sync-alt"></i>
431
+ <span>Refresh</span>
432
+ </button>
433
+ <button id="export-events" class="text-green-600 hover:text-green-800 flex items-center space-x-1">
434
+ <i class="fas fa-file-export"></i>
435
+ <span>Export</span>
436
+ </button>
437
+ </div>
438
  </div>
439
  </div>
440
 
 
454
  </span>
455
  <span class="text-sm text-gray-500">2023-11-15 14:32:17</span>
456
  </div>
457
+ <h3 class="font-semibold text-gray-800 mt-2">High CPU load on
458
+ web-server-01</h3>
459
+ <p class="text-sm text-gray-600 mt-1">CPU load has been over 90%
460
+ for the last 5 minutes</p>
461
  <div class="mt-3 flex flex-wrap gap-2">
462
  <span class="px-2 py-1 bg-gray-100 text-xs rounded-full">web-server-01</span>
463
  <span class="px-2 py-1 bg-gray-100 text-xs rounded-full">CPU</span>
 
492
 
493
  <div class="p-6">
494
  <div id="analysis-placeholder" class="text-center py-12">
495
+ <div
496
+ class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
497
  <i class="fas fa-brain text-blue-600 text-2xl"></i>
498
  </div>
499
  <h3 class="text-gray-800 font-medium">Select an event to analyze</h3>
500
+ <p class="text-gray-500 text-sm mt-1">Click the "Analyze" button on any event to get
501
+ AI-powered insights</p>
502
  </div>
503
 
504
  <div id="analysis-result" class="hidden">
 
510
  <div class="space-y-4">
511
  <div>
512
  <p class="text-sm font-medium text-gray-500 mb-1">Root Cause</p>
513
+ <p class="text-gray-800" id="analysis-root-cause">The high CPU load is
514
+ likely caused by a sudden spike in web traffic combined with inefficient
515
+ database queries in the application.</p>
516
  </div>
517
 
518
  <div>
 
546
  <div class="pt-4 border-t">
547
  <p class="text-sm font-medium text-gray-500 mb-2">Additional Context</p>
548
  <div class="space-y-2 text-sm text-gray-600" id="analysis-context">
549
+ <p>Similar events occurred 3 times in the last week during peak hours.
550
+ </p>
551
+ <p>The database server shows correlated high load during these events.
552
+ </p>
553
  <p>No recent deployment changes that would explain this behavior.</p>
554
  </div>
555
  </div>
 
575
  </div>
576
  </div>
577
 
578
+ <!-- CSV Processing Overlay -->
579
+ <div id="csv-processing-overlay" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
580
+ <div class="bg-white rounded-lg p-6 w-96 text-center">
581
+ <div class="flex justify-center mb-4">
582
+ <div class="w-16 h-16 border-4 border-blue-200 border-t-blue-600 rounded-full animate-spin"></div>
583
+ </div>
584
+ <h3 class="text-lg font-semibold text-gray-800 mb-2" id="csv-processing-title">Processing CSV File</h3>
585
+ <p class="text-gray-600 mb-4" id="csv-processing-message">Parsing Zabbix events from the uploaded file...</p>
586
+ <div class="w-full bg-gray-200 rounded-full h-3 mb-2">
587
+ <div id="csv-processing-progress" class="bg-blue-600 h-3 rounded-full" style="width: 0%"></div>
588
+ </div>
589
+ <p class="text-sm text-gray-500" id="csv-processing-percentage">0% Complete</p>
590
+ </div>
591
+ </div>
592
+
593
  <script>
594
  // Mock data for events
595
+ let eventsData = [
596
  {
597
  id: 1,
598
  timestamp: "2023-11-15 14:32:17",
 
676
  const analysisPlaceholder = document.getElementById('analysis-placeholder');
677
  const analysisResult = document.getElementById('analysis-result');
678
  const loadingOverlay = document.getElementById('loading-overlay');
679
+ const csvProcessingOverlay = document.getElementById('csv-processing-overlay');
680
  const menuBtn = document.getElementById('menu-btn');
681
  const sidebar = document.getElementById('sidebar');
682
  const refreshBtn = document.getElementById('refresh-events');
683
  const loadMoreBtn = document.getElementById('load-more');
684
+ const uploadCsvBtn = document.getElementById('upload-csv-btn');
685
+ const csvUploadArea = document.getElementById('csv-upload-area');
686
+ const csvFileInput = document.getElementById('csv-file-input');
687
+ const filePreview = document.getElementById('file-preview');
688
+ const fileName = document.getElementById('file-name');
689
+ const fileSize = document.getElementById('file-size');
690
+ const parseCsvBtn = document.getElementById('parse-csv-btn');
691
+ const cancelUploadBtn = document.getElementById('cancel-upload-btn');
692
+ const parseProgress = document.getElementById('parse-progress');
693
+ const progressBar = document.getElementById('progress-bar');
694
+ const parsePercentage = document.getElementById('parse-percentage');
695
+ const csvProcessingProgress = document.getElementById('csv-processing-progress');
696
+ const csvProcessingPercentage = document.getElementById('csv-processing-percentage');
697
+ const csvProcessingTitle = document.getElementById('csv-processing-title');
698
+ const csvProcessingMessage = document.getElementById('csv-processing-message');
699
+
700
+ // Stats elements
701
+ const totalEventsEl = document.getElementById('total-events');
702
+ const criticalEventsEl = document.getElementById('critical-events');
703
+ const analyzedEventsEl = document.getElementById('analyzed-events');
704
+ const activeTriggersEl = document.getElementById('active-triggers');
705
 
706
  // Initialize the app
707
  function init() {
708
  renderEvents();
709
  setupEventListeners();
710
+ updateStats();
711
+ }
712
+
713
+ // Update statistics
714
+ function updateStats() {
715
+ const totalEvents = eventsData.length;
716
+ const criticalEvents = eventsData.filter(e => e.severity === 'disaster' || e.severity === 'high').length;
717
+ const analyzedEvents = eventsData.filter(e => e.analyzed).length;
718
+ const activeTriggers = Math.floor(totalEvents * 0.1); // Mock calculation
719
+
720
+ totalEventsEl.textContent = totalEvents.toLocaleString();
721
+ criticalEventsEl.textContent = criticalEvents.toLocaleString();
722
+ analyzedEventsEl.textContent = analyzedEvents.toLocaleString();
723
+ activeTriggersEl.textContent = activeTriggers.toLocaleString();
724
  }
725
 
726
  // Render events to the timeline
727
  function renderEvents() {
728
  eventsContainer.innerHTML = '';
729
 
730
+ // Sort events by timestamp (newest first)
731
+ const sortedEvents = [...eventsData].sort((a, b) => {
732
+ return new Date(b.timestamp) - new Date(a.timestamp);
733
+ });
734
+
735
  // For demo purposes, we'll show all events
736
+ sortedEvents.forEach(event => {
737
  const eventElement = createEventElement(event);
738
  eventsContainer.appendChild(eventElement);
739
  });
 
795
 
796
  // Refresh events button
797
  refreshBtn.addEventListener('click', () => {
 
798
  showNotification("Events refreshed", "success");
799
  });
800
 
801
  // Load more events button
802
  loadMoreBtn.addEventListener('click', () => {
 
803
  showNotification("No more events to load", "info");
804
  });
805
 
806
  // Apply filters button
807
  document.getElementById('apply-filters').addEventListener('click', () => {
 
808
  showNotification("Filters applied", "success");
809
  });
810
+
811
+ // CSV Upload functionality
812
+ uploadCsvBtn.addEventListener('click', () => {
813
+ csvFileInput.click();
814
+ });
815
+
816
+ csvUploadArea.addEventListener('click', () => {
817
+ csvFileInput.click();
818
+ });
819
+
820
+ // Drag and drop functionality
821
+ csvUploadArea.addEventListener('dragover', (e) => {
822
+ e.preventDefault();
823
+ csvUploadArea.classList.add('active');
824
+ });
825
+
826
+ csvUploadArea.addEventListener('dragleave', () => {
827
+ csvUploadArea.classList.remove('active');
828
+ });
829
+
830
+ csvUploadArea.addEventListener('drop', (e) => {
831
+ e.preventDefault();
832
+ csvUploadArea.classList.remove('active');
833
+
834
+ if (e.dataTransfer.files.length) {
835
+ handleFileSelect(e.dataTransfer.files[0]);
836
+ }
837
+ });
838
+
839
+ csvFileInput.addEventListener('change', (e) => {
840
+ if (e.target.files.length) {
841
+ handleFileSelect(e.target.files[0]);
842
+ }
843
+ });
844
+
845
+ parseCsvBtn.addEventListener('click', () => {
846
+ parseCsvFile();
847
+ });
848
+
849
+ cancelUploadBtn.addEventListener('click', () => {
850
+ resetFileUpload();
851
+ });
852
+ }
853
+
854
+ // Handle file selection
855
+ function handleFileSelect(file) {
856
+ if (file.type !== 'text/csv' && !file.name.endsWith('.csv')) {
857
+ showNotification("Please upload a valid CSV file", "error");
858
+ return;
859
+ }
860
+
861
+ filePreview.classList.remove('hidden');
862
+ fileName.textContent = file.name;
863
+ fileSize.textContent = formatFileSize(file.size);
864
+ }
865
+
866
+ // Format file size
867
+ function formatFileSize(bytes) {
868
+ if (bytes === 0) return '0 Bytes';
869
+
870
+ const k = 1024;
871
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
872
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
873
+
874
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
875
+ }
876
+
877
+ // Parse CSV file
878
+ function parseCsvFile() {
879
+ const file = csvFileInput.files[0];
880
+ if (!file) return;
881
+
882
+ // Show processing overlay
883
+ csvProcessingOverlay.classList.remove('hidden');
884
+ csvProcessingTitle.textContent = "Processing CSV File";
885
+ csvProcessingMessage.textContent = "Parsing Zabbix events from the uploaded file...";
886
+
887
+ // Simulate parsing progress
888
+ let progress = 0;
889
+ const interval = setInterval(() => {
890
+ progress += 5;
891
+ csvProcessingProgress.style.width = `${progress}%`;
892
+ csvProcessingPercentage.textContent = `${progress}% Complete`;
893
+
894
+ if (progress >= 100) {
895
+ clearInterval(interval);
896
+ // Simulate successful parsing
897
+ setTimeout(() => {
898
+ processMockCsvData();
899
+ csvProcessingOverlay.classList.add('hidden');
900
+ resetFileUpload();
901
+ showNotification("CSV file processed successfully! 12 new events imported.", "success");
902
+ }, 500);
903
+ }
904
+ }, 100);
905
+ }
906
+
907
+ // Process mock CSV data (simulating real CSV parsing)
908
+ function processMockCsvData() {
909
+ // Add some mock events to simulate CSV import
910
+ const newEvents = [
911
+ {
912
+ id: eventsData.length + 1,
913
+ timestamp: "2023-11-16 08:15:22",
914
+ severity: "high",
915
+ name: "Disk I/O latency on database server",
916
+ description: "Disk response time exceeds 100ms threshold",
917
+ host: "db-primary-01",
918
+ tags: ["db-primary-01", "Disk", "Performance"],
919
+ analyzed: false
920
+ },
921
+ {
922
+ id: eventsData.length + 2,
923
+ timestamp: "2023-11-16 07:42:11",
924
+ severity: "warning",
925
+ name: "High network traffic on firewall",
926
+ description: "Network throughput exceeds 80% of capacity",
927
+ host: "firewall-01",
928
+ tags: ["firewall-01", "Network", "Traffic"],
929
+ analyzed: false
930
+ },
931
+ {
932
+ id: eventsData.length + 3,
933
+ timestamp: "2023-11-16 07:18:04",
934
+ severity: "average",
935
+ name: "Memory usage warning on app server",
936
+ description: "Memory usage at 75% capacity",
937
+ host: "app-server-03",
938
+ tags: ["app-server-03", "Memory", "Performance"],
939
+ analyzed: false
940
+ }
941
+ ];
942
+
943
+ // Add new events to the data
944
+ eventsData.push(...newEvents);
945
+
946
+ // Update the UI
947
+ renderEvents();
948
+ updateStats();
949
+ }
950
+
951
+ // Reset file upload UI
952
+ function resetFileUpload() {
953
+ csvFileInput.value = '';
954
+ filePreview.classList.add('hidden');
955
+ parseProgress.classList.add('hidden');
956
+ progressBar.style.width = '0%';
957
+ parsePercentage.textContent = '0%';
958
  }
959
 
960
  // Simulate analyzing an event with Ollama
 
965
  // Hide placeholder and show analysis after a delay (simulating API call)
966
  setTimeout(() => {
967
  // Find the event
968
+ const event = eventsData.find(e => e.id === eventId);
969
 
970
  // Update the analysis result with mock data
971
  document.getElementById('analysis-root-cause').textContent = mockAnalysis.rootCause;
 
1003
  analyzeBtn.classList.add('text-green-600', 'cursor-default');
1004
  analyzeBtn.removeAttribute('data-event-id');
1005
 
1006
+ // Update stats
1007
+ updateStats();
1008
+
1009
  showNotification("Event analyzed successfully", "success");
1010
  }, 1500);
1011
  }
 
1017
 
1018
  // Show notification
1019
  function showNotification(message, type) {
 
1020
  const notification = document.createElement('div');
1021
  notification.className = `fixed bottom-4 right-4 px-4 py-2 rounded-md shadow-lg text-white ${
1022
  type === 'success' ? 'bg-green-600' :
1023
  type === 'error' ? 'bg-red-600' :
1024
+ type === 'warning' ? 'bg-yellow-600' : 'bg-blue-600'