diff --git a/frontend/src/components/evaluation/FullTechniquesProgress.tsx b/frontend/src/components/evaluation/FullTechniquesProgress.tsx index e93ad6d..d24c0a6 100644 --- a/frontend/src/components/evaluation/FullTechniquesProgress.tsx +++ b/frontend/src/components/evaluation/FullTechniquesProgress.tsx @@ -95,69 +95,77 @@ export const FullTechniquesProgress: React.FC = ({ ))} - {(state.currentStage === 'deep_synthesis' || state.currentStage === 'quality_gate' || state.currentStage === 'complete') && ( -
-
- -
- - {state.currentStage === 'complete' ? ( -
-
- -
-
-

Analysis Complete

-

Your vintage report is ready.

-
- {state.totalScore !== undefined && ( -
- - {state.totalScore} - - /100 -
- )} - +
+
+ +
+ +
+
+
+
- ) : ( -
-
- -
-
-
-

- {state.currentStage === 'deep_synthesis' ? 'Deep Synthesis' : 'Quality Gate'} -

-

- {state.currentStage === 'deep_synthesis' - ? 'Connecting patterns across categories...' - : 'Finalizing scores and recommendations...'} -

-
+
+

Analysis Complete

+

Your vintage report is ready.

- )} +
+ + {state.totalScore ?? 0} + + /100 +
+ +
+
+ +
+
+
+ +
+
+
+

+ {state.currentStage === 'deep_synthesis' ? 'Deep Synthesis' : 'Quality Gate'} +

+

+ {state.currentStage === 'deep_synthesis' + ? 'Connecting patterns across categories...' + : 'Finalizing scores and recommendations...'} +

+
+
- )} +
- {state.error && ( -
-
- -
-

Error

-

{state.error}

-
+
+
+ +
+

Error

+

{state.error || ''}

- )} +
diff --git a/frontend/src/components/evaluation/ProgressTopBar.tsx b/frontend/src/components/evaluation/ProgressTopBar.tsx index c766a1b..adb4929 100644 --- a/frontend/src/components/evaluation/ProgressTopBar.tsx +++ b/frontend/src/components/evaluation/ProgressTopBar.tsx @@ -61,12 +61,13 @@ export const ProgressTopBar: React.FC = ({
- {enrichmentMessage && ( -
- - {enrichmentMessage} -
- )} +
+ + {enrichmentMessage || 'Loading...'} +
{completedTechniques} / diff --git a/frontend/src/hooks/useEvaluationStream.ts b/frontend/src/hooks/useEvaluationStream.ts index 7a14280..f8ca138 100644 --- a/frontend/src/hooks/useEvaluationStream.ts +++ b/frontend/src/hooks/useEvaluationStream.ts @@ -155,6 +155,22 @@ export const useEvaluationStream = (evaluationId: string): UseEvaluationStreamRe case 'error': break; + // Full techniques mode events - handled by useFullTechniquesStream + // Silently ignore here to prevent console warnings + case 'technique_start': + case 'technique_complete': + case 'technique_error': + case 'category_start': + case 'category_complete': + case 'deep_synthesis_start': + case 'deep_synthesis_complete': + case 'quality_gate_complete': + case 'enrichment_start': + case 'enrichment_complete': + case 'enrichment_error': + case 'metrics_update': + break; + default: console.warn('Unknown event type:', eventType); } diff --git a/frontend/src/hooks/useFullTechniquesStream.ts b/frontend/src/hooks/useFullTechniquesStream.ts index 8fe3e1e..b0c9fe6 100644 --- a/frontend/src/hooks/useFullTechniquesStream.ts +++ b/frontend/src/hooks/useFullTechniquesStream.ts @@ -89,6 +89,8 @@ const INITIAL_CATEGORIES: Record = Object.entries(CATEGO {} ); +const ESTIMATED_TOTAL_SECONDS = 600; + const initialState: FullTechniquesStreamState = { connectionStatus: 'connecting', retryCount: 0, @@ -103,6 +105,8 @@ const initialState: FullTechniquesStreamState = { isComplete: false, tokensUsed: 0, costUsd: 0, + startedAt: new Date().toISOString(), + etaSeconds: ESTIMATED_TOTAL_SECONDS, enrichmentPhase: 'idle', enrichmentMessage: null, enrichmentStatus: { @@ -116,7 +120,8 @@ type Action = | { type: 'CONNECTION_CHANGE'; status: FullTechniquesStreamState['connectionStatus']; retryCount?: number } | { type: 'EVENT_RECEIVED'; event: SSEEvent } | { type: 'ERROR'; error: string } - | { type: 'RESET' }; + | { type: 'RESET' } + | { type: 'UPDATE_ETA'; elapsedSeconds: number }; function reducer(state: FullTechniquesStreamState, action: Action): FullTechniquesStreamState { switch (action.type) { @@ -334,6 +339,23 @@ function reducer(state: FullTechniquesStreamState, action: Action): FullTechniqu return newState; } + case 'UPDATE_ETA': { + if (state.isComplete || state.completedTechniques === 0) { + return state; + } + + const { elapsedSeconds } = action; + const completionRatio = state.completedTechniques / state.totalTechniques; + + if (completionRatio > 0 && completionRatio < 1) { + const estimatedTotalTime = elapsedSeconds / completionRatio; + const remainingSeconds = Math.max(0, Math.ceil(estimatedTotalTime - elapsedSeconds)); + return { ...state, etaSeconds: remainingSeconds }; + } + + return state; + } + default: return state; } @@ -427,5 +449,18 @@ export function useFullTechniquesStream(evaluationId: string | null) { }; }, [evaluationId, state.isComplete, token]); + useEffect(() => { + if (!evaluationId || state.isComplete) return; + + const startTime = state.startedAt ? new Date(state.startedAt).getTime() : Date.now(); + + const interval = setInterval(() => { + const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000); + dispatch({ type: 'UPDATE_ETA', elapsedSeconds }); + }, 5000); + + return () => clearInterval(interval); + }, [evaluationId, state.isComplete, state.startedAt]); + return state; }