From d018c7518272a7fbbfbcc024b0cdf129f22345ef Mon Sep 17 00:00:00 2001 From: Celes Renata Date: Fri, 10 Jul 2026 22:14:56 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20OpsPipeline=20dashboard=20=E2=80=94=20us?= =?UTF-8?q?e=20time-filtered=20doc=20stages,=20add=20time=20labels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document Stages now shows time-filtered counts from /health endpoint (consistent with Parsing/Extraction/Trends sections) with all-time totals shown as a subtle subtitle from the SSE stream. Added "(last Xh)" labels to all sections so the time window context is always visible. --- frontend/src/pages/OpsPipeline.tsx | 39 ++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/frontend/src/pages/OpsPipeline.tsx b/frontend/src/pages/OpsPipeline.tsx index c8b4156..85e06d9 100644 --- a/frontend/src/pages/OpsPipeline.tsx +++ b/frontend/src/pages/OpsPipeline.tsx @@ -63,15 +63,18 @@ export function OpsPipelinePage() { const aggregation = (data?.aggregation ?? {}) as Record; const pipelineEnabled = (data?.pipeline_enabled ?? true) as boolean; - // Prefer live stream data for queue depths and doc stages, fall back to initial fetch + // Queue depths come from live stream (always current) + // Document stages: show time-filtered from /health endpoint so numbers are consistent + // with Parsing/Extraction/Trends sections. Stream provides live queue data only. const queueDepths = stream?.queue_depths ?? (data?.queue_depths as Record | undefined) ?? {}; - const docStages = stream?.document_stages - ?? Object.fromEntries( - ((data?.document_stages as Array<{ status: string; doc_count: number }>) ?? []) - .map((s) => [s.status, s.doc_count]), - ); + const docStages = Object.fromEntries( + ((data?.document_stages as Array<{ status: string; doc_count: number }>) ?? []) + .map((s) => [s.status, s.doc_count]), + ); + // All-time totals from stream for reference + const allTimeStages = stream?.document_stages ?? {}; const failedCount = docStages['extraction_failed'] ?? 0; @@ -150,9 +153,13 @@ export function OpsPipelinePage() { {/* Document Stage Counts */} -

Document Stages

+

+ Document Stages + (last {hours}h) +

{Object.entries(docStages).map(([status, count]) => { + const allTime = allTimeStages[status]; const color = status === 'extracted' ? 'text-green-400' : status === 'parsed' ? 'text-yellow-400' : status === 'extraction_failed' ? 'text-red-400' @@ -162,6 +169,9 @@ export function OpsPipelinePage() {
{count}
{status.replace('_', ' ')}
+ {allTime != null && allTime !== count && ( +
{allTime} total
+ )}
); })} @@ -170,7 +180,10 @@ export function OpsPipelinePage() { {/* Parsing Quality */} -

Parsing Quality

+

+ Parsing Quality + (last {hours}h) +

@@ -182,7 +195,10 @@ export function OpsPipelinePage() { {/* Extraction Stats */} -

Extraction Validation

+

+ Extraction Validation + (last {hours}h) +

@@ -194,7 +210,10 @@ export function OpsPipelinePage() { {/* Aggregation */} -

Trend Generation

+

+ Trend Generation + (last {hours}h) +