/** * BatchOverlay — shared full-screen progress overlay. * * Usage: * BatchOverlay.show(title, total, options) * options: { unit: 'documents', color: '#dc3545' } * * BatchOverlay.update(done, total, currentItem) * Advances the bar and ETA. currentItem shown in the sub-line. * * BatchOverlay.done(message, onClose) * Switches to "complete" state — green checkmark + Done button. * onClose fires when the user dismisses. * * BatchOverlay.hide() * Force-hides (use for error bail-outs before done is reached). */ var BatchOverlay = (function () { var _startMs = 0; var _onClose = null; var _unit = 'items'; var _color = '#0d6efd'; var _injected = false; var _HTML = ''; function _inject() { if (_injected) return; $('body').append(_HTML); $('#bos-close').on('click', function () { _resetState(); $('#bos-wrap').hide(); if (_onClose) { _onClose(); _onClose = null; } }); _injected = true; } function _resetState() { $('#bos-spinner').show().css('color', _color); $('#bos-icon').hide(); $('#bos-pct').css('color', _color); $('#bos-bar') .addClass('progress-bar-striped progress-bar-animated') .removeClass('bg-success') .css('background-color', _color) .css('width', '0%').attr('aria-valuenow', 0); $('#bos-close').hide(); $('#bos-eta').html(' '); } function show(title, total, options) { options = options || {}; _unit = options.unit || 'items'; _color = options.color || '#0d6efd'; _onClose = options.onClose || null; _startMs = Date.now(); _inject(); _resetState(); $('#bos-title').text(title); $('#bos-sub').text('Starting…'); $('#bos-pct').text('0%'); $('#bos-count').text('0 / ' + total + ' ' + _unit); $('#bos-wrap').css('display', 'flex'); } function update(done, total, currentItem) { var pct = Math.round(done / total * 100); var elapsed = (Date.now() - _startMs) / 1000; var eta = ' '; if (done > 0) { var remaining = Math.round((elapsed / done) * (total - done)); eta = remaining > 0 ? ('~' + remaining + 's remaining') : 'almost done…'; } $('#bos-pct').text(pct + '%'); $('#bos-bar').css('width', pct + '%').attr('aria-valuenow', pct); $('#bos-count').text(done + ' / ' + total + ' ' + _unit); if (currentItem !== undefined) $('#bos-sub').text('Processing ' + currentItem + '…'); $('#bos-eta').html(eta); } function done(message, onClose) { _onClose = onClose || null; $('#bos-spinner').hide(); $('#bos-icon').show(); $('#bos-pct').text('100%').css('color', '#198754'); $('#bos-bar') .removeClass('progress-bar-striped progress-bar-animated') .css('background-color', '').addClass('bg-success') .css('width', '100%').attr('aria-valuenow', 100); $('#bos-sub').text(message || 'Complete.'); $('#bos-eta').html(' '); $('#bos-close').show(); } function hide() { if (_injected) $('#bos-wrap').hide(); } return { show: show, update: update, done: done, hide: hide }; })();