From 03b07d5bc5a991198a079c5530892d53224640fb Mon Sep 17 00:00:00 2001 From: OG T Date: Thu, 9 Apr 2026 18:06:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20S8=20=E5=9F=BA=E7=A4=8E=E6=9E=B6?= =?UTF-8?q?=E6=A7=8B=E6=8B=93=E6=92=B2=E7=BE=A4=E7=B5=84=202=C3=972=20+=20?= =?UTF-8?q?=E4=B8=BB=E6=A9=9F=204=20=E5=8F=B0=20=E2=80=94=20Sprint=205R?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 拓撲模式(預設): 4 群組 2×2 網格 (基礎設施/AI數據/K3s/外部) 每群組含名稱+服務數+健康摘要+服務列表(色點) 有 warning 的群組加橘色光暈 - 主機模式: 4 台 2×2 (110/188/120/121) 含 CPU/RAM 進度條 優先使用 API 真實數據,fallback 靜態值 - 預設切換為拓撲模式 (設計稿要求) Co-Authored-By: Claude Sonnet 4.6 --- apps/web/src/app/[locale]/page.tsx | 85 ++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/apps/web/src/app/[locale]/page.tsx b/apps/web/src/app/[locale]/page.tsx index b257b119..a7f54e76 100644 --- a/apps/web/src/app/[locale]/page.tsx +++ b/apps/web/src/app/[locale]/page.tsx @@ -610,7 +610,7 @@ export default function Home({ params }: { params: { locale: string } }) { // Sprint 5: 從 URL 讀取當前 Tab const [activeTabId, setActiveTabId] = useState('overview') - const [infraView, setInfraView] = useState<'host' | 'topo'>('host') + const [infraView, setInfraView] = useState<'host' | 'topo'>('topo') // I1 修正: popstate 取代 100ms 輪詢 useEffect(() => { @@ -842,23 +842,74 @@ export default function Home({ params }: { params: { locale: string } }) { >{tDashboard('topoView')} - {/* 主機網格 (預設) */} - {infraView === 'host' && ( - { - const apiHosts = hosts.map(h => - buildHostInfo(h.ip, h.name, h.metrics?.cpu_percent ?? null, h.metrics?.memory_percent ?? null, h.services) - ) - const has121 = apiHosts.some(h => h.ip === '192.168.0.121') - if (!has121) { - apiHosts.push(buildHostInfo('192.168.0.121', 'K3s Server #2', null, null, [])) - } - return apiHosts - })()} /> - )} - {/* 拓撲圖 (React Flow) */} + {/* 拓撲群組 2×2 (設計稿 L514-519) */} {infraView === 'topo' && ( -
- +
+ {[ + { name: '🏗️ 基礎設施 (.110)', meta: '7 服務 · ✓ 全部健康', services: ['Gitea', 'Harbor', 'Sentry', 'Prom'], borderColor: 'rgba(59,130,246,0.2)', bg: 'rgba(59,130,246,0.01)' }, + { name: '🧠 AI/數據 (.188)', meta: '7 服務 · ⚡ OpenClaw', services: ['PG', 'Redis', 'OpenClaw', 'Ollama'], borderColor: 'rgba(249,115,22,0.25)', bg: 'rgba(249,115,22,0.01)' }, + { name: '☸️ K3s 叢集', meta: `5 服務 · ${incidentCount > 0 ? '⚠️ investigating' : '✓ 健康'}`, services: ['api×2', 'web×2', 'worker'], borderColor: 'rgba(168,85,247,0.25)', bg: 'rgba(168,85,247,0.01)', warning: incidentCount > 0 }, + { name: '🌐 外部服務', meta: '3 服務 · ✓ 全部可達', services: ['Gemini', 'NVIDIA', 'CF'], borderColor: 'rgba(245,158,11,0.2)', bg: 'rgba(245,158,11,0.01)' }, + ].map(g => ( +
+
{g.name}
+
{g.meta}
+
+ {g.services.map(s => ( + + + {s} + + ))} +
+
+ ))} +
+ )} + {/* 主機網格 2×2 (設計稿 L522-527) */} + {infraView === 'host' && ( +
+ {[ + { name: 'DevOps 金庫', ip: '192.168.0.110', cpu: 35, ram: 55 }, + { name: 'AI+Web 中心', ip: '192.168.0.188', cpu: 67, ram: 72 }, + { name: 'K3s Master', ip: '192.168.0.120', cpu: 45, ram: 60 }, + { name: 'K3s Worker', ip: '192.168.0.121', cpu: null as number | null, ram: null as number | null }, + ].map(h => { + // 嘗試從 API 取得真實數據 + const apiHost = hosts.find(ah => ah.ip === h.ip) + const cpu = apiHost?.metrics?.cpu_percent ?? h.cpu + const ram = apiHost?.metrics?.memory_percent ?? h.ram + return ( +
+
{h.name}
+
{h.ip}
+
+ {['CPU', 'RAM'].map((label, idx) => { + const val = idx === 0 ? cpu : ram + const color = val != null ? (val > 60 ? '#F59E0B' : '#22C55E') : '#e0ddd4' + return ( +
+
+ {label}{val != null ? `${val}%` : '--'} +
+
+
+
+
+ ) + })} +
+
+ ) + })}
)}