Compare commits
1 Commits
65493b2c17
...
0b3f0cbca4
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b3f0cbca4 |
@@ -182,12 +182,6 @@ class ApiClient {
|
|||||||
return response.blob();
|
return response.blob();
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteFirmwareFromRegistry(name, version) {
|
|
||||||
return this.request(`/api/registry/firmware/${encodeURIComponent(name)}/${encodeURIComponent(version)}`, {
|
|
||||||
method: 'DELETE'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rollout API methods
|
// Rollout API methods
|
||||||
async getClusterNodeVersions() {
|
async getClusterNodeVersions() {
|
||||||
return this.request('/api/cluster/node/versions', { method: 'GET' });
|
return this.request('/api/cluster/node/versions', { method: 'GET' });
|
||||||
|
|||||||
@@ -53,11 +53,6 @@ class FirmwareComponent extends Component {
|
|||||||
logger.debug('FirmwareComponent: Mounted successfully');
|
logger.debug('FirmwareComponent: Mounted successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
unmount() {
|
|
||||||
this.cleanupDynamicListeners();
|
|
||||||
super.unmount();
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkRegistryConnection() {
|
async checkRegistryConnection() {
|
||||||
try {
|
try {
|
||||||
await window.apiClient.getRegistryHealth();
|
await window.apiClient.getRegistryHealth();
|
||||||
@@ -283,25 +278,19 @@ class FirmwareComponent extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setupFirmwareItemListeners() {
|
setupFirmwareItemListeners() {
|
||||||
// First, clean up existing listeners for dynamically created content
|
|
||||||
this.cleanupDynamicListeners();
|
|
||||||
this.dynamicUnsubscribers = [];
|
|
||||||
|
|
||||||
// Firmware group header clicks (for expand/collapse)
|
// Firmware group header clicks (for expand/collapse)
|
||||||
const groupHeaders = this.findAllElements('.firmware-group-header');
|
const groupHeaders = this.findAllElements('.firmware-group-header');
|
||||||
groupHeaders.forEach(header => {
|
groupHeaders.forEach(header => {
|
||||||
const handler = (e) => {
|
this.addEventListener(header, 'click', (e) => {
|
||||||
const group = header.closest('.firmware-group');
|
const group = header.closest('.firmware-group');
|
||||||
group.classList.toggle('expanded');
|
group.classList.toggle('expanded');
|
||||||
};
|
});
|
||||||
header.addEventListener('click', handler);
|
|
||||||
this.dynamicUnsubscribers.push(() => header.removeEventListener('click', handler));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Version item clicks (for editing)
|
// Version item clicks (for editing)
|
||||||
const versionItems = this.findAllElements('.firmware-version-item.clickable');
|
const versionItems = this.findAllElements('.firmware-version-item.clickable');
|
||||||
versionItems.forEach(item => {
|
versionItems.forEach(item => {
|
||||||
const handler = (e) => {
|
this.addEventListener(item, 'click', (e) => {
|
||||||
// Don't trigger if clicking on action buttons
|
// Don't trigger if clicking on action buttons
|
||||||
if (e.target.closest('.firmware-version-actions')) {
|
if (e.target.closest('.firmware-version-actions')) {
|
||||||
return;
|
return;
|
||||||
@@ -310,61 +299,44 @@ class FirmwareComponent extends Component {
|
|||||||
const name = item.getAttribute('data-name');
|
const name = item.getAttribute('data-name');
|
||||||
const version = item.getAttribute('data-version');
|
const version = item.getAttribute('data-version');
|
||||||
this.showEditFirmwareForm(name, version);
|
this.showEditFirmwareForm(name, version);
|
||||||
};
|
});
|
||||||
item.addEventListener('click', handler);
|
|
||||||
this.dynamicUnsubscribers.push(() => item.removeEventListener('click', handler));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Rollout buttons
|
// Rollout buttons
|
||||||
const rolloutBtns = this.findAllElements('.rollout-btn');
|
const rolloutBtns = this.findAllElements('.rollout-btn');
|
||||||
rolloutBtns.forEach(btn => {
|
rolloutBtns.forEach(btn => {
|
||||||
const handler = (e) => {
|
this.addEventListener(btn, 'click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const name = btn.getAttribute('data-name');
|
const name = btn.getAttribute('data-name');
|
||||||
const version = btn.getAttribute('data-version');
|
const version = btn.getAttribute('data-version');
|
||||||
const labels = JSON.parse(btn.getAttribute('data-labels') || '{}');
|
const labels = JSON.parse(btn.getAttribute('data-labels') || '{}');
|
||||||
this.showRolloutPanel(name, version, labels);
|
this.showRolloutPanel(name, version, labels);
|
||||||
};
|
});
|
||||||
btn.addEventListener('click', handler);
|
|
||||||
this.dynamicUnsubscribers.push(() => btn.removeEventListener('click', handler));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Download buttons
|
// Download buttons
|
||||||
const downloadBtns = this.findAllElements('.download-btn');
|
const downloadBtns = this.findAllElements('.download-btn');
|
||||||
downloadBtns.forEach(btn => {
|
downloadBtns.forEach(btn => {
|
||||||
const handler = (e) => {
|
this.addEventListener(btn, 'click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const name = btn.getAttribute('data-name');
|
const name = btn.getAttribute('data-name');
|
||||||
const version = btn.getAttribute('data-version');
|
const version = btn.getAttribute('data-version');
|
||||||
this.downloadFirmware(name, version);
|
this.downloadFirmware(name, version);
|
||||||
};
|
});
|
||||||
btn.addEventListener('click', handler);
|
|
||||||
this.dynamicUnsubscribers.push(() => btn.removeEventListener('click', handler));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Delete buttons
|
// Delete buttons
|
||||||
const deleteBtns = this.findAllElements('.delete-btn');
|
const deleteBtns = this.findAllElements('.delete-btn');
|
||||||
logger.debug('Found delete buttons:', deleteBtns.length);
|
|
||||||
deleteBtns.forEach(btn => {
|
deleteBtns.forEach(btn => {
|
||||||
const handler = (e) => {
|
this.addEventListener(btn, 'click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const name = btn.getAttribute('data-name');
|
const name = btn.getAttribute('data-name');
|
||||||
const version = btn.getAttribute('data-version');
|
const version = btn.getAttribute('data-version');
|
||||||
logger.debug('Delete button clicked:', name, version);
|
|
||||||
this.showDeleteConfirmation(name, version);
|
this.showDeleteConfirmation(name, version);
|
||||||
};
|
});
|
||||||
btn.addEventListener('click', handler);
|
|
||||||
this.dynamicUnsubscribers.push(() => btn.removeEventListener('click', handler));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanupDynamicListeners() {
|
|
||||||
if (this.dynamicUnsubscribers) {
|
|
||||||
this.dynamicUnsubscribers.forEach(unsub => unsub());
|
|
||||||
this.dynamicUnsubscribers = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showAddFirmwareForm() {
|
showAddFirmwareForm() {
|
||||||
this.openFirmwareForm('Add Firmware', null, null);
|
this.openFirmwareForm('Add Firmware', null, null);
|
||||||
}
|
}
|
||||||
@@ -426,12 +398,13 @@ class FirmwareComponent extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showDeleteConfirmation(name, version) {
|
showDeleteConfirmation(name, version) {
|
||||||
OverlayDialogComponent.danger({
|
this.showConfirmationDialog({
|
||||||
title: 'Delete Firmware',
|
title: 'Delete Firmware',
|
||||||
message: `Are you sure you want to delete firmware "${name}" version "${version}"?<br><br>This action cannot be undone.`,
|
message: `Are you sure you want to delete firmware "${name}" version "${version}"?<br><br>This action cannot be undone.`,
|
||||||
confirmText: 'Delete',
|
confirmText: 'Delete',
|
||||||
cancelText: 'Cancel',
|
cancelText: 'Cancel',
|
||||||
onConfirm: () => this.deleteFirmware(name, version)
|
onConfirm: () => this.deleteFirmware(name, version),
|
||||||
|
onCancel: () => {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,9 +574,9 @@ class FirmwareComponent extends Component {
|
|||||||
|
|
||||||
async deleteFirmware(name, version) {
|
async deleteFirmware(name, version) {
|
||||||
try {
|
try {
|
||||||
await window.apiClient.deleteFirmwareFromRegistry(name, version);
|
// Note: The registry API doesn't have a delete endpoint in the OpenAPI spec
|
||||||
this.showSuccess(`Firmware ${name} v${version} deleted successfully`);
|
// This would need to be implemented in the registry service
|
||||||
await this.loadFirmwareList();
|
this.showError('Delete functionality not yet implemented in registry API');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Delete failed:', error);
|
logger.error('Delete failed:', error);
|
||||||
this.showError('Delete failed: ' + error.message);
|
this.showError('Delete failed: ' + error.message);
|
||||||
@@ -665,6 +638,52 @@ class FirmwareComponent extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showConfirmationDialog(options) {
|
||||||
|
// Create a simple confirmation dialog
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.className = 'overlay-dialog';
|
||||||
|
overlay.innerHTML = `
|
||||||
|
<div class="dialog">
|
||||||
|
<div class="dialog-header">
|
||||||
|
<h3>${options.title}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-body">
|
||||||
|
<p>${options.message}</p>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
${options.cancelText ? `<button class="btn btn-secondary" id="cancel-btn">${options.cancelText}</button>` : ''}
|
||||||
|
<button class="btn btn-primary" id="confirm-btn">${options.confirmText}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
|
const confirmBtn = overlay.querySelector('#confirm-btn');
|
||||||
|
const cancelBtn = overlay.querySelector('#cancel-btn');
|
||||||
|
|
||||||
|
confirmBtn.addEventListener('click', () => {
|
||||||
|
document.body.removeChild(overlay);
|
||||||
|
if (options.onConfirm) options.onConfirm();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cancelBtn) {
|
||||||
|
cancelBtn.addEventListener('click', () => {
|
||||||
|
document.body.removeChild(overlay);
|
||||||
|
if (options.onCancel) options.onCancel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close on escape key
|
||||||
|
const handleEscape = (e) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
document.body.removeChild(overlay);
|
||||||
|
document.removeEventListener('keydown', handleEscape);
|
||||||
|
if (options.onCancel) options.onCancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', handleEscape);
|
||||||
|
}
|
||||||
|
|
||||||
showSuccess(message) {
|
showSuccess(message) {
|
||||||
this.showNotification(message, 'success');
|
this.showNotification(message, 'success');
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ class OverlayDialogComponent extends Component {
|
|||||||
this.message = '';
|
this.message = '';
|
||||||
this.confirmText = 'Yes';
|
this.confirmText = 'Yes';
|
||||||
this.cancelText = 'No';
|
this.cancelText = 'No';
|
||||||
this.confirmClass = 'overlay-dialog-btn-confirm';
|
|
||||||
this.showCloseButton = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mount() {
|
mount() {
|
||||||
@@ -40,8 +38,6 @@ class OverlayDialogComponent extends Component {
|
|||||||
message = 'Are you sure you want to proceed?',
|
message = 'Are you sure you want to proceed?',
|
||||||
confirmText = 'Yes',
|
confirmText = 'Yes',
|
||||||
cancelText = 'No',
|
cancelText = 'No',
|
||||||
confirmClass = 'overlay-dialog-btn-confirm',
|
|
||||||
showCloseButton = true,
|
|
||||||
onConfirm = null,
|
onConfirm = null,
|
||||||
onCancel = null
|
onCancel = null
|
||||||
} = options;
|
} = options;
|
||||||
@@ -50,74 +46,53 @@ class OverlayDialogComponent extends Component {
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
this.confirmText = confirmText;
|
this.confirmText = confirmText;
|
||||||
this.cancelText = cancelText;
|
this.cancelText = cancelText;
|
||||||
this.confirmClass = confirmClass;
|
|
||||||
this.showCloseButton = showCloseButton;
|
|
||||||
this.onConfirm = onConfirm;
|
this.onConfirm = onConfirm;
|
||||||
this.onCancel = onCancel;
|
this.onCancel = onCancel;
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
|
this.container.classList.add('visible');
|
||||||
// Add visible class with small delay for animation
|
|
||||||
setTimeout(() => {
|
|
||||||
this.container.classList.add('visible');
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
this.isVisible = true;
|
this.isVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.container.classList.remove('visible');
|
this.container.classList.remove('visible');
|
||||||
|
this.isVisible = false;
|
||||||
|
|
||||||
setTimeout(() => {
|
// Call cancel callback if provided
|
||||||
this.isVisible = false;
|
if (this.onCancel) {
|
||||||
|
this.onCancel();
|
||||||
// Call cancel callback if provided
|
}
|
||||||
if (this.onCancel) {
|
|
||||||
this.onCancel();
|
|
||||||
this.onCancel = null;
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConfirm() {
|
handleConfirm() {
|
||||||
this.container.classList.remove('visible');
|
this.hide();
|
||||||
|
|
||||||
setTimeout(() => {
|
// Call confirm callback if provided
|
||||||
this.isVisible = false;
|
if (this.onConfirm) {
|
||||||
|
this.onConfirm();
|
||||||
// Call confirm callback if provided
|
}
|
||||||
if (this.onConfirm) {
|
|
||||||
this.onConfirm();
|
|
||||||
this.onConfirm = null;
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.container.innerHTML = `
|
this.container.innerHTML = `
|
||||||
<div class="overlay-dialog-content">
|
<div class="overlay-dialog-content">
|
||||||
<div class="overlay-dialog-header">
|
<div class="overlay-dialog-header">
|
||||||
<h3 class="overlay-dialog-title">${this.escapeHtml(this.title)}</h3>
|
<h3 class="overlay-dialog-title">${this.title}</h3>
|
||||||
${this.showCloseButton ? `
|
<button class="overlay-dialog-close" type="button">
|
||||||
<button class="overlay-dialog-close" type="button" aria-label="Close">
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20">
|
<path d="M18 6L6 18M6 6l12 12"/>
|
||||||
<line x1="18" y1="6" x2="6" y2="18"/>
|
</svg>
|
||||||
<line x1="6" y1="6" x2="18" y2="18"/>
|
</button>
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
` : ''}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="overlay-dialog-body">
|
<div class="overlay-dialog-body">
|
||||||
<p class="overlay-dialog-message">${this.message}</p>
|
<div class="overlay-dialog-message">${this.message}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="overlay-dialog-footer">
|
<div class="overlay-dialog-footer">
|
||||||
${this.cancelText ? `
|
<button class="overlay-dialog-btn overlay-dialog-btn-cancel" type="button">
|
||||||
<button class="overlay-dialog-btn overlay-dialog-btn-cancel" type="button">
|
${this.cancelText}
|
||||||
${this.escapeHtml(this.cancelText)}
|
</button>
|
||||||
</button>
|
<button class="overlay-dialog-btn overlay-dialog-btn-confirm" type="button">
|
||||||
` : ''}
|
${this.confirmText}
|
||||||
<button class="overlay-dialog-btn ${this.confirmClass}" type="button">
|
|
||||||
${this.escapeHtml(this.confirmText)}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -126,7 +101,7 @@ class OverlayDialogComponent extends Component {
|
|||||||
// Add event listeners to buttons
|
// Add event listeners to buttons
|
||||||
const closeBtn = this.container.querySelector('.overlay-dialog-close');
|
const closeBtn = this.container.querySelector('.overlay-dialog-close');
|
||||||
const cancelBtn = this.container.querySelector('.overlay-dialog-btn-cancel');
|
const cancelBtn = this.container.querySelector('.overlay-dialog-btn-cancel');
|
||||||
const confirmBtn = this.container.querySelector(`.${this.confirmClass}`);
|
const confirmBtn = this.container.querySelector('.overlay-dialog-btn-confirm');
|
||||||
|
|
||||||
if (closeBtn) {
|
if (closeBtn) {
|
||||||
this.addEventListener(closeBtn, 'click', () => this.hide());
|
this.addEventListener(closeBtn, 'click', () => this.hide());
|
||||||
@@ -141,13 +116,6 @@ class OverlayDialogComponent extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
escapeHtml(text) {
|
|
||||||
if (typeof text !== 'string') return text;
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.textContent = text;
|
|
||||||
return div.innerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
unmount() {
|
unmount() {
|
||||||
// Clean up event listeners
|
// Clean up event listeners
|
||||||
this.removeAllEventListeners();
|
this.removeAllEventListeners();
|
||||||
@@ -156,68 +124,3 @@ class OverlayDialogComponent extends Component {
|
|||||||
super.unmount();
|
super.unmount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static utility methods for easy usage without mounting
|
|
||||||
OverlayDialogComponent.show = function(options) {
|
|
||||||
// Create a temporary container
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.className = 'overlay-dialog';
|
|
||||||
document.body.appendChild(container);
|
|
||||||
|
|
||||||
// Create component instance
|
|
||||||
const dialog = new OverlayDialogComponent(container, null, null);
|
|
||||||
|
|
||||||
// Override hide to clean up container
|
|
||||||
const originalHide = dialog.hide.bind(dialog);
|
|
||||||
dialog.hide = function() {
|
|
||||||
originalHide();
|
|
||||||
setTimeout(() => {
|
|
||||||
if (container.parentNode) {
|
|
||||||
document.body.removeChild(container);
|
|
||||||
}
|
|
||||||
}, 350);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Override handleConfirm to clean up container
|
|
||||||
const originalHandleConfirm = dialog.handleConfirm.bind(dialog);
|
|
||||||
dialog.handleConfirm = function() {
|
|
||||||
originalHandleConfirm();
|
|
||||||
setTimeout(() => {
|
|
||||||
if (container.parentNode) {
|
|
||||||
document.body.removeChild(container);
|
|
||||||
}
|
|
||||||
}, 350);
|
|
||||||
};
|
|
||||||
|
|
||||||
dialog.mount();
|
|
||||||
dialog.show(options);
|
|
||||||
|
|
||||||
return dialog;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convenience method for confirmation dialogs
|
|
||||||
OverlayDialogComponent.confirm = function(options) {
|
|
||||||
return OverlayDialogComponent.show({
|
|
||||||
...options,
|
|
||||||
confirmClass: options.confirmClass || 'overlay-dialog-btn-confirm'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convenience method for danger/delete confirmations
|
|
||||||
OverlayDialogComponent.danger = function(options) {
|
|
||||||
return OverlayDialogComponent.show({
|
|
||||||
...options,
|
|
||||||
confirmClass: 'overlay-dialog-btn-danger'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convenience method for alerts
|
|
||||||
OverlayDialogComponent.alert = function(message, title = 'Notice') {
|
|
||||||
return OverlayDialogComponent.show({
|
|
||||||
title,
|
|
||||||
message,
|
|
||||||
confirmText: 'OK',
|
|
||||||
cancelText: null,
|
|
||||||
showCloseButton: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -228,30 +228,17 @@ class RolloutComponent extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeCount = this.matchingNodes.length;
|
// Send the firmware info and matching nodes directly
|
||||||
const nodePlural = nodeCount !== 1 ? 's' : '';
|
const rolloutData = {
|
||||||
const { name, version } = this.rolloutData;
|
firmware: {
|
||||||
|
name: this.rolloutData.name,
|
||||||
|
version: this.rolloutData.version,
|
||||||
|
labels: this.rolloutData.labels
|
||||||
|
},
|
||||||
|
nodes: this.matchingNodes
|
||||||
|
};
|
||||||
|
|
||||||
// Show confirmation dialog
|
this.onRolloutCallback(rolloutData);
|
||||||
OverlayDialogComponent.confirm({
|
|
||||||
title: 'Confirm Firmware Rollout',
|
|
||||||
message: `Are you sure you want to deploy firmware <strong>${this.escapeHtml(name)}</strong> version <strong>${this.escapeHtml(version)}</strong> to <strong>${nodeCount} node${nodePlural}</strong>?<br><br>The rollout process cannot be cancelled once started. All nodes will be updated and rebooted.`,
|
|
||||||
confirmText: `Rollout to ${nodeCount} Node${nodePlural}`,
|
|
||||||
cancelText: 'Cancel',
|
|
||||||
onConfirm: () => {
|
|
||||||
// Send the firmware info and matching nodes directly
|
|
||||||
const rolloutData = {
|
|
||||||
firmware: {
|
|
||||||
name: this.rolloutData.name,
|
|
||||||
version: this.rolloutData.version,
|
|
||||||
labels: this.rolloutData.labels
|
|
||||||
},
|
|
||||||
nodes: this.matchingNodes
|
|
||||||
};
|
|
||||||
|
|
||||||
this.onRolloutCallback(rolloutData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancel() {
|
handleCancel() {
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ p {
|
|||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
backdrop-filter: var(--backdrop-blur);
|
backdrop-filter: var(--backdrop-blur);
|
||||||
|
box-shadow: var(--shadow-primary);
|
||||||
border: 1px solid var(--border-primary);
|
border: 1px solid var(--border-primary);
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
@@ -2139,6 +2140,7 @@ p {
|
|||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
backdrop-filter: var(--backdrop-blur);
|
backdrop-filter: var(--backdrop-blur);
|
||||||
|
box-shadow: var(--shadow-primary);
|
||||||
border: 1px solid var(--border-primary);
|
border: 1px solid var(--border-primary);
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
@@ -2317,6 +2319,7 @@ p {
|
|||||||
.firmware-group.expanded .firmware-group-header {
|
.firmware-group.expanded .firmware-group-header {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
padding-bottom: 0.75rem;
|
padding-bottom: 0.75rem;
|
||||||
|
border-bottom: 1px solid var(--border-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.firmware-group-header-content {
|
.firmware-group-header-content {
|
||||||
@@ -4482,6 +4485,7 @@ select.param-input:focus {
|
|||||||
#topology-graph-container {
|
#topology-graph-container {
|
||||||
background: var(--bg-tertiary);
|
background: var(--bg-tertiary);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -5052,15 +5056,6 @@ select.param-input:focus {
|
|||||||
background: rgba(244, 67, 54, 0.2);
|
background: rgba(244, 67, 54, 0.2);
|
||||||
color: #f44336;
|
color: #f44336;
|
||||||
border: 1px solid rgba(244, 67, 54, 0.3);
|
border: 1px solid rgba(244, 67, 54, 0.3);
|
||||||
padding: 0.5rem 0.75rem !important;
|
|
||||||
text-align: center;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
font-size: 0.8rem;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ultra-compact layout for upload progress */
|
/* Ultra-compact layout for upload progress */
|
||||||
@@ -6485,6 +6480,7 @@ html {
|
|||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
backdrop-filter: var(--backdrop-blur);
|
backdrop-filter: var(--backdrop-blur);
|
||||||
|
box-shadow: var(--shadow-primary);
|
||||||
border: 1px solid var(--border-primary);
|
border: 1px solid var(--border-primary);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
@@ -7506,19 +7502,6 @@ html {
|
|||||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-dialog-btn-danger {
|
|
||||||
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
|
||||||
border: 1px solid #ef4444;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlay-dialog-btn-danger:hover {
|
|
||||||
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
|
|
||||||
border-color: #dc2626;
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* === Rollout Component Styles === */
|
/* === Rollout Component Styles === */
|
||||||
.rollout-btn {
|
.rollout-btn {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|||||||
Reference in New Issue
Block a user