aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--static/js/main.js341
1 files changed, 227 insertions, 114 deletions
diff --git a/static/js/main.js b/static/js/main.js
index b06bfee..55de443 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -1,6 +1,25 @@
// --- JWT Constants ---
const JWT_TOKEN_KEY = 'metrics_jwt_token';
+// --- Global DOM Elements ---
+// These will be properly initialized in DOMContentLoaded
+let chartModal = null;
+let chartModalTitle = null;
+let closeChartModalBtn = null;
+let closeChartBtn = null;
+let expandedChart = null;
+let metricsSection = null;
+let viewMetricsBtn = null;
+let passwordModal = null;
+let passwordInput = null;
+let submitPasswordBtn = null;
+let closeModalBtn = null;
+let passwordError = null;
+let metricsControls = null;
+
+// --- DOM Ready Flag ---
+let domReady = false;
+
// --- Server Stats Logic ---
async function getServerStats() {
const statusElement = document.getElementById('server-status');
@@ -95,14 +114,8 @@ function updateServiceStatusDisplay() {
}
// --- Metrics Authentication & Display Logic ---
-const viewMetricsBtn = document.getElementById('view-metrics-btn');
-const passwordModal = document.getElementById('password-modal');
-const passwordInput = document.getElementById('metrics-password');
-const submitPasswordBtn = document.getElementById('submit-password');
-const closeModalBtn = document.querySelector('.close-button');
-const passwordError = document.getElementById('password-error');
-const metricsSection = document.getElementById('metrics-section');
-const metricsControls = document.getElementById('metrics-controls'); // Container for buttons
+// Constants and variables will now be initialized when DOM is ready
+// Functions stay decoupled from global DOM element references
function getToken() {
return localStorage.getItem(JWT_TOKEN_KEY);
@@ -309,91 +322,125 @@ function calculateRates(data) {
}
function createChartContainers(metrics) {
- const metricGrid = document.querySelector('.metric-grid');
- if (!metricGrid) {
+ if (!domReady) {
+ console.warn("DOM not ready for creating chart containers");
+ // Wait a bit and try again
+ setTimeout(() => createChartContainers(metrics), 300);
return;
}
- metricGrid.innerHTML = ''; // Clear previous
- charts = {}; // Clear chart objects
+ if (!metricsSection) {
+ metricsSection = document.getElementById('metrics-section');
+ if (!metricsSection) {
+ console.error("Metrics section not found in DOM");
+ return;
+ }
+ }
- // Group the metrics into two sections
- const networkAndSystemMetrics = {};
- const appPerformanceMetrics = {};
- const serviceStatusMetrics = {};
+ const metricGrid = metricsSection.querySelector('.metric-grid');
+ if (!metricGrid) {
+ console.error("Metric grid not found in DOM");
+ return;
+ }
- // First categorize the metrics
- for (const [metricName, definition] of Object.entries(metrics)) {
- // Skip system_uptime and memory metrics (as requested)
- if (metricName === 'system_uptime' ||
- metricName.includes('memory_total') ||
- metricName === 'memory_size') {
- continue;
- }
+ try {
+ metricGrid.innerHTML = ''; // Clear previous
+ charts = {}; // Clear chart objects
- // Categorize based on metric name
- if (metricName.startsWith('app_')) {
- appPerformanceMetrics[metricName] = definition;
- } else if (metricName.startsWith('service_')) {
- serviceStatusMetrics[metricName] = definition;
- } else {
- networkAndSystemMetrics[metricName] = definition;
+ // Group the metrics into two sections
+ const networkAndSystemMetrics = {};
+ const appPerformanceMetrics = {};
+ const serviceStatusMetrics = {};
+
+ // First categorize the metrics
+ for (const [metricName, definition] of Object.entries(metrics)) {
+ // Skip system_uptime and memory metrics (as requested)
+ if (metricName === 'system_uptime' ||
+ metricName.includes('memory_total') ||
+ metricName === 'memory_size') {
+ continue;
+ }
+
+ // Categorize based on metric name
+ if (metricName.startsWith('app_')) {
+ appPerformanceMetrics[metricName] = definition;
+ } else if (metricName.startsWith('service_')) {
+ serviceStatusMetrics[metricName] = definition;
+ } else {
+ networkAndSystemMetrics[metricName] = definition;
+ }
}
- }
-
- // Create section headers and containers
- if (Object.keys(networkAndSystemMetrics).length > 0) {
- const sectionHeader = document.createElement('h4');
- sectionHeader.textContent = 'Network & System Metrics';
- sectionHeader.className = 'metric-section-header';
- metricGrid.appendChild(sectionHeader);
- // Create containers for network & system metrics
- for (const [metricName, definition] of Object.entries(networkAndSystemMetrics)) {
- createMetricCard(metricGrid, metricName, definition);
+ // Create section headers and containers
+ if (Object.keys(networkAndSystemMetrics).length > 0) {
+ const sectionHeader = document.createElement('h4');
+ sectionHeader.textContent = 'Network & System Metrics';
+ sectionHeader.className = 'metric-section-header';
+ metricGrid.appendChild(sectionHeader);
+
+ // Create containers for network & system metrics
+ for (const [metricName, definition] of Object.entries(networkAndSystemMetrics)) {
+ createMetricCard(metricGrid, metricName, definition);
+ }
}
- }
-
- // Application Performance section
- if (Object.keys(appPerformanceMetrics).length > 0) {
- const sectionHeader = document.createElement('h4');
- sectionHeader.textContent = 'Application Performance';
- sectionHeader.className = 'metric-section-header';
- metricGrid.appendChild(sectionHeader);
- // Create containers for app performance metrics
- for (const [metricName, definition] of Object.entries(appPerformanceMetrics)) {
- createMetricCard(metricGrid, metricName, definition);
+ // Application Performance section
+ if (Object.keys(appPerformanceMetrics).length > 0) {
+ const sectionHeader = document.createElement('h4');
+ sectionHeader.textContent = 'Application Performance';
+ sectionHeader.className = 'metric-section-header';
+ metricGrid.appendChild(sectionHeader);
+
+ // Create containers for app performance metrics
+ for (const [metricName, definition] of Object.entries(appPerformanceMetrics)) {
+ createMetricCard(metricGrid, metricName, definition);
+ }
}
+ } catch (error) {
+ console.error("Error creating chart containers:", error);
}
-
- // Service Status section (not displayed as a chart - we already added it to server stats)
- // We skip adding charts for service_* metrics
}
function createMetricCard(container, metricName, definition) {
- const displayName = definition.label || metricName;
- const metricCard = document.createElement('div');
- metricCard.className = 'metric-card';
- metricCard.innerHTML = `<h5>${displayName}</h5><div class="chart-container" data-metric="${metricName}" data-label="${displayName}"><canvas id="${metricName}Chart"></canvas></div>`;
- container.appendChild(metricCard);
-
- // Add click event for chart expansion
- const chartContainer = metricCard.querySelector('.chart-container');
- if (chartContainer) {
- chartContainer.addEventListener('click', function() {
- try {
- const metricAttr = this.getAttribute('data-metric');
- const labelAttr = this.getAttribute('data-label');
- if (metricAttr && labelAttr && typeof expandChart === 'function') {
- expandChart(metricAttr, labelAttr);
+ if (!container) return;
+
+ try {
+ const displayName = definition.label || metricName;
+ const metricCard = document.createElement('div');
+ metricCard.className = 'metric-card';
+ metricCard.innerHTML = `<h5>${displayName}</h5><div class="chart-container" data-metric="${metricName}" data-label="${displayName}"><canvas id="${metricName}Chart"></canvas></div>`;
+ container.appendChild(metricCard);
+
+ // Add click event for chart expansion
+ const chartContainer = metricCard.querySelector('.chart-container');
+ if (chartContainer) {
+ chartContainer.addEventListener('click', function() {
+ try {
+ const metricAttr = this.getAttribute('data-metric');
+ const labelAttr = this.getAttribute('data-label');
+ if (metricAttr && labelAttr && typeof expandChart === 'function') {
+ // Wait until DOM is ready before trying to expand
+ if (domReady) {
+ expandChart(metricAttr, labelAttr);
+ } else {
+ console.warn("DOM not ready for chart expansion, delaying...");
+ // Delay expansion until DOM is ready
+ setTimeout(() => {
+ if (typeof expandChart === 'function') {
+ expandChart(metricAttr, labelAttr);
+ }
+ }, 500);
+ }
+ }
+ } catch (error) {
+ console.error("Error expanding chart:", error);
+ // Fallback if there's an error - show a simple alert
+ alert(`Could not expand the ${displayName} chart. Please try again later.`);
}
- } catch (error) {
- console.error("Error expanding chart:", error);
- // Fallback if there's an error - show a simple alert
- alert(`Could not expand the ${displayName} chart. Please try again later.`);
- }
- });
+ });
+ }
+ } catch (error) {
+ console.error("Error creating metric card:", error);
}
}
@@ -750,21 +797,19 @@ function handleMetricsVisibilityChange() {
}
// Chart expansion functionality
-let chartModal;
-let chartModalTitle;
-let closeChartModalBtn;
-let closeChartBtn;
-let expandedChart = null;
-
function expandChart(metricName, displayName) {
- // Make sure the modal elements are initialized
- if (!chartModal || !chartModalTitle) {
- chartModal = document.getElementById('chart-modal');
- chartModalTitle = document.getElementById('chart-modal-title');
- closeChartModalBtn = document.querySelector('#chart-modal .close-button');
- closeChartBtn = document.getElementById('close-chart-modal');
+ // Safety check - if DOM is not ready, we can't expand the chart yet
+ if (!domReady) {
+ console.warn("Cannot expand chart, DOM not ready yet");
+ return;
}
+ // Make sure the modal elements are initialized
+ if (!chartModal) chartModal = document.getElementById('chart-modal');
+ if (!chartModalTitle) chartModalTitle = document.getElementById('chart-modal-title');
+ if (!closeChartModalBtn) closeChartModalBtn = document.querySelector('#chart-modal .close-button');
+ if (!closeChartBtn) closeChartBtn = document.getElementById('close-chart-modal');
+
// Safety check - if elements still not found, exit
if (!chartModal || !chartModalTitle) {
console.error("Chart modal elements not found");
@@ -793,7 +838,11 @@ function expandChart(metricName, displayName) {
// If there's already an expanded chart, destroy it first
if (expandedChart) {
- expandedChart.destroy();
+ try {
+ expandedChart.destroy();
+ } catch (error) {
+ console.warn("Error destroying previous chart:", error);
+ }
}
// Clone the options and data from the original chart
@@ -803,47 +852,95 @@ function expandChart(metricName, displayName) {
return;
}
- const chartOptions = JSON.parse(JSON.stringify(originalChart.options));
- // Adjust options for the expanded view
- chartOptions.maintainAspectRatio = false;
- if (chartOptions.scales && chartOptions.scales.y) {
- chartOptions.scales.y.ticks.maxTicksLimit = 10; // More ticks for expanded view
+ try {
+ const chartOptions = JSON.parse(JSON.stringify(originalChart.options));
+ // Adjust options for the expanded view
+ chartOptions.maintainAspectRatio = false;
+ if (chartOptions.scales && chartOptions.scales.y) {
+ chartOptions.scales.y.ticks.maxTicksLimit = 10; // More ticks for expanded view
+ }
+
+ // Create the expanded chart with cloned data
+ const chartData = {
+ datasets: originalChart.data.datasets.map(dataset => ({
+ ...dataset,
+ data: [...dataset.data],
+ pointRadius: 3 // Show points in expanded view
+ }))
+ };
+
+ expandedChart = new Chart(ctx, {
+ type: 'line',
+ data: chartData,
+ options: chartOptions
+ });
+ } catch (error) {
+ console.error("Error creating expanded chart:", error);
}
-
- // Create the expanded chart with cloned data
- const chartData = {
- datasets: originalChart.data.datasets.map(dataset => ({
- ...dataset,
- data: [...dataset.data],
- pointRadius: 3 // Show points in expanded view
- }))
- };
-
- expandedChart = new Chart(ctx, {
- type: 'line',
- data: chartData,
- options: chartOptions
- });
}
// Close modal events
function closeChartModal() {
+ if (!domReady) {
+ console.warn("Cannot close chart modal, DOM not ready yet");
+ return;
+ }
+
if (!chartModal) {
chartModal = document.getElementById('chart-modal');
- if (!chartModal) return;
+ if (!chartModal) {
+ console.error("Chart modal element not found");
+ return;
+ }
}
- chartModal.classList.remove('show');
- setTimeout(() => chartModal.style.display = 'none', 300);
+ try {
+ chartModal.classList.remove('show');
+ setTimeout(() => {
+ if (chartModal) chartModal.style.display = 'none';
+ }, 300);
+ } catch (error) {
+ console.error("Error closing chart modal:", error);
+ }
}
// --- Initialize everything when DOM is loaded ---
document.addEventListener('DOMContentLoaded', function() {
- // Initialize chart modal elements
+ // Set DOM ready flag
+ domReady = true;
+
+ // Initialize all DOM elements
chartModal = document.getElementById('chart-modal');
chartModalTitle = document.getElementById('chart-modal-title');
closeChartModalBtn = document.querySelector('#chart-modal .close-button');
closeChartBtn = document.getElementById('close-chart-modal');
+ metricsSection = document.getElementById('metrics-section');
+ viewMetricsBtn = document.getElementById('view-metrics-btn');
+ passwordModal = document.getElementById('password-modal');
+ passwordInput = document.getElementById('metrics-password');
+ submitPasswordBtn = document.getElementById('submit-password');
+ passwordError = document.getElementById('password-error');
+ metricsControls = document.getElementById('metrics-controls');
+
+ // Log any missing critical elements
+ if (!chartModal) console.warn("Chart modal element not found in DOM");
+ if (!chartModalTitle) console.warn("Chart modal title element not found in DOM");
+ if (!metricsSection) console.warn("Metrics section element not found in DOM");
+ if (!passwordModal) console.warn("Password modal element not found in DOM");
+
+ // For debugging - log all initialized elements
+ console.log("DOM elements initialized:", {
+ chartModal: !!chartModal,
+ chartModalTitle: !!chartModalTitle,
+ closeChartModalBtn: !!closeChartModalBtn,
+ closeChartBtn: !!closeChartBtn,
+ metricsSection: !!metricsSection,
+ viewMetricsBtn: !!viewMetricsBtn,
+ passwordModal: !!passwordModal
+ });
+
+ // Dispatch a custom event to indicate DOM is ready
+ document.dispatchEvent(new Event('dom-fully-ready'));
// Start server stats check
getServerStats();
@@ -929,4 +1026,20 @@ document.addEventListener('DOMContentLoaded', function() {
}
}
});
+});
+
+// Ensure chart elements are initialized when DOM is fully ready
+document.addEventListener('dom-fully-ready', function() {
+ // Initialize chart elements again to be extra safe
+ chartModal = document.getElementById('chart-modal');
+ chartModalTitle = document.getElementById('chart-modal-title');
+ closeChartModalBtn = document.querySelector('#chart-modal .close-button');
+ closeChartBtn = document.getElementById('close-chart-modal');
+
+ console.log("Chart elements initialized on dom-fully-ready event:", {
+ chartModal: !!chartModal,
+ chartModalTitle: !!chartModalTitle,
+ closeChartModalBtn: !!closeChartModalBtn,
+ closeChartBtn: !!closeChartBtn
+ });
}); \ No newline at end of file