diff --git a/app/static/index.html b/app/static/index.html index 6cd09a1..a26903f 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -1730,6 +1730,15 @@ overcommitByNamespace: false } }; + + // Smart loading system + let loadingProgress = { + lastActivity: Date.now(), + timeoutId: null, + isActive: false, + progressSteps: 0, + totalSteps: 3 + }; // Initialize the application document.addEventListener('DOMContentLoaded', function() { @@ -1849,6 +1858,58 @@ } console.log(`Loading progress: ${progress}% (${loaded}/${total})`); } + + // Smart loading system functions + function resetLoadingTimeout() { + loadingProgress.lastActivity = Date.now(); + if (loadingProgress.timeoutId) { + clearTimeout(loadingProgress.timeoutId); + } + + // Set timeout only if no activity for 30 seconds + loadingProgress.timeoutId = setTimeout(() => { + if (loadingProgress.isActive) { + console.log('Loading timeout - no activity for 30 seconds'); + hideFullscreenLoading(); + showError('metrics-grid', 'Request timeout - API stopped responding'); + } + }, 30000); + } + + function updateSmartProgress(step, message = '') { + loadingProgress.progressSteps = step; + loadingProgress.lastActivity = Date.now(); + + // Update progress bar + const progress = Math.round((step / loadingProgress.totalSteps) * 100); + const progressBar = document.getElementById('loading-progress'); + if (progressBar) { + progressBar.style.width = progress + '%'; + } + + // Update status message if provided + if (message) { + console.log(`Loading progress: ${progress}% - ${message}`); + } + + // Reset timeout since we have activity + resetLoadingTimeout(); + } + + function startSmartLoading() { + loadingProgress.isActive = true; + loadingProgress.progressSteps = 0; + loadingProgress.lastActivity = Date.now(); + resetLoadingTimeout(); + } + + function stopSmartLoading() { + loadingProgress.isActive = false; + if (loadingProgress.timeoutId) { + clearTimeout(loadingProgress.timeoutId); + loadingProgress.timeoutId = null; + } + } function setupNavigation() { // Sidebar navigation @@ -1917,67 +1978,52 @@ } async function loadWorkloadScanner() { - let loadingModal = null; - let timeoutId = null; - try { // Show fullscreen loading modal - loadingModal = showFullscreenLoading( + showFullscreenLoading( 'Analyzing Cluster Resources', 'Please wait while we analyze your cluster resources and generate insights... This may take up to 60 seconds for large clusters.' ); - // Set timeout for loading (60 seconds) - timeoutId = setTimeout(() => { - hideFullscreenLoading(); - showError('metrics-grid', 'Request timeout - API is taking too long to respond'); - }, 60000); + // Start smart loading system + startSmartLoading(); - // Load cluster status with timeout - const controller = new AbortController(); - const timeoutController = setTimeout(() => controller.abort(), 50000); - - const clusterResponse = await fetch('/api/v1/cluster/status', { - signal: controller.signal - }); - clearTimeout(timeoutController); + // Step 1: Load cluster status + updateSmartProgress(0, 'Connecting to OpenShift API...'); + const clusterResponse = await fetch('/api/v1/cluster/status'); if (!clusterResponse.ok) { throw new Error(`HTTP error! status: ${clusterResponse.status}`); } const clusterData = await clusterResponse.json(); - - // Update progress - updateLoadingProgress(1, 3); + updateSmartProgress(1, 'Cluster data loaded successfully'); // Update metrics cards updateMetricsCards(clusterData); - // Update progress - updateLoadingProgress(2, 3); - - // Load dashboard charts with loading states + // Step 2: Load dashboard charts + updateSmartProgress(2, 'Loading dashboard charts...'); await loadDashboardCharts(); - // Update progress - updateLoadingProgress(3, 3); + // Step 3: Complete + updateSmartProgress(3, 'Analysis complete'); currentData = { cluster: clusterData }; - // Clear timeout and hide loading modal - clearTimeout(timeoutId); + // Stop smart loading and hide modal + stopSmartLoading(); setTimeout(() => { hideFullscreenLoading(); }, 500); } catch (error) { console.error('Error loading workload scanner data:', error); - if (timeoutId) clearTimeout(timeoutId); + stopSmartLoading(); hideFullscreenLoading(); if (error.name === 'AbortError') { - showError('metrics-grid', 'Request timeout - API is taking too long to respond'); + showError('metrics-grid', 'Request timeout - API stopped responding'); } else { showError('metrics-grid', 'Failed to load cluster data: ' + error.message); } @@ -2125,16 +2171,45 @@ // Dashboard Charts Functions async function loadDashboardCharts() { try { - // Load all charts in parallel - await Promise.all([ - loadResourceUtilizationTrend(), - loadNamespaceDistribution(), - loadIssuesTimeline(), - loadTopWorkloads(), - loadOvercommitByNamespace() - ]); + // Load all charts in parallel with individual error handling + const chartPromises = [ + loadResourceUtilizationTrend().catch(err => { + console.warn('Resource utilization trend failed:', err); + return null; + }), + loadNamespaceDistribution().catch(err => { + console.warn('Namespace distribution failed:', err); + return null; + }), + loadIssuesTimeline().catch(err => { + console.warn('Issues timeline failed:', err); + return null; + }), + loadTopWorkloads().catch(err => { + console.warn('Top workloads failed:', err); + return null; + }), + loadOvercommitByNamespace().catch(err => { + console.warn('Overcommit by namespace failed:', err); + return null; + }) + ]; - console.log('All dashboard charts loaded successfully'); + // Wait for all charts to complete (or fail gracefully) + await Promise.allSettled(chartPromises); + + // Update progress for each successful chart + let successCount = 0; + chartPromises.forEach((promise, index) => { + promise.then(result => { + if (result !== null) { + successCount++; + updateSmartProgress(2, `Loaded ${successCount}/5 charts successfully`); + } + }); + }); + + console.log('Dashboard charts loading completed'); } catch (error) { console.error('Error loading dashboard charts:', error); } @@ -2143,8 +2218,14 @@ // 1. Resource Utilization Trend (24h) async function loadResourceUtilizationTrend() { try { + // Update progress + updateSmartProgress(2, 'Loading resource utilization trend...'); + // Use real Prometheus data from historical analysis const response = await fetch('/api/v1/optimized/historical/summary'); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } const data = await response.json(); // Create trend data from real cluster metrics @@ -2272,7 +2353,13 @@ // 2. Namespace Resource Distribution async function loadNamespaceDistribution() { try { + // Update progress + updateSmartProgress(2, 'Loading namespace distribution...'); + const response = await fetch('/api/v1/namespace-distribution'); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } const data = await response.json(); // Pass data directly to chart function (no mapping needed)