114 lines
4.2 KiB
JavaScript
114 lines
4.2 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;
|
|
let requestInProgress = false;
|
|
async function refresh() {
|
|
if (requestInProgress) return;
|
|
const active = activeRows();
|
|
if (!active.length) return;
|
|
const ids = active.map(row => Number(row.dataset.jobId)).filter(Number.isInteger);
|
|
requestInProgress = true;
|
|
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);
|
|
} finally {
|
|
requestInProgress = false;
|
|
}
|
|
}
|
|
|
|
if (activeRows().length) timer = window.setTimeout(refresh, 1200);
|
|
window.addEventListener('asset-table-content-reloaded', event => {
|
|
if (event.detail?.table !== table || !activeRows().length) return;
|
|
if (timer) window.clearTimeout(timer);
|
|
timer = window.setTimeout(refresh, 250);
|
|
});
|
|
window.addEventListener('beforeunload', () => {
|
|
if (timer) window.clearTimeout(timer);
|
|
}, {once: true});
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init, {once: true});
|
|
} else {
|
|
init();
|
|
}
|
|
})();
|