diff --git a/apps/web/src/app/[locale]/topology/page.tsx b/apps/web/src/app/[locale]/topology/page.tsx index 0ba187451..e0814ae3f 100644 --- a/apps/web/src/app/[locale]/topology/page.tsx +++ b/apps/web/src/app/[locale]/topology/page.tsx @@ -1,102 +1,56 @@ 'use client' /** - * 拓撲圖 Page - * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation) - * @updated 2026-04-02 ogt - 升級為真實 UI,串接 /api/v1/dashboard hosts + * 拓撲圖 Page — Sprint 5 React Flow 完整版 + * ========================================== + * 嵌套群組拓撲圖 (elkjs + React Flow) + * 串接真實 /api/v1/dashboard API + * 零假數據 + * + * @created 2026-04-01 ogt + * @updated 2026-04-09 Claude Code — Sprint 5 React Flow 完整升級 */ -import { useEffect, useState } from 'react' import { useTranslations } from 'next-intl' import { AppLayout } from '@/components/layout' - -const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? '' - -interface HostItem { - name: string - status: string - cpu?: number - ram?: number - services?: { name: string; status: string }[] -} - -const statusColor = (s: string) => { - if (s === 'healthy' || s === 'running') return '#4caf50' - if (s === 'warning') return '#ff9800' - if (s === 'critical' || s === 'error') return '#f44336' - return '#87867f' -} +import { ServiceTopology } from '@/components/topology' export default function TopologyPage({ params }: { params: { locale: string } }) { const t = useTranslations('topology') - const tc = useTranslations('common') - const [hosts, setHosts] = useState([]) - const [loading, setLoading] = useState(true) - const [error, setError] = useState(false) - - useEffect(() => { - setLoading(true) - setError(false) - fetch(`${API_BASE}/api/v1/dashboard`) - .then(r => r.json()) - .then(data => { setHosts(data?.hosts ?? []) }) - .catch(() => setError(true)) - .finally(() => setLoading(false)) - }, []) return ( -
-
-

{t('title')}

-

{t('subtitle')}

+
+ {/* 標題列 */} +
+

+ {t('title')} +

+ {t('subtitle')}
- {loading && ( -
{tc('loading')}
- )} - {!loading && error && ( -
{t('fetchError')}
- )} - {!loading && !error && hosts.length === 0 && ( -
{t('noHosts')}
- )} - - {!loading && !error && hosts.length > 0 && ( -
- {hosts.map((host, i) => ( -
-
- - {host.name} -
-
-
-
{t('cpu')}
-
{host.cpu != null ? `${host.cpu}%` : '--'}
-
-
-
{t('ram')}
-
{host.ram != null ? `${host.ram}%` : '--'}
-
-
- {(host.services ?? []).length > 0 && ( -
-
{t('services')}
-
- {(host.services ?? []).map((svc, j) => ( - - - {svc.name} - - ))} -
-
- )} -
- ))} -
- )} + {/* React Flow 拓撲圖 (全高度) */} +
+ +
)