// Dashboard JavaScript class ResourceDashboard { constructor() { this.connection = null; this.cpuChart = null; this.memoryChart = null; this.cpuHistory = []; this.memoryHistory = []; this.maxHistoryPoints = 20; this.lastResourceData = null; // Store latest resource data this.lastSystemInfo = null; // Store latest system info this.autoRefreshEnabled = true; // Auto-refresh toggle this.refreshInterval = null; // Manual refresh interval this.init(); } async init() { this.setupEventListeners(); this.initializeCharts(); await this.connectSignalR(); await this.loadInitialData(); this.hideLoading(); this.startAutoRefresh(); // Use our new auto-refresh system } setupEventListeners() { document.getElementById('toggleAutoRefresh').addEventListener('click', () => { this.toggleAutoRefresh(); }); document.getElementById('toggleProcesses').addEventListener('click', () => { this.toggleProcessesSection(); }); document.getElementById('toggleDetails').addEventListener('click', () => { this.toggleDetailsSection(); }); document.getElementById('refreshData').addEventListener('click', () => { this.refreshData(); }); } toggleDetailsSection() { const detailsSection = document.getElementById('detailsSection'); const toggleButton = document.getElementById('toggleDetails'); if (detailsSection.classList.contains('hidden')) { detailsSection.classList.remove('hidden'); toggleButton.innerHTML = 'Hide Details'; // Refresh disk usage when details section becomes visible this.refreshDetailsData(); } else { detailsSection.classList.add('hidden'); toggleButton.innerHTML = 'Details'; } } toggleProcessesSection() { const processesSection = document.getElementById('processesSection'); const toggleButton = document.getElementById('toggleProcesses'); if (processesSection.classList.contains('hidden')) { processesSection.classList.remove('hidden'); toggleButton.innerHTML = 'Hide Processes'; // Refresh processes when section becomes visible this.refreshProcessesData(); } else { processesSection.classList.add('hidden'); toggleButton.innerHTML = 'Processes'; } } async refreshProcessesData() { try { // If we have cached data, use it immediately if (this.lastResourceData && this.lastResourceData.topProcesses) { this.updateProcessTable(this.lastResourceData.topProcesses); } // Then fetch fresh data const resourceUsage = await this.fetchData('/api/resource/usage'); this.updateProcessTable(resourceUsage.topProcesses); } catch (error) { console.error('Error refreshing processes data:', error); } } async refreshDetailsData() { try { // If we have cached data, use it immediately if (this.lastResourceData && this.lastResourceData.disks) { this.updateDiskUsage(this.lastResourceData.disks); } if (this.lastSystemInfo) { this.updateSystemInfo(this.lastSystemInfo); } // Then fetch fresh data const [resourceUsage, systemInfo] = await Promise.all([ this.fetchData('/api/resource/usage'), this.fetchData('/api/resource/system-info') ]); this.updateDiskUsage(resourceUsage.disks); this.updateSystemInfo(systemInfo); } catch (error) { console.error('Error refreshing details data:', error); } } toggleAutoRefresh() { this.autoRefreshEnabled = !this.autoRefreshEnabled; const toggleButton = document.getElementById('toggleAutoRefresh'); if (this.autoRefreshEnabled) { toggleButton.innerHTML = 'Auto: ON'; toggleButton.className = 'bg-yellow-500 hover:bg-yellow-700 px-4 py-2 rounded-lg transition-colors'; this.startAutoRefresh(); } else { toggleButton.innerHTML = 'Auto: OFF'; toggleButton.className = 'bg-gray-500 hover:bg-gray-700 px-4 py-2 rounded-lg transition-colors'; this.stopAutoRefresh(); } console.log(`Auto-refresh ${this.autoRefreshEnabled ? 'enabled' : 'disabled'}`); } startAutoRefresh() { // Connect SignalR for real-time updates if (!this.connection || this.connection.state === 'Disconnected') { this.connectSignalR(); } // Also start a fallback manual refresh timer (every 60 seconds as backup) if (this.refreshInterval) { clearInterval(this.refreshInterval); } this.refreshInterval = setInterval(() => { if (this.autoRefreshEnabled) { this.refreshData(); } }, 60000); // 60 second fallback } stopAutoRefresh() { // Disconnect SignalR if (this.connection && this.connection.state === 'Connected') { this.connection.stop(); } // Clear manual refresh timer if (this.refreshInterval) { clearInterval(this.refreshInterval); this.refreshInterval = null; } } async connectSignalR() { try { // Only connect if auto-refresh is enabled if (!this.autoRefreshEnabled) { console.log("SignalR connection skipped - auto-refresh disabled"); return; } this.connection = new signalR.HubConnectionBuilder() .withUrl("/resourceHub") .build(); this.connection.on("ResourceUpdate", (data) => { if (this.autoRefreshEnabled) { this.updateDashboard(data); } }); await this.connection.start(); console.log("SignalR Connected"); } catch (err) { console.error("SignalR Connection Error: ", err); } } async loadInitialData() { try { const [resourceUsage, systemInfo] = await Promise.all([ this.fetchData('/api/resource/usage'), this.fetchData('/api/resource/system-info') ]); this.updateDashboard(resourceUsage); this.updateSystemInfo(systemInfo); } catch (error) { console.error('Error loading initial data:', error); } } async fetchData(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } updateDashboard(data) { try { // Store the latest data for when details section is opened this.lastResourceData = data; // Update CPU if (data.cpu) { const cpuUsage = data.cpu.usage || 0; document.getElementById('cpuUsage').textContent = `${cpuUsage.toFixed(1)}%`; document.getElementById('cpuBar').style.width = `${cpuUsage}%`; this.updateCpuChart(cpuUsage); } // Update Memory if (data.memory) { const memUsage = data.memory.usagePercentage || 0; document.getElementById('memoryUsage').textContent = `${memUsage.toFixed(1)}%`; document.getElementById('memoryBar').style.width = `${memUsage}%`; this.updateMemoryChart(memUsage); } // Update GPU if (data.gpu) { const gpuUsage = data.gpu.usage || 0; document.getElementById('gpuUsage').textContent = `${gpuUsage.toFixed(1)}%`; document.getElementById('gpuBar').style.width = `${gpuUsage}%`; } // Update Network if (data.network) { const bytesReceived = data.network.bytesReceived || 0; const bytesSent = data.network.bytesSent || 0; const totalSpeed = (bytesReceived + bytesSent) / 1024 / 1024; document.getElementById('networkSpeed').textContent = `${totalSpeed.toFixed(1)} MB/s`; document.getElementById('networkDetail').textContent = `↑ ${(bytesSent / 1024 / 1024).toFixed(1)} MB/s ↓ ${(bytesReceived / 1024 / 1024).toFixed(1)} MB/s`; } // Update Game Detection if (data.runningGame) { this.updateGameInfo(data.runningGame); } else { this.updateGameInfo(null); } // Update Processes (only if processes section is visible) if (data.topProcesses && Array.isArray(data.topProcesses) && !document.getElementById('processesSection').classList.contains('hidden')) { this.updateProcessTable(data.topProcesses); } // Update Disk Usage (only if details section is visible) if (data.disks && !document.getElementById('detailsSection').classList.contains('hidden')) { this.updateDiskUsage(data.disks); } } catch (error) { console.error('Error updating dashboard:', error); this.showNotification('Error updating dashboard data', 'error'); } } updateProcessTable(processes) { const tableBody = document.getElementById('processTable'); tableBody.innerHTML = ''; if (!processes || !Array.isArray(processes)) { const row = document.createElement('tr'); row.innerHTML = '
${systemInfo.machineName || 'N/A'}
${systemInfo.osVersion || 'N/A'}
${systemInfo.cpuName || 'N/A'}
${systemInfo.processorCount || 0} cores
${systemInfo.totalPhysicalMemory ? (systemInfo.totalPhysicalMemory / 1024 / 1024 / 1024).toFixed(1) : 'N/A'} GB
${systemInfo.uptime ? this.formatUptime(systemInfo.uptime) : 'N/A'}
No disk information available
'; return; } disks.forEach(disk => { if (!disk || !disk.totalSize || !disk.freeSpace) return; const usagePercentage = ((disk.totalSize - disk.freeSpace) / disk.totalSize * 100); const diskName = disk.label ? `${disk.driveLetter} (${disk.label})` : disk.driveLetter || 'Unknown Drive'; const diskDiv = document.createElement('div'); diskDiv.className = 'bg-gray-50 p-4 rounded-lg'; diskDiv.innerHTML = `No game currently running
Running since ${this.formatGameStartTime(gameInfo.startTime)}
${gameInfo.processId || 'N/A'}
${gameInfo.memoryUsage ? (gameInfo.memoryUsage / 1024 / 1024).toFixed(1) + ' MB' : 'N/A'}
${gameInfo.cpuUsage ? gameInfo.cpuUsage.toFixed(1) + '%' : 'N/A'}