Files
dashcaddy/status/js/batch-operations.js
T
Krystie 77688daec7 Add service filter, batch operations, and snapshot features
- Service Filter Bar: search by name, filter by status (online/offline)
- Batch Operations: multi-select containers for start/stop/restart
- Container Snapshots: create and manage Docker checkpoints
- Added filter bar and batch action bar to index.html
- Added snapshot button to Admin tools section
- New JS modules: service-filter.js, batch-operations.js, snapshot.js
- Updated build.js to include new modules in bundle
2026-05-15 02:17:24 -07:00

139 lines
4.6 KiB
JavaScript

// ========== 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);
})();