// 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 += `
`;
}
}
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' }
]
});
if (!response.success) {
throw new Error(response.error || 'Failed to configure WiFi');
}
return response;
}
unmount() {
logger.debug('WiFiConfigComponent: Unmounting...');
super.unmount();
logger.debug('WiFiConfigComponent: Unmounted');
}
}
window.WiFiConfigComponent = WiFiConfigComponent;