From 7ff0c5c3045cb63464c4e27648b89e5f34ae02b3 Mon Sep 17 00:00:00 2001 From: OG T Date: Fri, 3 Apr 2026 14:36:46 +0800 Subject: [PATCH] =?UTF-8?q?fix(i18n):=20MonitoringTools=20=E7=A1=AC?= =?UTF-8?q?=E7=B7=A8=E7=A2=BC=E4=B8=AD=E6=96=87=20=E2=86=92=20i18n=20keys?= =?UTF-8?q?=20+=20MTTR=20=E8=B6=A8=E5=8B=A2=E6=94=B9=E7=82=BA=E7=9C=9F?= =?UTF-8?q?=E5=AF=A6=E8=A8=88=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MonitoringTools: 載入中/無法連線/觸發/正常/離線/版本/統計/更新 → useTranslations - MTTR 趨勢: '↓2m' hardcode → 前半/後半 resolved incidents 真實比較 - zh-TW.json + en.json: 新增 connectionError/monitoringStatus.firing/metaVersion/metaStats/metaUpdatedAt Co-Authored-By: Claude Sonnet 4.6 --- apps/web/messages/en.json | 12 ++++++-- apps/web/messages/zh-TW.json | 12 ++++++-- apps/web/src/app/[locale]/page.tsx | 45 ++++++++++++++++++++---------- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 3067243d..6987cb7a 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -148,8 +148,14 @@ "monitoringStatus": { "up": "OK", "down": "Down", - "unknown": "Unknown" - } + "unknown": "Unknown", + "firing": "firing", + "alert": "alerts" + }, + "connectionError": "Connection failed", + "metaVersion": "Version", + "metaStats": "Stats", + "metaUpdatedAt": "Updated" }, "openclaw": { "name": "OpenClaw", @@ -911,4 +917,4 @@ "noData": "--", "comingSoon": "Integration pending" } -} +} \ No newline at end of file diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index dc0a239f..098b2fc3 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -149,8 +149,14 @@ "monitoringStatus": { "up": "正常", "down": "離線", - "unknown": "未知" - } + "unknown": "未知", + "firing": "觸發", + "alert": "告警" + }, + "connectionError": "無法連線", + "metaVersion": "版本", + "metaStats": "統計", + "metaUpdatedAt": "更新" }, "openclaw": { "name": "OpenClaw", @@ -912,4 +918,4 @@ "noData": "--", "comingSoon": "資料尚未整合" } -} +} \ No newline at end of file diff --git a/apps/web/src/app/[locale]/page.tsx b/apps/web/src/app/[locale]/page.tsx index f729143b..e75a91ac 100644 --- a/apps/web/src/app/[locale]/page.tsx +++ b/apps/web/src/app/[locale]/page.tsx @@ -109,6 +109,8 @@ const TOOL_EMOJI: Record = { } function MonitoringTools() { + const tDash = useTranslations('dashboard') + const tCommon = useTranslations('common') const [tools, setTools] = useState([]) const [loading, setLoading] = useState(true) @@ -125,10 +127,10 @@ function MonitoringTools() { }, []) if (loading) return ( -
載入中...
+
{tCommon('loading')}
) if (tools.length === 0) return ( -
無法連線
+
{tDash('connectionError')}
) return ( @@ -137,7 +139,7 @@ function MonitoringTools() { const isUp = tool.status === 'up' const hasFiring = (tool.firing_count ?? 0) > 0 const statusColor = isUp ? (hasFiring ? '#F59E0B' : '#22C55E') : '#cc2200' - const statusText = isUp ? (hasFiring ? `${tool.firing_count} 觸發` : '正常') : '離線' + const statusText = isUp ? (hasFiring ? `${tool.firing_count} ${tDash('monitoringStatus.firing')}` : tDash('monitoringStatus.up')) : tDash('monitoringStatus.down') const accentColor = TOOL_ACCENT_COLOR[tool.name] ?? '#b0ad9f' const emoji = TOOL_EMOJI[tool.name] ?? '🔧' const link = TOOL_LINKS[tool.name] ?? '#' @@ -218,18 +220,18 @@ function MonitoringTools() { }}> {tool.version && (
- 版本 + {tDash('metaVersion')} v{tool.version}
)} {tool.stats && (
- 統計 + {tDash('metaStats')} {tool.stats}
)}
- 更新 + {tDash('metaUpdatedAt')} {timeStr}
@@ -386,16 +388,29 @@ export default function Home({ params }: { params: { locale: string } }) { return Math.round((resolved / incidents.length) * 100) })() - // MTTR 均值 - const mttrAvg = (() => { - if (!incidents?.length) return '--' + // MTTR 均值 + 趨勢(前半 vs 後半比較) + const { mttrAvg, mttrTrend } = (() => { + if (!incidents?.length) return { mttrAvg: '--', mttrTrend: undefined } const resolved = incidents.filter(i => i.updated_at && (i.status === 'resolved' || i.status === 'closed')) - if (!resolved.length) return '--' - const avgMs = resolved.reduce((sum, i) => - sum + (new Date(i.updated_at).getTime() - new Date(i.created_at).getTime()), 0 - ) / resolved.length + if (!resolved.length) return { mttrAvg: '--', mttrTrend: undefined } + const durs = resolved.map(i => new Date(i.updated_at).getTime() - new Date(i.created_at).getTime()) + const avgMs = durs.reduce((a, b) => a + b, 0) / durs.length const mins = Math.round(avgMs / 60000) - return mins < 60 ? `${mins}m` : `${(mins / 60).toFixed(1)}h` + const avgStr = mins < 60 ? `${mins}m` : `${(mins / 60).toFixed(1)}h` + // 趨勢:比較前半與後半(需至少4筆) + let trend: { text: string; color: string } | undefined + if (durs.length >= 4) { + const half = Math.floor(durs.length / 2) + const older = durs.slice(0, half).reduce((a, b) => a + b, 0) / half + const newer = durs.slice(half).reduce((a, b) => a + b, 0) / (durs.length - half) + const diffMins = Math.round((newer - older) / 60000) + if (Math.abs(diffMins) >= 1) { + trend = diffMins < 0 + ? { text: `↓${Math.abs(diffMins)}m`, color: '#22C55E' } + : { text: `↑${diffMins}m`, color: '#d97757' } + } + } + return { mttrAvg: avgStr, mttrTrend: trend } })() // (pulseMetrics reserved for future sparklines) @@ -483,7 +498,7 @@ export default function Home({ params }: { params: { locale: string } }) { { label: tDashboard('mttrAvg'), value: mttrAvg, - trend: mttrAvg !== '--' ? { text: '↓2m', color: '#22C55E' } : undefined, + trend: mttrTrend, extra: (