Microservice Health Dashboard

Microservice Health Dashboard

Service Overview

All Microservices

Service Name Status Latency (ms) Error Rate (%) Uptime (%) Last Checked Notes

Configure Your Services

Manually input or edit your microservice health data below. All changes will automatically update the dashboard.

Service Name Status Latency (ms) Error Rate (%) Uptime (%) Last Checked Notes Actions

${formatPercentage(overallMetrics.overallUptime)}

`; } /** * Renders the all microservices table in the Dashboard tab. */ function renderAllServicesTable() { // Null check for the all services table body if (!allServicesTableBody) { console.error("All services table body not found."); return; } allServicesTableBody.innerHTML = ''; // Clear existing rows serviceData.forEach(service => { const row = document.createElement('tr'); row.innerHTML = ` ${service.serviceName} ${service.status} ${service.latencyMs.toLocaleString()} ${formatPercentage(service.errorRatePercent)} ${formatPercentage(service.uptimePercent)} ${service.lastChecked || 'N/A'} ${service.notes || 'N/A'} `; allServicesTableBody.appendChild(row); }); } /** * Renders the data input table in the Service Configuration tab. */ function renderDataInputTable() { // Null check for the data input table body if (!dataInputTableBody) { console.error("Data input table body not found."); return; } dataInputTableBody.innerHTML = ''; // Clear existing rows serviceData.forEach(service => { const row = document.createElement('tr'); row.innerHTML = ` `; dataInputTableBody.appendChild(row); }); // Attach event listeners to newly created input fields and delete buttons document.querySelectorAll('.service-input').forEach(input => { input.addEventListener('change', handleInputChange); }); document.querySelectorAll('.delete-row-btn').forEach(button => { button.addEventListener('click', handleDeleteRow); }); } /** * Updates all dashboard and data configuration views. */ function updateViews() { renderServiceSummary(); renderAllServicesTable(); renderDataInputTable(); } // --- Event Handlers --- /** * Handles tab button clicks to switch between tabs. * @param {Event} event - The click event. */ function handleTabClick(event) { // Null check for event target if (!event.target) { console.error("Event target is null."); return; } const targetTab = event.target.dataset.tab; // Remove 'active' class from all tab buttons and content tabButtons.forEach(button => button.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); // Add 'active' class to the clicked tab button and corresponding content event.target.classList.add('active'); const activeTabContent = document.getElementById(`${targetTab}-tab`); if (activeTabContent) { // Null check for activeTabContent activeTabContent.classList.add('active'); } else { console.error(`Tab content for ${targetTab} not found.`); } updateViews(); // Update views whenever tab changes } /** * Handles changes in data input fields. * @param {Event} event - The change event. */ function handleInputChange(event) { // Null check for event target if (!event.target) { console.error("Event target is null."); return; } const input = event.target; const id = parseInt(input.dataset.id); const field = input.dataset.field; let value = input.value; // Convert numeric fields to numbers if (['latencyMs', 'errorRatePercent', 'uptimePercent'].includes(field)) { value = parseFloat(value) || 0; // Default to 0 if parsing fails if (value < 0) value = 0; // Ensure non-negative values if (field === 'uptimePercent' && value > 100) value = 100; // Uptime max 100% if (field === 'errorRatePercent' && value > 100) value = 100; // Error Rate max 100% input.value = value; // Update input field with sanitized value } const serviceIndex = serviceData.findIndex(d => d.id === id); if (serviceIndex !== -1) { serviceData[serviceIndex][field] = value; updateViews(); // Re-render dashboard and data input tables } else { console.error(`Service with ID ${id} not found.`); } } /** * Adds a new empty row to the data input table. */ function handleAddService() { const newService = { id: nextId++, serviceName: 'New Service', status: 'Operational', latencyMs: 0, errorRatePercent: 0.00, uptimePercent: 100.00, lastChecked: new Date().toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true, year: 'numeric', month: '2-digit', day: '2-digit' }), notes: '' }; serviceData.push(newService); updateViews(); // Re-render data input table } /** * Deletes a row from the data input table. * @param {Event} event - The click event. */ function handleDeleteRow(event) { // Null check for event target if (!event.target) { console.error("Event target is null."); return; } const idToDelete = parseInt(event.target.dataset.id); serviceData = serviceData.filter(d => d.id !== idToDelete); updateViews(); // Re-render data input table } /** * Handles navigation to the previous tab. */ function handlePrevTab() { const currentActiveTab = document.querySelector('.tab-button.active'); if (!currentActiveTab) { console.error("No active tab found."); return; } const currentIndex = Array.from(tabButtons).indexOf(currentActiveTab); if (currentIndex > 0) { tabButtons[currentIndex - 1].click(); // Simulate click on previous tab } } /** * Handles navigation to the next tab. */ function handleNextTab() { const currentActiveTab = document.querySelector('.tab-button.active'); if (!currentActiveTab) { console.error("No active tab found."); return; } const currentIndex = Array.from(tabButtons).indexOf(currentActiveTab); if (currentIndex < tabButtons.length - 1) { tabButtons[currentIndex + 1].click(); // Simulate click on next tab } } /** * Handles the PDF download functionality. * It uses html2canvas to capture the dashboard content and jsPDF to generate the PDF. */ async function handleDownloadPdf() { const dashboardContent = document.querySelector('.dashboard-content-for-pdf'); if (!dashboardContent) { console.error("Dashboard content for PDF not found."); return; } // Add a temporary title for the PDF const tempTitle = document.createElement('h1'); tempTitle.textContent = "Microservice Health Dashboard"; tempTitle.style.textAlign = 'center'; tempTitle.style.marginBottom = '20px'; tempTitle.style.fontSize = '2em'; tempTitle.style.fontWeight = 'bold'; tempTitle.style.color = '#333'; dashboardContent.prepend(tempTitle); // Prepend to capture // Temporarily hide tab navigation and buttons for PDF capture const elementsToHide = document.querySelectorAll('.tab-nav, .tab-navigation-buttons, .add-row-btn, .delete-row-btn, .pdf-download-section .btn-primary'); elementsToHide.forEach(el => el.style.display = 'none'); // Ensure the dashboard tab is active before capturing for PDF const dashboardTabButton = document.querySelector('.tab-button[data-tab="dashboard"]'); if (dashboardTabButton && !dashboardTabButton.classList.contains('active')) { dashboardTabButton.click(); // Activate dashboard tab if not already active // A small delay might be needed here if content takes time to render after tab switch await new Promise(resolve => setTimeout(resolve, 100)); } try { const canvas = await html2canvas(dashboardContent, { scale: 2, // Increase scale for better resolution useCORS: true, // Required if images are from different origin (though we don't use external images) windowWidth: dashboardContent.scrollWidth, windowHeight: dashboardContent.scrollHeight }); const imgData = canvas.toDataURL('image/png'); const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'landscape', // Landscape for wider tables unit: 'px', format: 'a4' }); const imgWidth = pdf.internal.pageSize.getWidth(); const pageHeight = pdf.internal.pageSize.getHeight(); const imgHeight = canvas.height * imgWidth / canvas.width; let heightLeft = imgHeight; let position = 0; pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight); heightLeft -= pageHeight; while (heightLeft >= 0) { position = heightLeft - imgHeight; pdf.addPage(); pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight); heightLeft -= pageHeight; } pdf.save('microservice_health_dashboard.pdf'); } catch (error) { console.error("Error generating PDF:", error); // Using alert as a fallback for critical error feedback, as per standard specification alert("Failed to generate PDF. Please try again."); } finally { // Restore hidden elements and remove temporary title elementsToHide.forEach(el => el.style.display = ''); if (tempTitle.parentNode) { tempTitle.parentNode.removeChild(tempTitle); } } } // --- Event Listener Attachments --- tabButtons.forEach(button => { // Null check for button if (button) { button.addEventListener('click', handleTabClick); } }); // Null checks before attaching listeners if (addServiceBtn) { addServiceBtn.addEventListener('click', handleAddService); } else { console.error("Add Service button not found."); } if (downloadPdfBtn) { downloadPdfBtn.addEventListener('click', handleDownloadPdf); } else { console.error("Download PDF button not found."); } if (prevTabBtn) { prevTabBtn.addEventListener('click', handlePrevTab); } else { console.error("Previous Tab button not found."); } if (nextTabBtn) { nextTabBtn.addEventListener('click', handleNextTab); } else { console.error("Next Tab button not found."); } // Initial render of the dashboard and data input table updateViews(); });
Scroll to Top