Telemetry Dashboard

Telemetry Dashboard

Monitor real-time application performance and event metrics.

Real-Time System Usage

Event Types (Last 24h)

Recent Event Log

${currentCpu.toFixed(1)}%

Memory Usage

${currentMemory.toFixed(1)}%

Errors (24h)

${errorCount}

Active Users

${activeUsers}

`; }; const renderSystemUsageChart = () => { const ctx = document.getElementById('systemUsageChart').getContext('2d'); if (systemUsageChart) systemUsageChart.destroy(); systemUsageChart = new Chart(ctx, { type: 'line', data: { labels: systemUsageData.labels, datasets: [ { label: 'CPU (%)', data: systemUsageData.cpu, borderColor: '#8b5cf6', backgroundColor: 'rgba(139, 92, 246, 0.1)', fill: true, tension: 0.3, yAxisID: 'yCpu' }, { label: 'Memory (%)', data: systemUsageData.memory, borderColor: '#10b981', backgroundColor: 'rgba(16, 185, 129, 0.1)', fill: true, tension: 0.3, yAxisID: 'yMemory' } ] }, options: { responsive: true, maintainAspectRatio: false, scales: { x: { type: 'time', time: { unit: 'minute', displayFormats: { minute: 'HH:mm' } } }, yCpu: { type: 'linear', position: 'left', min: 0, max: 100, title: { display: true, text: 'CPU (%)' } }, yMemory: { type: 'linear', position: 'right', min: 0, max: 100, title: { display: true, text: 'Memory (%)' }, grid: { drawOnChartArea: false } } } } }); }; const renderEventTypesChart = () => { const ctx = document.getElementById('eventTypesChart').getContext('2d'); const eventTypes = ['INFO', 'WARN', 'ERROR']; const typeCounts = eventTypes.map(type => events.filter(e => e.type === type).length); if (eventTypesChart) eventTypesChart.destroy(); eventTypesChart = new Chart(ctx, { type: 'doughnut', data: { labels: eventTypes, datasets: [{ data: typeCounts, backgroundColor: ['#3b82f6', '#f59e0b', '#ef4444'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } } } }); }; const renderEventLogTable = () => { const container = document.getElementById('event-log-container'); const sortedEvents = [...events].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)); let tableHtml = ``; sortedEvents.forEach(event => { let typeClass = event.type === 'ERROR' ? 'bg-red-100 text-red-800' : event.type === 'WARN' ? 'bg-amber-100 text-amber-800' : 'bg-blue-100 text-blue-800'; tableHtml += ``; }); tableHtml += `
Timestamp Type Source Message
${formatTimestamp(event.timestamp)} ${event.type} ${event.source} ${event.message}
`; container.innerHTML = tableHtml; }; const renderEventListEditor = () => { const container = document.getElementById('event-list-editor').querySelector('.space-y-2'); container.innerHTML = events.map(event => `

${event.source}: ${event.message}

${formatTimestamp(event.timestamp)} | Type: ${event.type}

