// ========== BATCH CONTAINER OPERATIONS ========== (function() { const batchBtn = document.getElementById('batch-operations-btn'); const batchBar = document.getElementById('batch-action-bar'); const batchCount = document.getElementById('batch-selected-count'); const startBtn = document.getElementById('batch-start-btn'); const stopBtn = document.getElementById('batch-stop-btn'); const restartBtn = document.getElementById('batch-restart-btn'); const cancelBtn = document.getElementById('batch-cancel-btn'); let batchMode = false; let selectedContainers = new Set(); function enterBatchMode() { batchMode = true; selectedContainers.clear(); batchBar.style.display = ''; batchBtn.textContent = '✓ Exit Batch Mode'; updateSelectedCount(); // Add checkboxes to all cards with containerId const cards = document.querySelectorAll('#cards .card[data-app]'); cards.forEach(card => { const containerId = card.dataset.containerId; if (!containerId) return; // Remove existing checkbox if any const existing = card.querySelector('.batch-checkbox'); if (existing) existing.remove(); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'batch-checkbox'; checkbox.dataset.containerId = containerId; checkbox.dataset.serviceName = card.querySelector('.name')?.textContent || containerId; checkbox.style.cssText = 'position: absolute; top: 8px; left: 8px; z-index: 10; width: 18px; height: 18px; cursor: pointer;'; checkbox.addEventListener('change', (e) => { e.stopPropagation(); if (checkbox.checked) { selectedContainers.add(containerId); } else { selectedContainers.delete(containerId); } updateSelectedCount(); }); card.style.position = 'relative'; card.insertBefore(checkbox, card.firstChild); }); } function exitBatchMode() { batchMode = false; selectedContainers.clear(); batchBar.style.display = 'none'; batchBtn.textContent = '☰ Batch Operations'; // Remove all checkboxes document.querySelectorAll('.batch-checkbox').forEach(cb => cb.remove()); } function updateSelectedCount() { const count = selectedContainers.size; batchCount.textContent = `${count} selected`; startBtn.disabled = count === 0; stopBtn.disabled = count === 0; restartBtn.disabled = count === 0; } async function batchAction(action) { if (selectedContainers.size === 0) return; const containers = Array.from(selectedContainers); const actionLabel = { start: 'Starting', stop: 'Stopping', restart: 'Restarting' }[action]; if (!confirm(`${actionLabel} ${containers.length} container(s)? This cannot be undone.`)) return; const btns = [startBtn, stopBtn, restartBtn]; btns.forEach(b => { b.disabled = true; b.textContent = '...'; }); let success = 0; let failed = 0; const errors = []; for (const containerId of containers) { try { const res = await fetch(`/api/v1/containers/${encodeURIComponent(containerId)}/${action}`, { method: 'POST' }); if (res.ok) { success++; } else { failed++; const data = await res.json().catch(() => ({})); errors.push(`${containerId}: ${data.error || res.statusText}`); } } catch (e) { failed++; errors.push(`${containerId}: ${e.message}`); } } // Restore buttons btns[0].textContent = '▶ Start All'; btns[1].textContent = '⬛ Stop All'; btns[2].textContent = '🔄 Restart All'; updateSelectedCount(); // Show results if (failed === 0) { if (typeof showNotification === 'function') { showNotification(`${actionLabel} completed: ${success} container(s)`, 'success'); } } else { if (typeof showNotification === 'function') { showNotification(`${actionLabel}: ${success} succeeded, ${failed} failed`, 'warning'); } console.error('Batch operation errors:', errors); } // Refresh dashboard after a short delay setTimeout(() => { if (typeof refreshAll === 'function') refreshAll(); }, 1500); } batchBtn?.addEventListener('click', () => { if (batchMode) { exitBatchMode(); } else { enterBatchMode(); } }); startBtn?.addEventListener('click', () => batchAction('start')); stopBtn?.addEventListener('click', () => batchAction('stop')); restartBtn?.addEventListener('click', () => batchAction('restart')); cancelBtn?.addEventListener('click', exitBatchMode); })();