// Rollout Component - Shows rollout panel with matching nodes and starts rollout class RolloutComponent extends Component { constructor(container, viewModel, eventBus) { super(container, viewModel, eventBus); logger.debug('RolloutComponent: Constructor called'); this.rolloutData = null; this.matchingNodes = []; this.onRolloutCallback = null; this.onCancelCallback = null; } setupEventListeners() { // Rollout button const rolloutBtn = this.findElement('#rollout-confirm-btn'); if (rolloutBtn) { this.addEventListener(rolloutBtn, 'click', this.handleRollout.bind(this)); } // Cancel button const cancelBtn = this.findElement('#rollout-cancel-btn'); if (cancelBtn) { this.addEventListener(cancelBtn, 'click', this.handleCancel.bind(this)); } } // Start rollout - hide labels and show status indicators startRollout() { const nodeItems = this.container.querySelectorAll('.rollout-node-item'); nodeItems.forEach(item => { const labelsDiv = item.querySelector('.rollout-node-labels'); const statusDiv = item.querySelector('.status-indicator'); if (labelsDiv && statusDiv) { labelsDiv.style.display = 'none'; statusDiv.style.display = 'block'; statusDiv.textContent = 'Ready'; statusDiv.className = 'status-indicator ready'; } }); // Disable the confirm button const confirmBtn = this.findElement('#rollout-confirm-btn'); if (confirmBtn) { confirmBtn.disabled = true; confirmBtn.textContent = 'Rollout in Progress...'; } } // Update status for a specific node updateNodeStatus(nodeIp, status) { const nodeItem = this.container.querySelector(`[data-node-ip="${nodeIp}"]`); if (!nodeItem) return; const statusDiv = nodeItem.querySelector('.status-indicator'); if (!statusDiv) return; let displayStatus = status; let statusClass = ''; switch (status) { case 'updating_labels': displayStatus = 'Updating Labels...'; statusClass = 'uploading'; break; case 'uploading': displayStatus = 'Uploading...'; statusClass = 'uploading'; break; case 'completed': displayStatus = 'Completed'; statusClass = 'success'; break; case 'failed': displayStatus = 'Failed'; statusClass = 'error'; break; default: displayStatus = status; statusClass = 'pending'; } statusDiv.textContent = displayStatus; statusDiv.className = `status-indicator ${statusClass}`; } // Check if rollout is complete isRolloutComplete() { const statusIndicators = this.container.querySelectorAll('.status-indicator'); for (const indicator of statusIndicators) { const status = indicator.textContent.toLowerCase(); if (status !== 'completed' && status !== 'failed') { return false; } } return true; } // Reset to initial state (show labels, hide status indicators) resetRolloutState() { const nodeItems = this.container.querySelectorAll('.rollout-node-item'); nodeItems.forEach(item => { const labelsDiv = item.querySelector('.rollout-node-labels'); const statusDiv = item.querySelector('.status-indicator'); if (labelsDiv && statusDiv) { labelsDiv.style.display = 'block'; statusDiv.style.display = 'none'; } }); // Re-enable the confirm button const confirmBtn = this.findElement('#rollout-confirm-btn'); if (confirmBtn) { confirmBtn.disabled = false; confirmBtn.textContent = `Rollout to ${this.matchingNodes.length} Node${this.matchingNodes.length !== 1 ? 's' : ''}`; } } mount() { super.mount(); logger.debug('RolloutComponent: Mounting...'); this.render(); logger.debug('RolloutComponent: Mounted successfully'); } setRolloutData(name, version, labels, matchingNodes) { this.rolloutData = { name, version, labels }; this.matchingNodes = matchingNodes; } setOnRolloutCallback(callback) { this.onRolloutCallback = callback; } setOnCancelCallback(callback) { this.onCancelCallback = callback; } render() { if (!this.rolloutData) { this.container.innerHTML = '
Deploy firmware to matching cluster nodes