From 11197c9db1c64f0219a3d630b6d54aaa8bdc3ed1 Mon Sep 17 00:00:00 2001 From: Timo Uttenweiler Date: Fri, 27 Mar 2026 17:13:30 +0100 Subject: [PATCH] Progress bar: forward-only crawl, never goes backward --- components/search/LoadingCard.tsx | 49 ++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/components/search/LoadingCard.tsx b/components/search/LoadingCard.tsx index 44b15e9..fa6691a 100644 --- a/components/search/LoadingCard.tsx +++ b/components/search/LoadingCard.tsx @@ -45,27 +45,35 @@ export function LoadingCard({ jobId, onDone, onError }: LoadingCardProps) { totalLeads: 0, emailsFound: 0, }); - const [progressWidth, setProgressWidth] = useState(40); + const [progressWidth, setProgressWidth] = useState(3); + + // Phase → minimum progress threshold (never go below these) + const PHASE_MIN: Record = { + scraping: 3, + enriching: 35, + emails: 60, + done: 100, + }; + const PHASE_MAX: Record = { + scraping: 34, + enriching: 59, + emails: 88, + done: 100, + }; useEffect(() => { let cancelled = false; - let pendulumInterval: ReturnType | null = null; + let crawlInterval: ReturnType | null = null; let pollTimeout: ReturnType | null = null; - // Pendulum animation while running - let goingUp = true; - pendulumInterval = setInterval(() => { + // Slowly creep forward — never backwards + crawlInterval = setInterval(() => { if (cancelled) return; - setProgressWidth((prev) => { - if (goingUp) { - if (prev >= 85) { goingUp = false; return 84; } - return prev + 1; - } else { - if (prev <= 40) { goingUp = true; return 41; } - return prev - 1; - } + setProgressWidth(prev => { + if (prev >= 88) return prev; // hard cap while loading + return prev + 0.4; // ~0.4% per 200ms → ~2min to reach 88% }); - }, 120); + }, 200); async function poll() { if (cancelled) return; @@ -76,16 +84,23 @@ export function LoadingCard({ jobId, onDone, onError }: LoadingCardProps) { if (!cancelled) { setJobStatus(data); + const phase = getPhase(data); + + // Advance to at least the phase minimum — never go backwards + setProgressWidth(prev => Math.max(prev, PHASE_MIN[phase])); + if (data.status === "complete") { - if (pendulumInterval) clearInterval(pendulumInterval); + if (crawlInterval) clearInterval(crawlInterval); setProgressWidth(100); setTimeout(() => { if (!cancelled) onDone(data.totalLeads); }, 800); } else if (data.status === "failed") { - if (pendulumInterval) clearInterval(pendulumInterval); + if (crawlInterval) clearInterval(crawlInterval); onError(); } else { + // Cap crawl at phase max while in that phase + setProgressWidth(prev => Math.min(prev, PHASE_MAX[phase])); pollTimeout = setTimeout(poll, 2500); } } @@ -100,7 +115,7 @@ export function LoadingCard({ jobId, onDone, onError }: LoadingCardProps) { return () => { cancelled = true; - if (pendulumInterval) clearInterval(pendulumInterval); + if (crawlInterval) clearInterval(crawlInterval); if (pollTimeout) clearTimeout(pollTimeout); }; }, [jobId, onDone, onError]);