// Main SPORE UI Application // Initialize the application when DOM is loaded document.addEventListener('DOMContentLoaded', function() { console.log('=== SPORE UI Application Initialization ==='); // Initialize the framework (but don't navigate yet) console.log('App: Creating framework instance...'); const app = window.app; // Create view models console.log('App: Creating view models...'); const clusterViewModel = new ClusterViewModel(); const firmwareViewModel = new FirmwareViewModel(); const topologyViewModel = new TopologyViewModel(); console.log('App: View models created:', { clusterViewModel, firmwareViewModel, topologyViewModel }); // Connect firmware view model to cluster data clusterViewModel.subscribe('members', (members) => { console.log('App: Members subscription triggered:', members); if (members && members.length > 0) { // Extract node information for firmware view const nodes = members.map(member => ({ ip: member.ip, hostname: member.hostname || member.ip, labels: member.labels || {} })); firmwareViewModel.updateAvailableNodes(nodes); console.log('App: Updated firmware view model with nodes:', nodes); } else { firmwareViewModel.updateAvailableNodes([]); console.log('App: Cleared firmware view model nodes'); } }); // Register routes with their view models console.log('App: Registering routes...'); app.registerRoute('cluster', ClusterViewComponent, 'cluster-view', clusterViewModel); app.registerRoute('topology', TopologyGraphComponent, 'topology-view', topologyViewModel); app.registerRoute('firmware', FirmwareViewComponent, 'firmware-view', firmwareViewModel); console.log('App: Routes registered and components pre-initialized'); // Initialize cluster status component for header badge console.log('App: Initializing cluster status component...'); const clusterStatusComponent = new ClusterStatusComponent( document.querySelector('.cluster-status'), clusterViewModel, app.eventBus ); clusterStatusComponent.mount(); console.log('App: Cluster status component initialized'); // Set up navigation event listeners console.log('App: Setting up navigation...'); app.setupNavigation(); // Now navigate to the default route console.log('App: Navigating to default route...'); app.navigateTo('cluster'); console.log('=== SPORE UI Application initialization completed ==='); }); // Burger menu toggle for mobile (function setupBurgerMenu(){ document.addEventListener('DOMContentLoaded', function(){ const nav = document.querySelector('.main-navigation'); const burger = document.getElementById('burger-btn'); const navLeft = nav ? nav.querySelector('.nav-left') : null; if (!nav || !burger || !navLeft) return; burger.addEventListener('click', function(e){ e.preventDefault(); nav.classList.toggle('mobile-open'); }); // Close menu when a nav tab is clicked navLeft.addEventListener('click', function(e){ const btn = e.target.closest('.nav-tab'); if (btn && nav.classList.contains('mobile-open')) { nav.classList.remove('mobile-open'); } }); // Close menu on outside click document.addEventListener('click', function(e){ if (!nav.contains(e.target) && nav.classList.contains('mobile-open')) { nav.classList.remove('mobile-open'); } }); }); })(); // Set up periodic updates with state preservation function setupPeriodicUpdates() { // Auto-refresh cluster members every 30 seconds using smart update setInterval(() => { if (window.app.currentView && window.app.currentView.viewModel) { const viewModel = window.app.currentView.viewModel; // Use smart update if available, otherwise fall back to regular update if (viewModel.smartUpdate && typeof viewModel.smartUpdate === 'function') { console.log('App: Performing smart update to preserve UI state...'); viewModel.smartUpdate(); } else if (viewModel.updateClusterMembers && typeof viewModel.updateClusterMembers === 'function') { console.log('App: Performing regular update...'); viewModel.updateClusterMembers(); } } }, 30000); // Update primary node display every 10 seconds (this is lightweight and doesn't affect UI state) setInterval(() => { if (window.app.currentView && window.app.currentView.viewModel) { const viewModel = window.app.currentView.viewModel; if (viewModel.updatePrimaryNodeDisplay && typeof viewModel.updatePrimaryNodeDisplay === 'function') { viewModel.updatePrimaryNodeDisplay(); } } }, 10000); } // Global error handler window.addEventListener('error', function(event) { console.error('Global error:', event.error); }); // Global unhandled promise rejection handler window.addEventListener('unhandledrejection', function(event) { console.error('Unhandled promise rejection:', event.reason); }); // Clean up on page unload window.addEventListener('beforeunload', function() { if (window.app) { console.log('App: Cleaning up cached components...'); window.app.cleanup(); } });