// WiFi Configuration Component class WiFiConfigComponent extends Component { constructor(container, viewModel, eventBus) { super(container, viewModel, eventBus); logger.debug('WiFiConfigComponent: Constructor called'); logger.debug('WiFiConfigComponent: Container:', container); // Track form state this.formValid = false; } mount() { logger.debug('WiFiConfigComponent: Mounting...'); super.mount(); this.setupFormValidation(); this.setupApplyButton(); this.setupProgressDisplay(); // Initial validation to ensure button starts disabled this.validateForm(); logger.debug('WiFiConfigComponent: Mounted successfully'); } setupFormValidation() { logger.debug('WiFiConfigComponent: Setting up form validation...'); const ssidInput = this.findElement('#wifi-ssid'); const passwordInput = this.findElement('#wifi-password'); const applyBtn = this.findElement('#apply-wifi-config'); if (!ssidInput || !passwordInput || !applyBtn) { logger.error('WiFiConfigComponent: Required form elements not found'); return; } // Add input event listeners this.addEventListener(ssidInput, 'input', this.validateForm.bind(this)); this.addEventListener(passwordInput, 'input', this.validateForm.bind(this)); // Initial validation this.validateForm(); } setupApplyButton() { logger.debug('WiFiConfigComponent: Setting up apply button...'); const applyBtn = this.findElement('#apply-wifi-config'); if (applyBtn) { this.addEventListener(applyBtn, 'click', this.handleApply.bind(this)); logger.debug('WiFiConfigComponent: Apply button event listener added'); } else { logger.error('WiFiConfigComponent: Apply button not found'); } } setupProgressDisplay() { logger.debug('WiFiConfigComponent: Setting up progress display...'); // Subscribe to view model changes this.viewModel.subscribe('isConfiguring', (isConfiguring) => { this.updateApplyButton(isConfiguring); }); this.viewModel.subscribe('configProgress', (progress) => { this.updateProgressDisplay(progress); }); this.viewModel.subscribe('configResults', (results) => { this.updateResultsDisplay(results); }); } validateForm() { logger.debug('WiFiConfigComponent: Validating form...'); const ssidInput = this.findElement('#wifi-ssid'); const passwordInput = this.findElement('#wifi-password'); const applyBtn = this.findElement('#apply-wifi-config'); if (!ssidInput || !passwordInput || !applyBtn) { return; } const ssid = ssidInput.value.trim(); const password = passwordInput.value.trim(); this.formValid = ssid.length > 0 && password.length > 0; // Update apply button state applyBtn.disabled = !this.formValid; // Update view model this.viewModel.setCredentials(ssid, password); logger.debug('WiFiConfigComponent: Form validation complete. Valid:', this.formValid); } updateApplyButton(isConfiguring) { logger.debug('WiFiConfigComponent: Updating apply button. Configuring:', isConfiguring); const applyBtn = this.findElement('#apply-wifi-config'); if (!applyBtn) { return; } if (isConfiguring) { applyBtn.disabled = true; applyBtn.classList.add('loading'); applyBtn.innerHTML = `Apply`; } else { applyBtn.disabled = !this.formValid; applyBtn.classList.remove('loading'); applyBtn.innerHTML = `Apply`; } } updateProgressDisplay(progress) { logger.debug('WiFiConfigComponent: Updating progress display:', progress); const progressContainer = this.findElement('#wifi-progress-container'); if (!progressContainer || !progress) { return; } const { current, total, status } = progress; const percentage = total > 0 ? Math.round((current / total) * 100) : 0; progressContainer.innerHTML = `
${current}/${total} Configured (${percentage}%)
Status: ${status}
`; } showProgressBar(totalNodes) { logger.debug('WiFiConfigComponent: Showing initial progress bar for', totalNodes, 'nodes'); const progressContainer = this.findElement('#wifi-progress-container'); if (!progressContainer) { return; } progressContainer.innerHTML = `
0/${totalNodes} Configured (0%)
Status: Preparing configuration...
`; } updateResultsDisplay(results) { logger.debug('WiFiConfigComponent: Updating results display:', results); const progressContainer = this.findElement('#wifi-progress-container'); if (!progressContainer || !results || results.length === 0) { return; } const resultsHtml = results.map(result => `
${result.node.hostname || result.node.ip} ${result.node.ip}
${result.success ? 'Success' : 'Failed'}
${result.error ? `
${this.escapeHtml(result.error)}
` : ''}
`).join(''); // Append results to existing progress container const existingProgress = progressContainer.querySelector('.upload-progress-info'); if (existingProgress) { existingProgress.innerHTML += `
${resultsHtml}
`; } } async handleApply() { logger.debug('WiFiConfigComponent: Apply button clicked'); if (!this.formValid) { logger.warn('WiFiConfigComponent: Form is not valid, cannot apply'); return; } const ssid = this.findElement('#wifi-ssid').value.trim(); const password = this.findElement('#wifi-password').value.trim(); const targetNodes = this.viewModel.get('targetNodes'); logger.debug('WiFiConfigComponent: Applying WiFi config to', targetNodes.length, 'nodes'); logger.debug('WiFiConfigComponent: SSID:', ssid); // Start configuration this.viewModel.startConfiguration(); // Show initial progress bar this.showProgressBar(targetNodes.length); try { // Update progress this.viewModel.updateConfigProgress(0, targetNodes.length, 'Starting configuration...'); // Apply configuration to each node for (let i = 0; i < targetNodes.length; i++) { const node = targetNodes[i]; try { logger.debug('WiFiConfigComponent: Configuring node:', node.ip); // Update progress this.viewModel.updateConfigProgress(i + 1, targetNodes.length, `Configuring ${node.hostname || node.ip}...`); // Make API call to configure WiFi const result = await this.configureNodeWiFi(node, ssid, password); // Add successful result this.viewModel.addConfigResult({ node, success: true, error: null }); logger.debug('WiFiConfigComponent: Successfully configured node:', node.ip); } catch (error) { logger.error('WiFiConfigComponent: Failed to configure node:', node.ip, error); // Add failed result this.viewModel.addConfigResult({ node, success: false, error: error.message || 'Configuration failed' }); } // Small delay between requests to avoid overwhelming the nodes if (i < targetNodes.length - 1) { await new Promise(resolve => setTimeout(resolve, 500)); } } // Complete configuration this.viewModel.updateConfigProgress(targetNodes.length, targetNodes.length, 'Configuration complete'); this.viewModel.completeConfiguration(); logger.debug('WiFiConfigComponent: WiFi configuration completed'); } catch (error) { logger.error('WiFiConfigComponent: WiFi configuration failed:', error); this.viewModel.completeConfiguration(); } } async configureNodeWiFi(node, ssid, password) { logger.debug('WiFiConfigComponent: Configuring WiFi for node:', node.ip); const response = await window.apiClient.callEndpoint({ ip: node.ip, method: 'POST', uri: '/api/network/wifi/config', params: [ { name: 'ssid', value: ssid, location: 'body' }, { name: 'password', value: password, location: 'body' } ] }); // Check if the API call was successful based on the response structure if (!response || response.status !== 200) { const errorMessage = response?.data?.message || response?.error || 'Failed to configure WiFi'; throw new Error(errorMessage); } return response; } unmount() { logger.debug('WiFiConfigComponent: Unmounting...'); super.unmount(); logger.debug('WiFiConfigComponent: Unmounted'); } } window.WiFiConfigComponent = WiFiConfigComponent;