diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 6b793ed0..bdb83cf8 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -237,6 +237,7 @@ "title": "AI Automation Evidence", "claimReady": "Loop claim ready", "claimBlocked": "Gaps remain", + "claimChecking": "Quality pending", "loading": "Loading AI automation evidence...", "empty": "No AI automation evidence is available yet.", "missingApiBase": "NEXT_PUBLIC_API_URL is not set", @@ -250,8 +251,10 @@ "mcpDetail": "{success} success / {failed} failed, latest {server}", "autoRepair": "Auto repair", "qualityDetail": "Average {score}, red {red}", + "qualityPending": "Quality summary is still calculating; other evidence is already shown", "humanGap": "Human gap", "humanGapDetail": "{gate} missing {count}", + "humanGapClear": "Quality summary has no top gap", "modelRoute": "Model route", "routeDetail": "{model}; current {selected}; {primary}={primaryStatus}; fallback {fallback}", "routeReasonSeparator": "; ", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 205b80d4..44a14381 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -238,6 +238,7 @@ "title": "AI 自動化證據鏈", "claimReady": "可宣稱閉環", "claimBlocked": "仍有缺口", + "claimChecking": "品質計算中", "loading": "讀取 AI 自動化證據中...", "empty": "尚無可呈現的 AI 自動化證據。", "missingApiBase": "NEXT_PUBLIC_API_URL 未設定", @@ -251,8 +252,10 @@ "mcpDetail": "成功 {success} / 失敗 {failed},最新 {server}", "autoRepair": "自動修復", "qualityDetail": "平均 {score},紅燈 {red}", + "qualityPending": "品質摘要計算中,其他證據已先顯示", "humanGap": "人工缺口", "humanGapDetail": "{gate} 缺 {count} 筆", + "humanGapClear": "品質摘要未列出主要缺口", "modelRoute": "模型路由", "routeDetail": "{model};目前 {selected};{primary}={primaryStatus};備援 {fallback}", "routeReasonSeparator": ";", diff --git a/apps/web/src/components/dashboard/automation-evidence-card.tsx b/apps/web/src/components/dashboard/automation-evidence-card.tsx index d2a92a1d..f5bddd95 100644 --- a/apps/web/src/components/dashboard/automation-evidence-card.tsx +++ b/apps/web/src/components/dashboard/automation-evidence-card.tsx @@ -248,30 +248,39 @@ export function AutomationEvidenceCard() { async function load() { try { setError(null) - const [ - quality, - coverage, - recurrence, - runs, - route, - ] = await Promise.all([ - fetchJson('/api/v1/platform/truth-chain/quality/summary?project_id=awoooi&hours=24&limit=200', controller.signal), + const [coverage, recurrence, runs, route] = await Promise.all([ fetchJson('/api/v1/platform/events/dossier/coverage?project_id=awoooi&limit=100', controller.signal), fetchJson('/api/v1/platform/events/dossier/recurrence?project_id=awoooi&limit=100', controller.signal), fetchJson('/api/v1/platform/runs/list?project_id=awoooi&per_page=25', controller.signal), fetchJson('/api/v1/platform/ai-route-status?workload_type=deep_rca', controller.signal), ]) - setSnapshot({ - quality, + setSnapshot((current) => ({ + quality: current?.quality ?? null, coverage, recurrence, runs: Array.isArray(runs?.runs) ? runs.runs : Array.isArray(runs?.items) ? runs.items : [], route, - }) + })) + setLoading(false) + + const quality = await fetchJson( + '/api/v1/platform/truth-chain/quality/summary?project_id=awoooi&hours=24&limit=200', + controller.signal + ) + if (!controller.signal.aborted) { + setSnapshot((current) => ({ + quality: quality ?? current?.quality ?? null, + coverage: current?.coverage ?? coverage, + recurrence: current?.recurrence ?? recurrence, + runs: current?.runs ?? (Array.isArray(runs?.runs) ? runs.runs : Array.isArray(runs?.items) ? runs.items : []), + route: current?.route ?? route, + })) + } } catch (err) { if (!controller.signal.aborted) { setError(err instanceof Error ? err.message : t('loadFailed')) + setLoading(false) } } finally { if (!controller.signal.aborted) setLoading(false) @@ -304,6 +313,7 @@ export function AutomationEvidenceCard() { ) const topGate = quality?.gate_failures?.[0] + const qualityLoaded = Boolean(quality) const claimReady = Boolean(quality?.production_claim?.can_claim_full_auto_repair) const route = snapshot?.route ?? null const primaryProvider = route?.policy_order?.[0]?.provider_name ?? null @@ -332,6 +342,7 @@ export function AutomationEvidenceCard() { recurrence, runEvidence, topGate, + qualityLoaded, claimReady, selectedProvider, fallback, @@ -340,7 +351,13 @@ export function AutomationEvidenceCard() { } }, [snapshot, t]) - const hasData = Boolean(snapshot?.quality || snapshot?.coverage || snapshot?.recurrence) + const hasData = Boolean( + snapshot?.quality || + snapshot?.coverage || + snapshot?.recurrence || + snapshot?.route || + (snapshot?.runs?.length ?? 0) > 0 + ) return (
-
+
{t('title')} - - {derived.claimReady ? t('claimReady') : t('claimBlocked')} + + {!derived.qualityLoaded ? t('claimChecking') : derived.claimReady ? t('claimReady') : t('claimBlocked')}
@@ -413,21 +441,25 @@ export function AutomationEvidenceCard() { /> 0 ? 'warn' : 'good'} icon={ShieldCheck} />