anycoder-9b294fef / index.html
slakwik's picture
Upload folder using huggingface_hub
2856bc2 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zabbix Event Analyzer with Ollama</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary: #3b82f6;
--primary-dark: #1d4ed8;
--secondary: #10b981;
--background: #f8fafc;
--card: #ffffff;
--text: #1e293b;
--text-light: #64748b;
--border: #e2e8f0;
--danger: #ef4444;
--warning: #f59e0b;
--info: #06b6d4;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background-color: var(--background);
color: var(--text);
}
.gradient-bg {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
}
.card {
background: var(--card);
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
border: 1px solid var(--border);
}
.severity-badge {
padding: 4px 12px;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 6px;
}
.severity-disaster {
background-color: #fef2f2;
color: #991b1b;
}
.severity-high {
background-color: #fee2e2;
color: #b91c1c;
}
.severity-average {
background-color: #fef3c7;
color: #b45309;
}
.severity-warning {
background-color: #fef9c3;
color: #a16207;
}
.severity-info {
background-color: #dbeafe;
color: #1e40af;
}
.severity-not-classified {
background-color: #f3f4f6;
color: #6b7280;
}
.event-timeline {
position: relative;
padding-left: 20px;
}
.event-timeline::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 2px;
background: linear-gradient(to bottom, var(--primary), var(--secondary));
border-radius: 2px;
}
.event-item {
position: relative;
margin-bottom: 24px;
}
.event-item::before {
content: '';
position: absolute;
left: -26px;
top: 0;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--card);
border: 2px solid var(--primary);
}
.analysis-result {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.sidebar {
transition: transform 0.3s ease-in-out;
}
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.open {
transform: translateX(0);
}
}
/* CSV Upload Styles */
.file-upload-area {
border: 2px dashed #d1d5db;
border-radius: 8px;
transition: all 0.3s ease;
}
.file-upload-area:hover {
border-color: var(--primary);
background-color: #f0f9ff;
}
.file-upload-area.active {
border-color: var(--secondary);
background-color: #d1fae5;
}
.file-list-item {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.progress-bar {
transition: width 0.3s ease;
}
</style>
</head>
<body class="min-h-screen">
<!-- Header -->
<header class="bg-white shadow-sm border-b">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<div class="flex items-center">
<button id="menu-btn" class="md:hidden p-2 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100">
<i class="fas fa-bars text-xl"></i>
</button>
<div class="flex items-center space-x-3">
<div
class="w-8 h-8 bg-gradient-to-r from-blue-500 to-green-500 rounded-md flex items-center justify-center">
<i class="fas fa-chart-line text-white text-sm"></i>
</div>
<span class="text-xl font-bold text-gray-800">Zabbix Event Analyzer</span>
</div>
</div>
<div class="flex items-center space-x-4">
<div class="hidden md:flex items-center space-x-2 text-sm text-gray-600">
<i class="fas fa-robot text-blue-500"></i>
<span>Powered by Ollama</span>
</div>
<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">
<i class="fas fa-code"></i>
<span>Built with anycoder</span>
</a>
</div>
</div>
</div>
</header>
<div class="flex">
<!-- Sidebar -->
<aside id="sidebar" class="sidebar w-64 bg-white border-r flex-shrink-0 md:block">
<div class="p-4 border-b">
<h3 class="font-semibold text-gray-800">Filters</h3>
</div>
<div class="p-4 space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Severity</label>
<div class="space-y-2">
<label class="flex items-center">
<input type="checkbox" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500" checked>
<span class="ml-2 text-sm text-gray-600">Disaster</span>
</label>
<label class="flex items-center">
<input type="checkbox" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500" checked>
<span class="ml-2 text-sm text-gray-600">High</span>
</label>
<label class="flex items-center">
<input type="checkbox" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500" checked>
<span class="ml-2 text-sm text-gray-600">Average</span>
</label>
<label class="flex items-center">
<input type="checkbox" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500" checked>
<span class="ml-2 text-sm text-gray-600">Warning</span>
</label>
<label class="flex items-center">
<input type="checkbox" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500" checked>
<span class="ml-2 text-sm text-gray-600">Information</span>
</label>
<label class="flex items-center">
<input type="checkbox" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500" checked>
<span class="ml-2 text-sm text-gray-600">Not classified</span>
</label>
</div>
</div>
<div>
<label for="time-range" class="block text-sm font-medium text-gray-700 mb-2">Time Range</label>
<select id="time-range" class="w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500">
<option value="1h">Last 1 hour</option>
<option value="6h" selected>Last 6 hours</option>
<option value="12h">Last 12 hours</option>
<option value="24h">Last 24 hours</option>
<option value="7d">Last 7 days</option>
<option value="30d">Last 30 days</option>
</select>
</div>
<div>
<label for="host-filter" class="block text-sm font-medium text-gray-700 mb-2">Host</label>
<input type="text" id="host-filter" placeholder="Filter by host..." class="w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500">
</div>
<div>
<label for="event-filter" class="block text-sm font-medium text-gray-700 mb-2">Event Name</label>
<input type="text" id="event-filter" placeholder="Filter by event name..." class="w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500">
</div>
<button id="apply-filters" class="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition-colors">
Apply Filters
</button>
</div>
</aside>
<!-- Main Content -->
<main class="flex-1 overflow-y-auto">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- CSV Upload Section -->
<div class="card mb-8">
<div class="p-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-lg font-semibold text-gray-800">Import Zabbix Data</h2>
<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">
<i class="fas fa-file-upload"></i>
<span>Upload CSV</span>
</button>
</div>
<div id="csv-upload-area" class="file-upload-area p-8 text-center cursor-pointer">
<input type="file" id="csv-file-input" accept=".csv" class="hidden">
<div class="mb-4">
<i class="fas fa-cloud-upload-alt text-3xl text-gray-400 mb-2"></i>
<p class="text-gray-600">Drag & drop CSV file here or click to browse</p>
<p class="text-sm text-gray-500 mt-1">Supports Zabbix event export format</p>
</div>
</div>
<div id="file-preview" class="hidden mt-6">
<h3 class="text-sm font-medium text-gray-700 mb-3">Selected File:</h3>
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-md">
<div class="flex items-center">
<i class="fas fa-file-csv text-blue-600 text-xl mr-3"></i>
<div>
<p id="file-name" class="font-medium text-gray-800"></p>
<p id="file-size" class="text-sm text-gray-500"></p>
</div>
</div>
<div class="flex items-center space-x-3">
<button id="parse-csv-btn" class="text-blue-600 hover:text-blue-800">
<i class="fas fa-play mr-1"></i> Parse
</button>
<button id="cancel-upload-btn" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div id="parse-progress" class="mt-4 hidden">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium text-gray-700">Parsing CSV...</span>
<span id="parse-percentage" class="text-sm font-medium text-gray-700">0%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2">
<div id="progress-bar" class="progress-bar bg-blue-600 h-2 rounded-full" style="width: 0%"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div class="card p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500">Total Events</p>
<p class="text-3xl font-bold text-gray-800 mt-1" id="total-events">1,248</p>
</div>
<div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center">
<i class="fas fa-bell text-blue-600 text-xl"></i>
</div>
</div>
<p class="text-sm text-green-600 mt-2 flex items-center">
<i class="fas fa-arrow-up mr-1"></i>
12% from yesterday
</p>
</div>
<div class="card p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500">Critical Events</p>
<p class="text-3xl font-bold text-gray-800 mt-1" id="critical-events">42</p>
</div>
<div class="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center">
<i class="fas fa-exclamation-triangle text-red-600 text-xl"></i>
</div>
</div>
<p class="text-sm text-red-600 mt-2 flex items-center">
<i class="fas fa-arrow-down mr-1"></i>
5% from yesterday
</p>
</div>
<div class="card p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500">Analyzed Events</p>
<p class="text-3xl font-bold text-gray-800 mt-1" id="analyzed-events">876</p>
</div>
<div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center">
<i class="fas fa-brain text-green-600 text-xl"></i>
</div>
</div>
<p class="text-sm text-green-600 mt-2 flex items-center">
<i class="fas fa-arrow-up mr-1"></i>
22% from yesterday
</p>
</div>
<div class="card p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500">Active Triggers</p>
<p class="text-3xl font-bold text-gray-800 mt-1" id="active-triggers">18</p>
</div>
<div class="w-12 h-12 bg-purple-100 rounded-full flex items-center justify-center">
<i class="fas fa-bolt text-purple-600 text-xl"></i>
</div>
</div>
<p class="text-sm text-purple-600 mt-2 flex items-center">
<i class="fas fa-arrow-up mr-1"></i>
8% from yesterday
</p>
</div>
</div>
<!-- Main Content Area -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Events Timeline -->
<div class="lg:col-span-2">
<div class="card">
<div class="p-6 border-b">
<div class="flex items-center justify-between">
<h2 class="text-lg font-semibold text-gray-800">Recent Events</h2>
<div class="flex items-center space-x-3">
<button id="refresh-events" class="text-blue-600 hover:text-blue-800 flex items-center space-x-1">
<i class="fas fa-sync-alt"></i>
<span>Refresh</span>
</button>
<button id="export-events" class="text-green-600 hover:text-green-800 flex items-center space-x-1">
<i class="fas fa-file-export"></i>
<span>Export</span>
</button>
</div>
</div>
</div>
<div class="p-6">
<div class="event-timeline">
<!-- Event items will be populated by JavaScript -->
<div id="events-container" class="space-y-6">
<!-- Sample event item (will be generated by JS) -->
<div class="event-item">
<div class="bg-gray-50 rounded-lg p-4">
<div class="flex items-start justify-between">
<div class="flex-1">
<div class="flex items-center space-x-3">
<span class="severity-badge severity-high">
<i class="fas fa-exclamation-circle"></i>
<span>High</span>
</span>
<span class="text-sm text-gray-500">2023-11-15 14:32:17</span>
</div>
<h3 class="font-semibold text-gray-800 mt-2">High CPU load on
web-server-01</h3>
<p class="text-sm text-gray-600 mt-1">CPU load has been over 90%
for the last 5 minutes</p>
<div class="mt-3 flex flex-wrap gap-2">
<span class="px-2 py-1 bg-gray-100 text-xs rounded-full">web-server-01</span>
<span class="px-2 py-1 bg-gray-100 text-xs rounded-full">CPU</span>
<span class="px-2 py-1 bg-gray-100 text-xs rounded-full">Performance</span>
</div>
</div>
<button class="analyze-btn text-blue-600 hover:text-blue-800 px-3 py-1 rounded-md text-sm font-medium" data-event-id="1">
<i class="fas fa-brain mr-1"></i> Analyze
</button>
</div>
</div>
</div>
</div>
</div>
<div class="mt-8 flex justify-center">
<button id="load-more" class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-4 py-2 rounded-md">
Load More Events
</button>
</div>
</div>
</div>
</div>
<!-- Analysis Panel -->
<div class="lg:col-span-1">
<div class="card h-full">
<div class="p-6 border-b">
<h2 class="text-lg font-semibold text-gray-800">AI Analysis</h2>
<p class="text-sm text-gray-500">Powered by Ollama</p>
</div>
<div class="p-6">
<div id="analysis-placeholder" class="text-center py-12">
<div
class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fas fa-brain text-blue-600 text-2xl"></i>
</div>
<h3 class="text-gray-800 font-medium">Select an event to analyze</h3>
<p class="text-gray-500 text-sm mt-1">Click the "Analyze" button on any event to get
AI-powered insights</p>
</div>
<div id="analysis-result" class="hidden">
<div class="flex items-center justify-between mb-4">
<h3 class="font-semibold text-gray-800">Analysis Results</h3>
<span class="text-sm text-gray-500" id="analysis-time">Just now</span>
</div>
<div class="space-y-4">
<div>
<p class="text-sm font-medium text-gray-500 mb-1">Root Cause</p>
<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>
</div>
<div>
<p class="text-sm font-medium text-gray-500 mb-1">Recommended Actions</p>
<ul class="space-y-2 text-gray-800" id="analysis-actions">
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span>Check web server access logs for traffic patterns</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span>Optimize slow database queries</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span>Consider adding more web server instances</span>
</li>
</ul>
</div>
<div>
<p class="text-sm font-medium text-gray-500 mb-1">Confidence Level</p>
<div class="flex items-center space-x-2">
<div class="w-full bg-gray-200 rounded-full h-2">
<div class="bg-blue-600 h-2 rounded-full" style="width: 85%"></div>
</div>
<span class="text-sm font-medium text-gray-800">85%</span>
</div>
</div>
<div class="pt-4 border-t">
<p class="text-sm font-medium text-gray-500 mb-2">Additional Context</p>
<div class="space-y-2 text-sm text-gray-600" id="analysis-context">
<p>Similar events occurred 3 times in the last week during peak hours.
</p>
<p>The database server shows correlated high load during these events.
</p>
<p>No recent deployment changes that would explain this behavior.</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Loading Overlay -->
<div id="loading-overlay" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
<div class="bg-white rounded-lg p-6 w-80 text-center">
<div class="flex justify-center mb-4">
<div class="loading-spinner"></div>
</div>
<h3 class="text-lg font-semibold text-gray-800 mb-2">Analyzing Event</h3>
<p class="text-gray-600">Ollama is processing the event data and generating insights...</p>
<p class="text-sm text-gray-500 mt-2">This may take a few seconds</p>
</div>
</div>
<!-- CSV Processing Overlay -->
<div id="csv-processing-overlay" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
<div class="bg-white rounded-lg p-6 w-96 text-center">
<div class="flex justify-center mb-4">
<div class="w-16 h-16 border-4 border-blue-200 border-t-blue-600 rounded-full animate-spin"></div>
</div>
<h3 class="text-lg font-semibold text-gray-800 mb-2" id="csv-processing-title">Processing CSV File</h3>
<p class="text-gray-600 mb-4" id="csv-processing-message">Parsing Zabbix events from the uploaded file...</p>
<div class="w-full bg-gray-200 rounded-full h-3 mb-2">
<div id="csv-processing-progress" class="bg-blue-600 h-3 rounded-full" style="width: 0%"></div>
</div>
<p class="text-sm text-gray-500" id="csv-processing-percentage">0% Complete</p>
</div>
</div>
<script>
// Mock data for events
let eventsData = [
{
id: 1,
timestamp: "2023-11-15 14:32:17",
severity: "high",
name: "High CPU load on web-server-01",
description: "CPU load has been over 90% for the last 5 minutes",
host: "web-server-01",
tags: ["web-server-01", "CPU", "Performance"],
analyzed: false
},
{
id: 2,
timestamp: "2023-11-15 13:45:22",
severity: "disaster",
name: "Database connection failed",
description: "Primary database server is not responding to connection requests",
host: "db-primary-01",
tags: ["db-primary-01", "Database", "Connection"],
analyzed: false
},
{
id: 3,
timestamp: "2023-11-15 12:10:08",
severity: "average",
name: "Disk space warning on backup server",
description: "Backup storage is at 85% capacity",
host: "backup-server-01",
tags: ["backup-server-01", "Storage", "Disk"],
analyzed: false
},
{
id: 4,
timestamp: "2023-11-15 11:22:45",
severity: "warning",
name: "High memory usage on API server",
description: "Memory usage exceeds 90% threshold",
host: "api-server-02",
tags: ["api-server-02", "Memory", "Performance"],
analyzed: false
},
{
id: 5,
timestamp: "2023-11-15 10:15:33",
severity: "info",
name: "Scheduled maintenance completed",
description: "Nightly maintenance tasks finished successfully",
host: "monitoring-server",
tags: ["monitoring-server", "Maintenance", "Scheduled"],
analyzed: false
},
{
id: 6,
timestamp: "2023-11-15 09:05:12",
severity: "high",
name: "Network latency detected",
description: "Increased latency between data centers",
host: "network-core-01",
tags: ["network-core-01", "Network", "Latency"],
analyzed: false
}
];
// Mock analysis data
const mockAnalysis = {
rootCause: "The high CPU load is likely caused by a sudden spike in web traffic combined with inefficient database queries in the application.",
actions: [
"Check web server access logs for traffic patterns",
"Optimize slow database queries",
"Consider adding more web server instances"
],
confidence: 85,
context: [
"Similar events occurred 3 times in the last week during peak hours.",
"The database server shows correlated high load during these events.",
"No recent deployment changes that would explain this behavior."
]
};
// DOM Elements
const eventsContainer = document.getElementById('events-container');
const analysisPlaceholder = document.getElementById('analysis-placeholder');
const analysisResult = document.getElementById('analysis-result');
const loadingOverlay = document.getElementById('loading-overlay');
const csvProcessingOverlay = document.getElementById('csv-processing-overlay');
const menuBtn = document.getElementById('menu-btn');
const sidebar = document.getElementById('sidebar');
const refreshBtn = document.getElementById('refresh-events');
const loadMoreBtn = document.getElementById('load-more');
const uploadCsvBtn = document.getElementById('upload-csv-btn');
const csvUploadArea = document.getElementById('csv-upload-area');
const csvFileInput = document.getElementById('csv-file-input');
const filePreview = document.getElementById('file-preview');
const fileName = document.getElementById('file-name');
const fileSize = document.getElementById('file-size');
const parseCsvBtn = document.getElementById('parse-csv-btn');
const cancelUploadBtn = document.getElementById('cancel-upload-btn');
const parseProgress = document.getElementById('parse-progress');
const progressBar = document.getElementById('progress-bar');
const parsePercentage = document.getElementById('parse-percentage');
const csvProcessingProgress = document.getElementById('csv-processing-progress');
const csvProcessingPercentage = document.getElementById('csv-processing-percentage');
const csvProcessingTitle = document.getElementById('csv-processing-title');
const csvProcessingMessage = document.getElementById('csv-processing-message');
// Stats elements
const totalEventsEl = document.getElementById('total-events');
const criticalEventsEl = document.getElementById('critical-events');
const analyzedEventsEl = document.getElementById('analyzed-events');
const activeTriggersEl = document.getElementById('active-triggers');
// Initialize the app
function init() {
renderEvents();
setupEventListeners();
updateStats();
}
// Update statistics
function updateStats() {
const totalEvents = eventsData.length;
const criticalEvents = eventsData.filter(e => e.severity === 'disaster' || e.severity === 'high').length;
const analyzedEvents = eventsData.filter(e => e.analyzed).length;
const activeTriggers = Math.floor(totalEvents * 0.1); // Mock calculation
totalEventsEl.textContent = totalEvents.toLocaleString();
criticalEventsEl.textContent = criticalEvents.toLocaleString();
analyzedEventsEl.textContent = analyzedEvents.toLocaleString();
activeTriggersEl.textContent = activeTriggers.toLocaleString();
}
// Render events to the timeline
function renderEvents() {
eventsContainer.innerHTML = '';
// Sort events by timestamp (newest first)
const sortedEvents = [...eventsData].sort((a, b) => {
return new Date(b.timestamp) - new Date(a.timestamp);
});
// For demo purposes, we'll show all events
sortedEvents.forEach(event => {
const eventElement = createEventElement(event);
eventsContainer.appendChild(eventElement);
});
}
// Create an event element
function createEventElement(event) {
const eventDiv = document.createElement('div');
eventDiv.className = 'event-item';
eventDiv.innerHTML = `
<div class="bg-gray-50 rounded-lg p-4">
<div class="flex items-start justify-between">
<div class="flex-1">
<div class="flex items-center space-x-3">
<span class="severity-badge severity-${event.severity}">
<i class="fas fa-exclamation-circle"></i>
<span>${capitalizeFirstLetter(event.severity)}</span>
</span>
<span class="text-sm text-gray-500">${event.timestamp}</span>
</div>
<h3 class="font-semibold text-gray-800 mt-2">${event.name}</h3>
<p class="text-sm text-gray-600 mt-1">${event.description}</p>
<div class="mt-3 flex flex-wrap gap-2">
${event.tags.map(tag => `
<span class="px-2 py-1 bg-gray-100 text-xs rounded-full">${tag}</span>
`).join('')}
</div>
</div>
<button class="analyze-btn text-blue-600 hover:text-blue-800 px-3 py-1 rounded-md text-sm font-medium" data-event-id="${event.id}">
<i class="fas fa-brain mr-1"></i> Analyze
</button>
</div>
</div>
`;
return eventDiv;
}
// Setup event listeners
function setupEventListeners() {
// Mobile menu toggle
menuBtn.addEventListener('click', () => {
sidebar.classList.toggle('open');
});
// Close sidebar when clicking outside on mobile
document.addEventListener('click', (e) => {
if (window.innerWidth < 768 && !sidebar.contains(e.target) && !menuBtn.contains(e.target)) {
sidebar.classList.remove('open');
}
});
// Analyze button click handler (event delegation)
eventsContainer.addEventListener('click', (e) => {
if (e.target.closest('.analyze-btn')) {
const eventId = parseInt(e.target.closest('.analyze-btn').dataset.eventId);
analyzeEvent(eventId);
}
});
// Refresh events button
refreshBtn.addEventListener('click', () => {
showNotification("Events refreshed", "success");
});
// Load more events button
loadMoreBtn.addEventListener('click', () => {
showNotification("No more events to load", "info");
});
// Apply filters button
document.getElementById('apply-filters').addEventListener('click', () => {
showNotification("Filters applied", "success");
});
// CSV Upload functionality
uploadCsvBtn.addEventListener('click', () => {
csvFileInput.click();
});
csvUploadArea.addEventListener('click', () => {
csvFileInput.click();
});
// Drag and drop functionality
csvUploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
csvUploadArea.classList.add('active');
});
csvUploadArea.addEventListener('dragleave', () => {
csvUploadArea.classList.remove('active');
});
csvUploadArea.addEventListener('drop', (e) => {
e.preventDefault();
csvUploadArea.classList.remove('active');
if (e.dataTransfer.files.length) {
handleFileSelect(e.dataTransfer.files[0]);
}
});
csvFileInput.addEventListener('change', (e) => {
if (e.target.files.length) {
handleFileSelect(e.target.files[0]);
}
});
parseCsvBtn.addEventListener('click', () => {
parseCsvFile();
});
cancelUploadBtn.addEventListener('click', () => {
resetFileUpload();
});
}
// Handle file selection
function handleFileSelect(file) {
if (file.type !== 'text/csv' && !file.name.endsWith('.csv')) {
showNotification("Please upload a valid CSV file", "error");
return;
}
filePreview.classList.remove('hidden');
fileName.textContent = file.name;
fileSize.textContent = formatFileSize(file.size);
}
// Format file size
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Parse CSV file
function parseCsvFile() {
const file = csvFileInput.files[0];
if (!file) return;
// Show processing overlay
csvProcessingOverlay.classList.remove('hidden');
csvProcessingTitle.textContent = "Processing CSV File";
csvProcessingMessage.textContent = "Parsing Zabbix events from the uploaded file...";
// Simulate parsing progress
let progress = 0;
const interval = setInterval(() => {
progress += 5;
csvProcessingProgress.style.width = `${progress}%`;
csvProcessingPercentage.textContent = `${progress}% Complete`;
if (progress >= 100) {
clearInterval(interval);
// Simulate successful parsing
setTimeout(() => {
processMockCsvData();
csvProcessingOverlay.classList.add('hidden');
resetFileUpload();
showNotification("CSV file processed successfully! 12 new events imported.", "success");
}, 500);
}
}, 100);
}
// Process mock CSV data (simulating real CSV parsing)
function processMockCsvData() {
// Add some mock events to simulate CSV import
const newEvents = [
{
id: eventsData.length + 1,
timestamp: "2023-11-16 08:15:22",
severity: "high",
name: "Disk I/O latency on database server",
description: "Disk response time exceeds 100ms threshold",
host: "db-primary-01",
tags: ["db-primary-01", "Disk", "Performance"],
analyzed: false
},
{
id: eventsData.length + 2,
timestamp: "2023-11-16 07:42:11",
severity: "warning",
name: "High network traffic on firewall",
description: "Network throughput exceeds 80% of capacity",
host: "firewall-01",
tags: ["firewall-01", "Network", "Traffic"],
analyzed: false
},
{
id: eventsData.length + 3,
timestamp: "2023-11-16 07:18:04",
severity: "average",
name: "Memory usage warning on app server",
description: "Memory usage at 75% capacity",
host: "app-server-03",
tags: ["app-server-03", "Memory", "Performance"],
analyzed: false
}
];
// Add new events to the data
eventsData.push(...newEvents);
// Update the UI
renderEvents();
updateStats();
}
// Reset file upload UI
function resetFileUpload() {
csvFileInput.value = '';
filePreview.classList.add('hidden');
parseProgress.classList.add('hidden');
progressBar.style.width = '0%';
parsePercentage.textContent = '0%';
}
// Simulate analyzing an event with Ollama
function analyzeEvent(eventId) {
// Show loading overlay
loadingOverlay.classList.remove('hidden');
// Hide placeholder and show analysis after a delay (simulating API call)
setTimeout(() => {
// Find the event
const event = eventsData.find(e => e.id === eventId);
// Update the analysis result with mock data
document.getElementById('analysis-root-cause').textContent = mockAnalysis.rootCause;
const actionsList = document.getElementById('analysis-actions');
actionsList.innerHTML = mockAnalysis.actions.map(action => `
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span>${action}</span>
</li>
`).join('');
document.getElementById('analysis-time').textContent = "Just now";
document.getElementById('analysis-context').innerHTML = mockAnalysis.context.map(item => `
<p>${item}</p>
`).join('');
// Update confidence level
const confidenceBar = analysisResult.querySelector('.bg-blue-600');
confidenceBar.style.width = `${mockAnalysis.confidence}%`;
confidenceBar.nextElementSibling.textContent = `${mockAnalysis.confidence}%`;
// Show analysis result
analysisPlaceholder.classList.add('hidden');
analysisResult.classList.remove('hidden');
// Hide loading overlay
loadingOverlay.classList.add('hidden');
// Mark event as analyzed
event.analyzed = true;
const analyzeBtn = document.querySelector(`.analyze-btn[data-event-id="${eventId}"]`);
analyzeBtn.innerHTML = '<i class="fas fa-check-circle mr-1"></i> Analyzed';
analyzeBtn.classList.remove('text-blue-600', 'hover:text-blue-800');
analyzeBtn.classList.add('text-green-600', 'cursor-default');
analyzeBtn.removeAttribute('data-event-id');
// Update stats
updateStats();
showNotification("Event analyzed successfully", "success");
}, 1500);
}
// Helper function to capitalize first letter
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
// Show notification
function showNotification(message, type) {
const notification = document.createElement('div');
notification.className = `fixed bottom-4 right-4 px-4 py-2 rounded-md shadow-lg text-white ${
type === 'success' ? 'bg-green-600' :
type === 'error' ? 'bg-red-600' :
type === 'warning' ? 'bg-yellow-600' : 'bg-blue-600'