Files
Assetmanager/app/static/js/software-job-status.js
T

104 lines
3.8 KiB
JavaScript

(() => {
const terminalStatuses = new Set(['success', 'failed', 'partial', 'timeout', 'cancelled']);
const dispatchingStatuses = new Set(['sending', 'running']);
const bulkBlockedStatuses = new Set(['success', 'sending', 'running']);
function init() {
const table = document.querySelector('table[data-storage-key="software-jobs"]');
if (!table) return;
const rows = () => [...table.querySelectorAll('tbody tr[data-job-id]')];
const activeRows = () => rows().filter(row => !terminalStatuses.has(row.dataset.jobStatus || 'created'));
function renderRestartCell(row, status) {
const cell = row.querySelector('[data-job-restart-cell]');
if (!cell) return;
const currentlyBlocked = Boolean(cell.querySelector('.muted'));
const mustBlock = dispatchingStatuses.has(status);
if (currentlyBlocked === mustBlock) return;
cell.replaceChildren();
if (mustBlock) {
const placeholder = document.createElement('span');
placeholder.className = 'muted';
placeholder.textContent = '—';
cell.appendChild(placeholder);
return;
}
const form = document.createElement('form');
form.method = 'post';
form.action = `/jobs/${row.dataset.jobId}/restart`;
form.className = 'inline-form';
form.addEventListener('submit', event => {
const message = table.dataset.restartConfirm || '';
if (message && !window.confirm(message)) event.preventDefault();
});
const button = document.createElement('button');
button.type = 'submit';
button.className = 'button button-secondary button-small';
button.textContent = table.dataset.restartLabel || '↻';
form.appendChild(button);
cell.appendChild(form);
}
function applyStatus(item) {
const row = table.querySelector(`tbody tr[data-job-id="${CSS.escape(String(item.id))}"]`);
if (!row) return;
const status = String(item.status || 'created');
const previous = row.dataset.jobStatus || '';
row.dataset.jobStatus = status;
const field = row.querySelector('[data-job-status-field]');
if (field) {
field.textContent = item.status_label || status;
field.className = `job-status job-status-${status}`;
}
const checkbox = row.querySelector('.software-job-restart-select');
if (checkbox) {
const hasNode = row.dataset.jobHasNode === '1';
checkbox.disabled = !hasNode || bulkBlockedStatuses.has(status);
if (checkbox.disabled) checkbox.checked = false;
}
renderRestartCell(row, status);
if (previous !== status) {
window.dispatchEvent(new CustomEvent('software-job-status-changed', {detail: {row, status}}));
}
}
let timer = null;
async function refresh() {
const active = activeRows();
if (!active.length) return;
const ids = active.map(row => Number(row.dataset.jobId)).filter(Number.isInteger);
try {
const response = await fetch('/api/software-jobs/statuses', {
method: 'POST',
cache: 'no-store',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(ids),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const payload = await response.json();
(payload.jobs || []).forEach(applyStatus);
if (activeRows().length) timer = window.setTimeout(refresh, 3000);
} catch (_) {
timer = window.setTimeout(refresh, 5000);
}
}
if (activeRows().length) timer = window.setTimeout(refresh, 1200);
window.addEventListener('beforeunload', () => {
if (timer) window.clearTimeout(timer);
}, {once: true});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init, {once: true});
} else {
init();
}
})();