diff --git a/frontend/src/components/common/ProgressModal.tsx b/frontend/src/components/common/ProgressModal.tsx index ffde5bb7..9bd27d7a 100644 --- a/frontend/src/components/common/ProgressModal.tsx +++ b/frontend/src/components/common/ProgressModal.tsx @@ -192,10 +192,15 @@ export default function ProgressModal({ functionId, stepLogs = [], }: ProgressModalProps) { - const hasAutoClosedRef = useRef(false); // Track which steps are visually completed (with delay) const [visuallyCompletedSteps, setVisuallyCompletedSteps] = React.useState>(new Set()); const stepCompletionTimersRef = useRef>(new Map()); + const visuallyCompletedStepsRef = useRef>(new Set()); + + // Sync ref with state + useEffect(() => { + visuallyCompletedStepsRef.current = visuallyCompletedSteps; + }, [visuallyCompletedSteps]); // Get steps for this function const steps = getStepsForFunction(functionId, title); @@ -307,6 +312,7 @@ export default function ProgressModal({ if (!isOpen) { // Reset when modal closes setVisuallyCompletedSteps(new Set()); + visuallyCompletedStepsRef.current = new Set(); stepCompletionTimersRef.current.forEach(timer => clearTimeout(timer)); stepCompletionTimersRef.current.clear(); return; @@ -324,27 +330,24 @@ export default function ProgressModal({ const shouldBeCompleted = currentIndex > stepIndex || stepLogs.some(log => log.stepName === stepPhase && log.status === 'success'); - // If step should be completed but isn't visually completed yet - if (shouldBeCompleted && !visuallyCompletedSteps.has(stepPhase)) { + // If step should be completed but isn't visually completed yet and not already scheduled + if (shouldBeCompleted && !visuallyCompletedStepsRef.current.has(stepPhase) && !stepCompletionTimersRef.current.has(stepPhase)) { // Check if previous step is visually completed (or if this is the first step) const previousStep = index > 0 ? steps[index - 1] : null; - const previousStepCompleted = !previousStep || visuallyCompletedSteps.has(previousStep.phase); + const previousStepCompleted = !previousStep || visuallyCompletedStepsRef.current.has(previousStep.phase); // Only schedule if previous step is completed (or this is first step) if (previousStepCompleted) { - // Clear any existing timer for this step - const existingTimer = stepCompletionTimersRef.current.get(stepPhase); - if (existingTimer) { - clearTimeout(existingTimer); - } - // Calculate delay: 2 seconds after previous step visually completed (or 0 for first step) const delay = previousStep ? 2000 : 0; // Schedule completion const timer = setTimeout(() => { - setVisuallyCompletedSteps(prev => new Set([...prev, stepPhase])); - stepCompletionTimersRef.current.delete(stepPhase); + setVisuallyCompletedSteps(prev => { + const newSet = new Set([...prev, stepPhase]); + stepCompletionTimersRef.current.delete(stepPhase); + return newSet; + }); }, delay); stepCompletionTimersRef.current.set(stepPhase, timer); @@ -357,7 +360,7 @@ export default function ProgressModal({ stepCompletionTimersRef.current.forEach(timer => clearTimeout(timer)); stepCompletionTimersRef.current.clear(); }; - }, [isOpen, currentPhase, stepLogs, steps, visuallyCompletedSteps]); + }, [isOpen, currentPhase, stepLogs, steps]); // Removed visuallyCompletedSteps from dependencies // Don't auto-close - user must click close button @@ -370,7 +373,7 @@ export default function ProgressModal({ isOpen={isOpen} onClose={onClose || (() => {})} className="max-w-lg" - showCloseButton={status === 'completed' || status === 'error'} + showCloseButton={false} >
{/* Header */}