From cf5e456fe74a9c1b2a59cf959c52d5a6e25f27b9 Mon Sep 17 00:00:00 2001 From: Desktop Date: Tue, 11 Nov 2025 00:54:45 +0500 Subject: [PATCH] Update ProgressModal.tsx --- .../src/components/common/ProgressModal.tsx | 69 +++++++++++++------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/frontend/src/components/common/ProgressModal.tsx b/frontend/src/components/common/ProgressModal.tsx index 9bd27d7a..d951a4ad 100644 --- a/frontend/src/components/common/ProgressModal.tsx +++ b/frontend/src/components/common/ProgressModal.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useMemo } from 'react'; import { Modal } from '../ui/modal'; import Button from '../ui/button/Button'; @@ -196,15 +196,27 @@ export default function ProgressModal({ const [visuallyCompletedSteps, setVisuallyCompletedSteps] = React.useState>(new Set()); const stepCompletionTimersRef = useRef>(new Map()); const visuallyCompletedStepsRef = useRef>(new Set()); + const lastProcessedStepLogsHashRef = useRef(''); + const lastProcessedPhaseRef = useRef(''); // Sync ref with state useEffect(() => { visuallyCompletedStepsRef.current = visuallyCompletedSteps; }, [visuallyCompletedSteps]); - // Get steps for this function - const steps = getStepsForFunction(functionId, title); - const currentPhase = getCurrentPhase(stepLogs, percentage); + // Memoize steps to prevent unnecessary re-renders + const steps = useMemo(() => getStepsForFunction(functionId, title), [functionId, title]); + + // Memoize currentPhase to prevent unnecessary re-renders + const currentPhase = useMemo(() => getCurrentPhase(stepLogs, percentage), [stepLogs, percentage]); + + // Create a stable hash of stepLogs to detect meaningful changes + const stepLogsHash = useMemo(() => { + return JSON.stringify(stepLogs.map(log => ({ + stepName: log.stepName, + status: log.status, + }))); + }, [stepLogs]); // Format step message with counts and better formatting const formatStepMessage = (stepPhase: string, stepLog: any, defaultLabel: string, allStepLogs: any[], functionId?: string, title?: string): string => { @@ -285,23 +297,25 @@ export default function ProgressModal({ }; // Build checklist items with visual completion state (needed for allStepsVisuallyCompleted) - const checklistItems = steps.map((step) => { - const actuallyCompleted = isStepCompleted(step.phase, currentPhase, stepLogs); - const visuallyCompleted = visuallyCompletedSteps.has(step.phase); - // Only show as in progress if it's the current phase AND not visually completed yet - const inProgress = step.phase === currentPhase && !visuallyCompleted && !actuallyCompleted; - - // Get step log and format message - const stepLog = stepLogs.find(log => log.stepName === step.phase); - const stepMessage = formatStepMessage(step.phase, stepLog, step.label, stepLogs, functionId, title); - - return { - label: stepMessage, - phase: step.phase, - completed: visuallyCompleted, - inProgress, - }; - }); + const checklistItems = useMemo(() => { + return steps.map((step) => { + const actuallyCompleted = isStepCompleted(step.phase, currentPhase, stepLogs); + const visuallyCompleted = visuallyCompletedSteps.has(step.phase); + // Only show as in progress if it's the current phase AND not visually completed yet + const inProgress = step.phase === currentPhase && !visuallyCompleted && !actuallyCompleted; + + // Get step log and format message + const stepLog = stepLogs.find(log => log.stepName === step.phase); + const stepMessage = formatStepMessage(step.phase, stepLog, step.label, stepLogs, functionId, title); + + return { + label: stepMessage, + phase: step.phase, + completed: visuallyCompleted, + inProgress, + }; + }); + }, [steps, currentPhase, stepLogs, visuallyCompletedSteps, functionId, title]); // Check if all steps are visually completed const allStepsVisuallyCompleted = steps.length > 0 && @@ -313,11 +327,22 @@ export default function ProgressModal({ // Reset when modal closes setVisuallyCompletedSteps(new Set()); visuallyCompletedStepsRef.current = new Set(); + lastProcessedStepLogsHashRef.current = ''; + lastProcessedPhaseRef.current = ''; 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; + } + + // Update last processed values + lastProcessedStepLogsHashRef.current = stepLogsHash; + lastProcessedPhaseRef.current = currentPhase; + const phaseOrder = ['INIT', 'PREP', 'AI_CALL', 'PARSE', 'SAVE', 'DONE']; // Check each step in order @@ -360,7 +385,7 @@ export default function ProgressModal({ stepCompletionTimersRef.current.forEach(timer => clearTimeout(timer)); stepCompletionTimersRef.current.clear(); }; - }, [isOpen, currentPhase, stepLogs, steps]); // Removed visuallyCompletedSteps from dependencies + }, [isOpen, currentPhase, stepLogsHash, steps]); // Use stepLogsHash instead of stepLogs // Don't auto-close - user must click close button