feat: introduce overlay dialog component

This commit is contained in:
2025-10-16 22:00:19 +02:00
parent 478d23b805
commit 79a28bae22
7 changed files with 584 additions and 25 deletions

View File

@@ -7,6 +7,9 @@ class FirmwareComponent extends Component {
logger.debug('FirmwareComponent: Container:', container);
logger.debug('FirmwareComponent: Container ID:', container?.id);
// Initialize overlay dialog
this.overlayDialog = null;
// Check if the dropdown exists in the container
if (container) {
const dropdown = container.querySelector('#specific-node-select');
@@ -105,6 +108,9 @@ class FirmwareComponent extends Component {
logger.debug('FirmwareComponent: Mounting...');
// Initialize overlay dialog
this.initializeOverlayDialog();
// Check if the dropdown exists when mounted
const dropdown = this.findElement('#specific-node-select');
logger.debug('FirmwareComponent: Mount - dropdown found:', !!dropdown);
@@ -131,6 +137,24 @@ class FirmwareComponent extends Component {
this.updateDeployButton();
}
initializeOverlayDialog() {
// Create overlay container if it doesn't exist
let overlayContainer = document.getElementById('firmware-overlay-dialog');
if (!overlayContainer) {
overlayContainer = document.createElement('div');
overlayContainer.id = 'firmware-overlay-dialog';
overlayContainer.className = 'overlay-dialog';
document.body.appendChild(overlayContainer);
}
// Create and initialize the overlay dialog component
if (!this.overlayDialog) {
const overlayVM = new ViewModel();
this.overlayDialog = new OverlayDialogComponent(overlayContainer, overlayVM, this.eventBus);
this.overlayDialog.mount();
}
}
handleFileSelect(event) {
const file = event.target.files[0];
this.viewModel.setSelectedFile(file);
@@ -160,15 +184,69 @@ class FirmwareComponent extends Component {
const specificNode = this.viewModel.get('specificNode');
if (!file) {
alert('Please select a firmware file first.');
this.showConfirmationDialog({
title: 'No File Selected',
message: 'Please select a firmware file first.',
confirmText: 'OK',
cancelText: null,
onConfirm: () => {},
onCancel: null
});
return;
}
if (targetType === 'specific' && !specificNode) {
alert('Please select a specific node to update.');
this.showConfirmationDialog({
title: 'No Node Selected',
message: 'Please select a specific node to update.',
confirmText: 'OK',
cancelText: null,
onConfirm: () => {},
onCancel: null
});
return;
}
// Show confirmation dialog for deployment
this.showDeploymentConfirmation(file, targetType, specificNode);
}
showConfirmationDialog(options) {
if (!this.overlayDialog) {
this.initializeOverlayDialog();
}
this.overlayDialog.show(options);
}
showDeploymentConfirmation(file, targetType, specificNode) {
let title, message;
if (targetType === 'all') {
const nodes = this.viewModel.get('availableNodes') || [];
title = 'Deploy to All Nodes';
message = `Upload firmware "${file.name}" to all ${nodes.length} nodes?<br><br>This will update:<br>${nodes.map(n => `${n.hostname || n.ip}`).join('<br>')}`;
} else if (targetType === 'specific') {
title = 'Deploy to Specific Node';
message = `Upload firmware "${file.name}" to node ${specificNode}?`;
} else if (targetType === 'labels') {
const nodes = this.viewModel.getAffectedNodesByLabels();
const labels = this.viewModel.get('selectedLabels') || [];
title = 'Deploy to Labeled Nodes';
message = `Upload firmware "${file.name}" to ${nodes.length} node(s) matching labels (${labels.join(', ')})?<br><br>This will update:<br>${nodes.map(n => `${n.hostname || n.ip}`).join('<br>')}`;
}
this.showConfirmationDialog({
title: title,
message: message,
confirmText: 'Deploy',
cancelText: 'Cancel',
onConfirm: () => this.performDeployment(file, targetType, specificNode),
onCancel: () => {}
});
}
async performDeployment(file, targetType, specificNode) {
try {
this.viewModel.startUpload();
@@ -185,7 +263,14 @@ class FirmwareComponent extends Component {
} catch (error) {
logger.error('Firmware deployment failed:', error);
alert(`Deployment failed: ${error.message}`);
this.showConfirmationDialog({
title: 'Deployment Failed',
message: `Deployment failed: ${error.message}`,
confirmText: 'OK',
cancelText: null,
onConfirm: () => {},
onCancel: null
});
} finally {
this.viewModel.completeUpload();
}
@@ -198,13 +283,17 @@ class FirmwareComponent extends Component {
const nodes = response.members || [];
if (nodes.length === 0) {
alert('No nodes available for firmware update.');
this.showConfirmationDialog({
title: 'No Nodes Available',
message: 'No nodes available for firmware update.',
confirmText: 'OK',
cancelText: null,
onConfirm: () => {},
onCancel: null
});
return;
}
const confirmed = confirm(`Upload firmware to all ${nodes.length} nodes? This will update: ${nodes.map(n => n.hostname || n.ip).join(', ')}`);
if (!confirmed) return;
// Show upload progress area
this.showUploadProgress(file, nodes);
@@ -222,9 +311,6 @@ class FirmwareComponent extends Component {
async uploadToSpecificNode(file, nodeIp) {
try {
const confirmed = confirm(`Upload firmware to node ${nodeIp}?`);
if (!confirmed) return;
// Show upload progress area
this.showUploadProgress(file, [{ ip: nodeIp, hostname: nodeIp }]);
@@ -266,12 +352,16 @@ class FirmwareComponent extends Component {
try {
const nodes = this.viewModel.getAffectedNodesByLabels();
if (!nodes || nodes.length === 0) {
alert('No nodes match the selected labels.');
this.showConfirmationDialog({
title: 'No Matching Nodes',
message: 'No nodes match the selected labels.',
confirmText: 'OK',
cancelText: null,
onConfirm: () => {},
onCancel: null
});
return;
}
const labels = this.viewModel.get('selectedLabels') || [];
const confirmed = confirm(`Upload firmware to ${nodes.length} node(s) matching labels (${labels.join(', ')})?`);
if (!confirmed) return;
// Show upload progress area
this.showUploadProgress(file, nodes);