`).join(''); }; // --- EVENT HANDLERS --- document.getElementById('event-form').addEventListener('submit', e => { e.preventDefault(); const id = document.getElementById('event-id').value; const newEvent = { timestamp: new Date(document.getElementById('event-timestamp').value).toISOString(), type: document.getElementById('event-type').value, source: document.getElementById('event-source').value, message: document.getElementById('event-message').value, }; if (id) { const index = events.findIndex(i => i.id == id); events[index] = { ...events[index], ...newEvent }; } else { newEvent.id = events.length > 0 ? Math.max(...events.map(i => i.id)) + 1 : 1; events.push(newEvent); } resetForm(); renderAll(); }); document.getElementById('event-list-editor').addEventListener('click', e => { if (e.target.classList.contains('edit-btn')) { const id = e.target.dataset.id; const event = events.find(i => i.id == id); if (event) populateFormForEdit(event); } if (e.target.classList.contains('delete-btn')) { const id = e.target.dataset.id; if (confirm('Are you sure you want to delete this event?')) { events = events.filter(i => i.id != id); renderAll(); } } }); document.getElementById('cancel-edit-btn').addEventListener('click', resetForm); document.getElementById('download-pdf-btn').addEventListener('click', () => { const exportArea = document.getElementById('dashboard-export-area'); const mainTitleEl = document.querySelector('#telemetry-tool-container h1'); if (!exportArea || !mainTitleEl) return; const btn = document.getElementById('download-pdf-btn'); const originalBtnText = btn.innerHTML; btn.disabled = true; btn.innerHTML = `Processing...`; html2canvas(exportArea, { scale: 2, useCORS: true, windowWidth: exportArea.scrollWidth, windowHeight: exportArea.scrollHeight }) .then(canvas => { const { jsPDF } = window.jspdf; const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'landscape', unit: 'mm', format: 'a4' }); const pageMargin = 15; const pdfPageWidth = pdf.internal.pageSize.getWidth(); const pdfPageHeight = pdf.internal.pageSize.getHeight(); const pdfContentWidth = pdfPageWidth - (2 * pageMargin); const canvasAspectRatio = canvas.width / canvas.height; const pdfImgHeight = pdfContentWidth / canvasAspectRatio; let yPos = pageMargin; pdf.setFontSize(18).setFont('helvetica', 'bold'); pdf.text(mainTitleEl.innerText, pdfPageWidth / 2, yPos, { align: 'center' }); yPos += 12; let heightLeft = pdfImgHeight; let positionOnCanvas = 0; pdf.addImage(imgData, 'PNG', pageMargin, yPos, pdfContentWidth, pdfImgHeight); heightLeft -= (pdfPageHeight - yPos - pageMargin); while (heightLeft > 0) { positionOnCanvas -= (pdfPageHeight - (2 * pageMargin)); pdf.addPage(); pdf.addImage(imgData, 'PNG', pageMargin, positionOnCanvas + pageMargin, pdfContentWidth, pdfImgHeight); heightLeft -= (pdfPageHeight - (2 * pageMargin)); } pdf.save(`telemetry-dashboard-${new Date().toISOString().slice(0,10)}.pdf`); }) .catch(err => { console.error("PDF generation failed:", err); alert("Error generating PDF."); }) .finally(() => { btn.disabled = false; btn.innerHTML = originalBtnText; }); }); // --- HELPER FUNCTIONS --- function populateFormForEdit(event) { document.getElementById('event-id').value = event.id; // Format for datetime-local input: YYYY-MM-DDTHH:mm document.getElementById('event-timestamp').value = new Date(event.timestamp).toISOString().slice(0, 16); document.getElementById('event-type').value = event.type; document.getElementById('event-source').value = event.source; document.getElementById('event-message').value = event.message; document.getElementById('event-form').querySelector('button[type="submit"]').textContent = 'Update Event'; document.getElementById('cancel-edit-btn').classList.remove('hidden'); document.getElementById('event-form').scrollIntoView({ behavior: 'smooth' }); } function resetForm() { document.getElementById('event-form').reset(); document.getElementById('event-id').value = ''; document.getElementById('event-form').querySelector('button[type="submit"]').textContent = 'Add Event'; document.getElementById('cancel-edit-btn').classList.add('hidden'); } // --- INITIALIZATION --- const switchTab = (tabName) => { const tabDashboardBtn = document.getElementById('tab-dashboard-btn'); const tabConfigBtn = document.getElementById('tab-config-btn'); const tabDashboardContent = document.getElementById('tab-dashboard-content'); const tabConfigContent = document.getElementById('tab-config-content'); const prevTabBtn = document.getElementById('prev-tab-btn'); const nextTabBtn = document.getElementById('next-tab-btn'); if (tabName === 'dashboard') { tabDashboardBtn.classList.add('active'); tabConfigBtn.classList.remove('active'); tabDashboardContent.classList.remove('hidden'); tabConfigContent.classList.add('hidden'); prevTabBtn.disabled = true; nextTabBtn.disabled = false; if (!realTimeInterval) { realTimeInterval = setInterval(updateRealTimeData, 5000); } } else { tabDashboardBtn.classList.remove('active'); tabConfigBtn.classList.add('active'); tabDashboardContent.classList.add('hidden'); tabConfigContent.classList.remove('hidden'); prevTabBtn.disabled = false; nextTabBtn.disabled = true; if (realTimeInterval) { clearInterval(realTimeInterval); realTimeInterval = null; } } }; document.getElementById('tab-dashboard-btn').addEventListener('click', () => switchTab('dashboard')); document.getElementById('tab-config-btn').addEventListener('click', () => switchTab('config')); document.getElementById('prev-tab-btn').addEventListener('click', () => switchTab('dashboard')); document.getElementById('next-tab-btn').addEventListener('click', () => switchTab('config')); generateInitialData(); switchTab('dashboard'); renderAll(); });
Scroll to Top