fix(web): 對齊報表統計 API 路徑
Some checks failed
Code Review / ai-code-review (push) Successful in 12s
CD Pipeline / tests (push) Successful in 1m32s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-18 18:42:37 +08:00
parent 4d4c6da340
commit 5e8492256e

View File

@@ -59,11 +59,16 @@ interface IncidentSummary {
total?: number
resolved?: number
unresolved?: number
total_incidents?: number
resolved_rate?: number
status_distribution?: Array<{ status: string; count: number }>
}
interface ResolutionStats {
resolutionRate?: number
avgResolutionTime?: string | number
avg_minutes?: number | null
sample_size?: number
}
interface ReportStatusCard {
@@ -157,8 +162,8 @@ export default function ReportsPage({ params }: { params: { locale: string } })
setLoading(true)
setError(false)
Promise.all([
fetchJson<IncidentSummary>('/api/v1/stats/incident-summary'),
fetchJson<ResolutionStats>('/api/v1/stats/resolution-stats'),
fetchJson<IncidentSummary>('/api/v1/stats/incidents/summary?days=30'),
fetchJson<ResolutionStats>('/api/v1/stats/incidents/resolution?days=30'),
fetchJson<DispositionResponse>('/api/v1/stats/disposition'),
fetchJson<ReportStatusBoardSnapshot>('/api/v1/agents/agent-report-status-board'),
])
@@ -182,11 +187,23 @@ export default function ReportsPage({ params }: { params: { locale: string } })
useEffect(() => { fetchAll() }, [])
const ds = disposition?.summary
const incidentTotal = summary?.total ?? summary?.total_incidents ?? 0
const resolvedFromDistribution = summary?.status_distribution
?.filter(item => item.status.toLowerCase().includes('resolved'))
.reduce((sum, item) => sum + item.count, 0)
const resolvedCount = summary?.resolved ?? resolvedFromDistribution
const unresolvedCount = summary?.unresolved ?? (
resolvedCount != null ? Math.max(incidentTotal - resolvedCount, 0) : undefined
)
const incidentResolvedRate = summary?.resolved_rate ?? resolution?.resolutionRate
const avgResolutionTime = resolution?.avgResolutionTime ?? (
resolution?.avg_minutes != null ? `${Math.round(resolution.avg_minutes)}m` : undefined
)
const sourceOkCount = Object.values(sourceState).filter(Boolean).length
const sourceTotal = Object.keys(sourceState).length
const allZeroSignal = Boolean(
sourceOkCount > 0 &&
(summary?.total ?? 0) === 0 &&
incidentTotal === 0 &&
(ds?.total ?? 0) === 0 &&
(ds?.auto_repair ?? 0) === 0 &&
(ds?.human_approved ?? 0) === 0
@@ -275,7 +292,7 @@ export default function ReportsPage({ params }: { params: { locale: string } })
<CommandMetric
icon={<Activity className="h-4 w-4" />}
label={t('command.metrics.alerts')}
value={summary?.total ?? '--'}
value={incidentTotal || '--'}
detail={allZeroSignal ? t('command.details.noSignal') : t('command.details.alertSignal')}
tone={allZeroSignal ? 'warn' : 'neutral'}
/>
@@ -356,8 +373,8 @@ export default function ReportsPage({ params }: { params: { locale: string } })
<div className="rounded-[7px] border border-border bg-card p-4 shadow-sm">
<SectionHeader icon={<Layers3 className="h-4 w-4" />} title={t('funnel.title')} subtitle={t('funnel.subtitle')} />
<div className="mt-4 grid gap-3">
<FunnelBar label={t('funnel.alerts')} value={summary?.total ?? 0} max={Math.max(summary?.total ?? 0, totalDispositions, 1)} tone="neutral" />
<FunnelBar label={t('funnel.dispositions')} value={totalDispositions} max={Math.max(summary?.total ?? 0, totalDispositions, 1)} tone="neutral" />
<FunnelBar label={t('funnel.alerts')} value={incidentTotal} max={Math.max(incidentTotal, totalDispositions, 1)} tone="neutral" />
<FunnelBar label={t('funnel.dispositions')} value={totalDispositions} max={Math.max(incidentTotal, totalDispositions, 1)} tone="neutral" />
<FunnelBar label={t('funnel.auto')} value={autoHandled} max={Math.max(totalDispositions, 1)} tone="ok" />
<FunnelBar label={t('funnel.human')} value={(ds?.human_approved ?? 0) + (ds?.manual_resolved ?? 0)} max={Math.max(totalDispositions, 1)} tone="warn" />
<FunnelBar label={t('funnel.waiting')} value={waitingApproval} max={Math.max(waitingApproval, reportBoard?.rollups?.workload_unit_total ?? 1)} tone={waitingApproval > 0 ? 'danger' : 'ok'} />
@@ -464,16 +481,16 @@ export default function ReportsPage({ params }: { params: { locale: string } })
<div className="rounded-[7px] border border-border bg-card p-4 shadow-sm">
<p className="mb-3 text-xs font-bold uppercase tracking-wider text-muted-foreground">{t('incidentSummary')}</p>
<div className="grid grid-cols-3 gap-3">
<MiniStat label={t('total')} value={summary?.total ?? '--'} />
<MiniStat label={t('resolved')} value={summary?.resolved ?? '--'} />
<MiniStat label={t('unresolved')} value={summary?.unresolved ?? '--'} />
<MiniStat label={t('total')} value={incidentTotal || '--'} />
<MiniStat label={t('resolved')} value={resolvedCount ?? '--'} />
<MiniStat label={t('unresolved')} value={unresolvedCount ?? '--'} />
</div>
</div>
<div className="rounded-[7px] border border-border bg-card p-4 shadow-sm">
<p className="mb-3 text-xs font-bold uppercase tracking-wider text-muted-foreground">{t('resolutionStats')}</p>
<div className="grid grid-cols-2 gap-3">
<MiniStat label={t('resolutionRate')} value={resolution?.resolutionRate != null ? `${resolution.resolutionRate}%` : '--'} />
<MiniStat label={t('avgResolutionTime')} value={resolution?.avgResolutionTime ?? '--'} />
<MiniStat label={t('resolutionRate')} value={incidentResolvedRate != null ? `${incidentResolvedRate}%` : '--'} />
<MiniStat label={t('avgResolutionTime')} value={avgResolutionTime ?? '--'} />
</div>
</div>
</section>