diff --git a/apps/web/src/app/[locale]/page.tsx b/apps/web/src/app/[locale]/page.tsx index 5930fba4..f4763a70 100644 --- a/apps/web/src/app/[locale]/page.tsx +++ b/apps/web/src/app/[locale]/page.tsx @@ -650,96 +650,24 @@ export default function Home({ params }: { params: { locale: string } }) { const podHealthStr = totalServices > 0 ? `${healthyServices}/${totalServices}` : '--' const podAllRunning = totalServices > 0 && healthyServices === totalServices - // ── 7 Metrics Strip ───────────────────────────────────────────────────────── - // figma-v2 順序: 活躍事件 | 服務健康 | 待處理授權 | 今日事件 | 自動處置率 | MTTR 均值 | Pod 健康 + // ── 5 KPI Cards (Sprint 5R 設計稿批准版) ──────────────────────────────────── const hasPendingApprovals = pendingApprovals !== null && pendingApprovals !== undefined && pendingApprovals > 0 - const incidentCount = incidents?.length ?? 0 - const todayIncidentCount = incidentCount - // P2 count + const p1Count = incidents?.filter(i => i.severity === 'P1').length ?? 0 const p2Count = incidents?.filter(i => i.severity === 'P2').length ?? 0 - const metrics: MetricItem[] = [ - { - label: tDashboard('activeIncidents'), - value: incidentCount > 0 ? incidentCount : '--', - sub: incidentCount === 0 ? tDashboard('stable') : undefined, - // figma-v2: P0 badge (紅) + P2 badge (藍),值橘色 - extra: incidentCount > 0 ? ( -
- {p0Count > 0 && ( - P0×{p0Count} - )} - {p2Count > 0 && ( - P2×{p2Count} - )} -
- ) : undefined, - valueColor: incidentCount > 0 ? '#d97757' : undefined, - }, - { - label: tDashboard('serviceHealth'), - value: totalServices > 0 ? `${healthyServices}/${totalServices}` : '--', - valueColor: '#22C55E', - // 固定4條,按比例顯示健康數 - extra: totalServices > 0 ? ( -
- {Array.from({ length: 4 }).map((_, idx) => ( - - ))} -
- ) : undefined, - }, - { - label: tDashboard('pendingApprovals'), - value: pendingApprovals ?? '--', - sub: hasPendingApprovals ? undefined : tDashboard('stable'), - badge: hasPendingApprovals ? { text: tDashboard('awaitingConfirm'), color: '#F59E0B', bg: 'rgba(245,158,11,0.08)' } : undefined, - valueColor: hasPendingApprovals ? '#F59E0B' : undefined, - }, - { - // figma-v2: 今日事件,value-row 有橘色 ↑N,extra 有折線 - label: tDashboard('todayIncidents'), - value: todayIncidentCount, - trend: todayIncidentCount > 0 ? { text: `↑${todayIncidentCount > 0 ? Math.max(1, Math.round(todayIncidentCount * 0.2)) : 0}`, color: '#d97757' } : undefined, - extra: todaySparkValues ? ( - - ) : undefined, - }, - { - label: tDashboard('autoRemediationRate'), - value: autoRemediationRate, - trend: autoRemediationPct > 0 ? { text: `↑${autoRemediationPct > 5 ? 5 : autoRemediationPct}%`, color: '#22C55E' } : undefined, - extra: ( -
-
-
- ), - }, - { - label: tDashboard('mttrAvg'), - value: mttrAvg, - trend: mttrTrend, - extra: mttrSparkValues ? ( - - ) : undefined, - }, - { - label: tDashboard('podHealth'), - value: podHealthStr, - sub: podAllRunning - ? tDashboard('allRunning') - : totalServices > 0 ? `${totalServices - healthyServices} down` : undefined, - valueColor: podAllRunning ? '#22C55E' : totalServices > 0 ? '#cc2200' : undefined, - }, - ] + // 本週操作數 + const [weeklyOps, setWeeklyOps] = useState(null) + useEffect(() => { + fetch(`${API_BASE}/api/v1/audit-logs/stats`) + .then(r => r.ok ? r.json() : null) + .then(d => { if (d?.total_executions != null) setWeeklyOps(d.total_executions) }) + .catch(() => {}) + }, []) + + // 系統健康百分比 + const systemHealthPct = totalServices > 0 ? Math.round((healthyServices / totalServices) * 100) : 0 // Sprint 5: 4 Tab 配置 (統帥批准 2026-04-08) const alertsCount = incidents?.length ?? 0 @@ -787,104 +715,51 @@ export default function Home({ params }: { params: { locale: string } }) { overflow: 'hidden', }}> - {/* ── Metrics Strip ─────────────────────────────────────────────────── */} -
- {/* Chibi 龍蝦游泳列 */} -
- -
-
- - - - - - - - - - - - - + {/* ── KPI Strip (5 卡片 — Sprint 5R 設計稿) ──────────────────────── */} +
+ {/* 系統健康 */} +
+
{tDashboard('serviceHealth')}
+
+ {totalServices > 0 ? `${Math.round((healthyServices / totalServices) * 100)}%` : '--'} +
+ {totalServices > 0 && ( +
+
+ )} +
+ {/* 活動事件 */} +
+
{tDashboard('activeIncidents')}
+
+ 0 ? '#d97757' : '#141413' }}>{incidentCount || '--'} + {incidentCount > 0 && P1:{p1Count} P2:{p2Count}}
- {/* Metrics Row — figma-v2 完整複製 */} -
- {metrics.map((m, i) => ( - -
- {/* Label — figma: font-size:11px */} - - {m.label} - - {/* Value row — figma: height:32px,值 + trend 箭頭同行 */} -
- - {String(m.value)} - - {m.trend && ( - {m.trend.text} - )} -
- {/* Extra row — figma: height:20px margin-top:4px */} -
- {m.extra ? m.extra : m.badge ? ( - - {m.badge.text} - - ) : m.sub ? ( - {m.sub} - ) : null} -
-
- {/* Divider — figma: 獨立元素 width:0.5px height:36px */} - {i < metrics.length - 1 && ( -
- )} - - ))} + {/* 自動修復率 */} +
+
{tDashboard('autoRemediationRate')}
+
+ {autoRemediationRate} + {autoRemediationPct > 0 && ↑5%} +
+
+
+
+
+ {/* 待審批 */} +
+
{tDashboard('pendingApprovals')}
+
+ {pendingApprovals ?? '--'} + {hasPendingApprovals && {tDashboard('awaitingConfirm')}} +
+
+ {/* 本週操作 */} +
+
{tDashboard('todayIncidents')}
+
{weeklyOps != null ? weeklyOps.toLocaleString() : '--'}