From f817a80704ce336a4607211bcba18fb5be79af79 Mon Sep 17 00:00:00 2001 From: Desktop Date: Tue, 11 Nov 2025 01:14:05 +0500 Subject: [PATCH] Update ProgressModal.tsx --- .../src/components/common/ProgressModal.tsx | 56 ++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/common/ProgressModal.tsx b/frontend/src/components/common/ProgressModal.tsx index d951a4ad..fe6c8036 100644 --- a/frontend/src/components/common/ProgressModal.tsx +++ b/frontend/src/components/common/ProgressModal.tsx @@ -198,12 +198,16 @@ export default function ProgressModal({ const visuallyCompletedStepsRef = useRef>(new Set()); const lastProcessedStepLogsHashRef = useRef(''); const lastProcessedPhaseRef = useRef(''); + const lastVisuallyCompletedCountRef = useRef(0); // Sync ref with state useEffect(() => { visuallyCompletedStepsRef.current = visuallyCompletedSteps; }, [visuallyCompletedSteps]); + // Track count to detect when steps complete visually (without causing loops) + const visuallyCompletedCount = visuallyCompletedSteps.size; + // Memoize steps to prevent unnecessary re-renders const steps = useMemo(() => getStepsForFunction(functionId, title), [functionId, title]); @@ -231,9 +235,13 @@ export default function ProgressModal({ if (funcName.includes('cluster')) { if (stepPhase === 'INIT') { - // For INIT: Message already includes keyword names from backend (e.g., "Validating keyword1, keyword2, keyword3 and 5 more keywords") - // Just return the message as-is since backend formats it - return message; + // For INIT: Backend message already includes keyword names (e.g., "Validating keyword1, keyword2, keyword3 and 5 more keywords") + // If message doesn't have keywords, try to extract from stepLogs or use default + if (message && message !== defaultLabel && message.includes('Validating')) { + return message; + } + // Fallback: use default label + return defaultLabel; } else if (stepPhase === 'PREP') { // For PREP: Show count of keywords being loaded const keywordCount = extractCount(/(\d+)\s+keyword/i); @@ -329,30 +337,46 @@ export default function ProgressModal({ visuallyCompletedStepsRef.current = new Set(); lastProcessedStepLogsHashRef.current = ''; lastProcessedPhaseRef.current = ''; + lastVisuallyCompletedCountRef.current = 0; stepCompletionTimersRef.current.forEach(timer => clearTimeout(timer)); stepCompletionTimersRef.current.clear(); return; } - // Only process if stepLogs or currentPhase actually changed in a meaningful way - if (stepLogsHash === lastProcessedStepLogsHashRef.current && currentPhase === lastProcessedPhaseRef.current) { - return; + // Check if we need to process: + // 1. Backend progress changed (stepLogsHash or currentPhase) + // 2. A step completed visually (count increased) + const hashChanged = stepLogsHash !== lastProcessedStepLogsHashRef.current; + const phaseChanged = currentPhase !== lastProcessedPhaseRef.current; + const countChanged = visuallyCompletedCount > lastVisuallyCompletedCountRef.current; + + if (!hashChanged && !phaseChanged && !countChanged) { + return; // Nothing changed, skip processing } // Update last processed values lastProcessedStepLogsHashRef.current = stepLogsHash; lastProcessedPhaseRef.current = currentPhase; + lastVisuallyCompletedCountRef.current = visuallyCompletedCount; const phaseOrder = ['INIT', 'PREP', 'AI_CALL', 'PARSE', 'SAVE', 'DONE']; + // If status is completed, mark all steps as shouldBeCompleted + const allStepsShouldComplete = status === 'completed'; + // Check each step in order - steps.forEach((step, index) => { + for (let index = 0; index < steps.length; index++) { + const step = steps[index]; const stepPhase = step.phase; const stepIndex = phaseOrder.indexOf(stepPhase); const currentIndex = phaseOrder.indexOf(currentPhase); - // Check if step should be completed (we've moved past it or have log entry) - const shouldBeCompleted = currentIndex > stepIndex || + // Check if step should be completed: + // 1. Status is completed (all steps should complete) + // 2. We've moved past it (currentIndex > stepIndex) + // 3. We have a log entry for it with success status + const shouldBeCompleted = allStepsShouldComplete || + currentIndex > stepIndex || stepLogs.some(log => log.stepName === stepPhase && log.status === 'success'); // If step should be completed but isn't visually completed yet and not already scheduled @@ -376,16 +400,22 @@ export default function ProgressModal({ }, delay); stepCompletionTimersRef.current.set(stepPhase, timer); + + // Only process one step at a time - break after scheduling the first eligible step + break; + } else { + // Previous step is not completed yet, stop processing + break; } } - }); + } // Cleanup on unmount return () => { stepCompletionTimersRef.current.forEach(timer => clearTimeout(timer)); stepCompletionTimersRef.current.clear(); }; - }, [isOpen, currentPhase, stepLogsHash, steps]); // Use stepLogsHash instead of stepLogs + }, [isOpen, currentPhase, stepLogsHash, steps, status, visuallyCompletedCount]); // Added status and count to detect completion // Don't auto-close - user must click close button @@ -413,8 +443,8 @@ export default function ProgressModal({

Processing...

)} - {/* Spinner below heading - only show when processing and not completed */} - {!showSuccess && status !== 'completed' && status !== 'error' && ( + {/* Spinner below heading - show when processing OR when completed but steps not all visually done */} + {(status === 'processing' || (status === 'completed' && !allStepsVisuallyCompleted)) && (