(function () { function initBatchProgress() { var page = document.querySelector('.batch-page'); if (!page) { return; } var statusUrl = page.dataset.statusUrl; var pollMs = 1000; var expectedTotal = parseInt(page.dataset.expectedTotal || '0', 10) || 0; var cancelUrl = page.dataset.cancelUrl; var title = document.getElementById('batchTitle'); var subtitle = document.getElementById('batchSubtitle'); var queueStatus = document.getElementById('queueStatus'); var fill = document.getElementById('progressFill'); var pctLabel = document.getElementById('progressPct'); var currentFile = document.getElementById('currentFile'); var statTotal = document.getElementById('statTotal'); var statProc = document.getElementById('statProcessed'); var statOK = document.getElementById('statSucceeded'); var statFail = document.getElementById('statFailed'); var feedPanel = document.getElementById('feedPanel'); var feedList = document.getElementById('batchFeed'); var donePanel = document.getElementById('donePanel'); var doneSummary = document.getElementById('doneSummary'); var failPanel = document.getElementById('failPanel'); var failList = document.getElementById('failList'); var cancelBtn = document.getElementById('cancelBatch'); var seenIds = new Set(); var canceled = false; if (!statusUrl || !title || !subtitle || !fill || !pctLabel || !currentFile || !statTotal || !statProc || !statOK || !statFail || !feedPanel || !feedList || !donePanel || !doneSummary || !failPanel || !failList) { return; } if (cancelBtn && cancelUrl) { cancelBtn.addEventListener('click', function () { if (!confirm('Cancel this batch? Any in-progress file may not complete.')) { return; } cancelBtn.disabled = true; fetch(cancelUrl, { method: 'POST' }) .then(function (response) { return response.json(); }) .then(function () { canceled = true; title.textContent = 'Batch Canceled'; subtitle.textContent = 'The batch was canceled. You can start a new upload anytime.'; currentFile.textContent = ''; if (queueStatus) { queueStatus.textContent = ''; } }) .catch(function () { cancelBtn.disabled = false; }); }); } function poll() { fetch(statusUrl) .then(function (response) { return response.json(); }) .then(function (data) { if (canceled) { return; } if (data.total > 0 && expectedTotal === 0) { expectedTotal = data.total; } var total = data.total > 0 ? data.total : expectedTotal; var pct = total > 0 ? Math.round(data.processed / total * 100) : 0; statTotal.textContent = total; statProc.textContent = data.processed; statOK.textContent = data.succeeded; statFail.textContent = data.failed_ids ? data.failed_ids.length : 0; if (queueStatus) { if (typeof data.queue_size === 'number') { queueStatus.textContent = 'Queue size: ' + data.queue_size; } else if (data.status === 'pending') { queueStatus.textContent = 'Queued for processing...'; } else { queueStatus.textContent = ''; } } if (data.status === 'pending' && expectedTotal > 0) { subtitle.textContent = 'Queued - waiting for a worker. Total files: ' + expectedTotal + '.'; } fill.style.width = pct + '%'; pctLabel.textContent = pct + '%'; currentFile.textContent = data.current_file ? 'Processing: ' + data.current_file : ''; if (data.image_ids && data.image_ids.length) { feedPanel.style.display = 'block'; data.image_ids.forEach(function (imageId) { if (!seenIds.has(imageId)) { seenIds.add(imageId); var li = document.createElement('li'); var link = document.createElement('a'); link.href = '/case/' + imageId; link.textContent = imageId; li.appendChild(link); feedList.insertBefore(li, feedList.firstChild); while (feedList.children.length > 20) { var last = feedList.lastChild; if (!last) { break; } var lastLink = last.querySelector('a'); if (lastLink && lastLink.textContent) { seenIds.delete(lastLink.textContent); } feedList.removeChild(last); } } }); } if (data.status === 'canceled') { title.textContent = 'Batch Canceled'; subtitle.textContent = 'The batch was canceled.'; if (cancelBtn) { cancelBtn.disabled = true; } return; } if (data.status === 'completed' || data.status === 'failed') { title.textContent = 'Batch Complete'; subtitle.textContent = ''; if (cancelBtn) { cancelBtn.disabled = true; } donePanel.style.display = 'block'; var failCount = data.failed_ids ? data.failed_ids.length : 0; doneSummary.textContent = data.succeeded + ' of ' + total + ' files processed successfully' + (failCount > 0 ? ', ' + failCount + ' failed' : '') + '.'; if (data.failed_ids && data.failed_ids.length) { failPanel.style.display = 'block'; data.failed_ids.forEach(function (failedId) { var li = document.createElement('li'); li.textContent = failedId; failList.appendChild(li); }); } return; } setTimeout(poll, pollMs); }) .catch(function () { setTimeout(poll, pollMs * 3); }); } poll(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initBatchProgress); } else { initBatchProgress(); } })();