feat: introduce global config dialog
This commit is contained in:
313
public/scripts/components/WiFiConfigComponent.js
Normal file
313
public/scripts/components/WiFiConfigComponent.js
Normal file
@@ -0,0 +1,313 @@
|
||||
// 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 = `
|
||||
<div class="upload-progress-info">
|
||||
<div class="overall-progress">
|
||||
<div class="progress-bar-container">
|
||||
<div class="progress-bar" id="wifi-progress-bar" style="width: ${percentage}%; background-color: #60a5fa;"></div>
|
||||
</div>
|
||||
<span class="progress-text">${current}/${total} Configured (${percentage}%)</span>
|
||||
</div>
|
||||
<div class="progress-summary" id="wifi-progress-summary">
|
||||
<span>Status: ${status}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
showProgressBar(totalNodes) {
|
||||
logger.debug('WiFiConfigComponent: Showing initial progress bar for', totalNodes, 'nodes');
|
||||
|
||||
const progressContainer = this.findElement('#wifi-progress-container');
|
||||
|
||||
if (!progressContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
progressContainer.innerHTML = `
|
||||
<div class="upload-progress-info">
|
||||
<div class="overall-progress">
|
||||
<div class="progress-bar-container">
|
||||
<div class="progress-bar" id="wifi-progress-bar" style="width: 0%; background-color: #60a5fa;"></div>
|
||||
</div>
|
||||
<span class="progress-text">0/${totalNodes} Configured (0%)</span>
|
||||
</div>
|
||||
<div class="progress-summary" id="wifi-progress-summary">
|
||||
<span>Status: Preparing configuration...</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
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 => `
|
||||
<div class="result-item ${result.success ? 'success' : 'error'}">
|
||||
<div class="result-node">
|
||||
<span class="node-name">${result.node.hostname || result.node.ip}</span>
|
||||
<span class="node-ip">${result.node.ip}</span>
|
||||
</div>
|
||||
<div class="result-status">
|
||||
<span class="status-indicator ${result.success ? 'success' : 'error'}">
|
||||
${result.success ? 'Success' : 'Failed'}
|
||||
</span>
|
||||
</div>
|
||||
${result.error ? `<div class="result-error">${this.escapeHtml(result.error)}</div>` : ''}
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Append results to existing progress container
|
||||
const existingProgress = progressContainer.querySelector('.upload-progress-info');
|
||||
if (existingProgress) {
|
||||
existingProgress.innerHTML += `
|
||||
<div class="results-section">
|
||||
<div class="results-list">
|
||||
${resultsHtml}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
Reference in New Issue
Block a user