Add progress overlay to AI Catalog Price Check

Shows a modal overlay with animated progress bar and batch-aware status messages
while Claude is analyzing. Progress animates in two phases: ease-out to ~85%
over the estimated duration, then a slow crawl to 99% so it never falsely
"completes" before the server responds.

- Overlay driven by CSS (hidden until .active added by JS)
- Item count passed from controller as data-item-count on the run button
- Batch count derived from item count (batches of 25) to show accurate
  "Analyzing batch N of M…" messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-25 19:27:08 -04:00
parent 360edace72
commit 9943c11571
3 changed files with 132 additions and 3 deletions
+75 -2
View File
@@ -3,10 +3,83 @@
var form = document.getElementById('runForm');
var btn = document.getElementById('runBtn');
if (!form || !btn) return;
var overlay = document.getElementById('price-check-overlay');
var bar = document.getElementById('overlay-bar');
var pctLabel = document.getElementById('overlay-pct');
var statusMsg = document.getElementById('overlay-status');
if (!form || !btn || !overlay) return;
// Estimate total seconds based on item count (roughly 12s per batch of 25, min 15s).
function estimateDuration(itemCount) {
var batches = Math.max(1, Math.ceil(itemCount / 25));
return Math.max(15, batches * 12);
}
// Messages keyed to approximate progress milestones (0100).
function messageAt(pct, batchCount) {
if (pct < 10) return 'Loading catalog items…';
if (pct < 20) return 'Sending items to Claude…';
if (batchCount <= 1) {
if (pct < 75) return 'Analyzing your catalog with AI…';
if (pct < 92) return 'Reviewing pricing data…';
} else {
var batchDone = Math.floor((pct / 100) * batchCount);
if (batchDone < batchCount) {
return 'Analyzing batch ' + (batchDone + 1) + ' of ' + batchCount + '…';
}
}
if (pct < 97) return 'Compiling results…';
return 'Almost done…';
}
function setProgress(pct, batchCount) {
var clamped = Math.min(99, Math.max(0, pct));
bar.style.width = clamped + '%';
pctLabel.textContent = Math.round(clamped);
statusMsg.textContent = messageAt(clamped, batchCount);
}
form.addEventListener('submit', function () {
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Analyzing with AI…';
var itemCount = parseInt(btn.getAttribute('data-item-count') || '0', 10);
var batchCount = Math.max(1, Math.ceil(itemCount / 25));
var totalSecs = estimateDuration(itemCount);
overlay.classList.add('active');
setProgress(0, batchCount);
// Animate progress: fast to ~85%, then slow crawl toward 99%.
// Uses two easing phases so it never "finishes" before the server responds.
var start = Date.now();
var phase1End = totalSecs * 0.80 * 1000; // 80% of time → 85% progress
var raf;
function tick() {
var elapsed = Date.now() - start;
var pct;
if (elapsed < phase1End) {
// Phase 1: ease-out from 0 → 85
var t = elapsed / phase1End;
pct = 85 * (1 - Math.pow(1 - t, 2));
} else {
// Phase 2: slow crawl 85 → 99 (never quite reaches 99)
var t2 = (elapsed - phase1End) / (totalSecs * 1000);
pct = 85 + 14 * (1 - Math.exp(-t2 * 1.5));
}
setProgress(pct, batchCount);
raf = requestAnimationFrame(tick);
}
raf = requestAnimationFrame(tick);
// Safety: if the server takes much longer than estimated, keep the overlay up.
// The page navigation itself will tear it down.
window.addEventListener('pagehide', function () {
cancelAnimationFrame(raf);
});
});
}());