fix(web): C2 拓撲元件 i18n — 10+ 處硬編碼中文改 useTranslations
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
This commit is contained in:
@@ -808,12 +808,19 @@
|
||||
},
|
||||
"topology": {
|
||||
"title": "Topology",
|
||||
"subtitle": "Host architecture view",
|
||||
"subtitle": "Service dependencies & health status",
|
||||
"noHosts": "No host data available",
|
||||
"fetchError": "Failed to load host data",
|
||||
"services": "Services",
|
||||
"cpu": "CPU",
|
||||
"ram": "RAM"
|
||||
"ram": "RAM",
|
||||
"groupInfra": "Infrastructure",
|
||||
"groupSecurity": "Security",
|
||||
"groupK3s": "K3s Cluster",
|
||||
"groupAiData": "AI/Data Center",
|
||||
"allHealthy": "All Healthy",
|
||||
"warning": "Warning",
|
||||
"healthy": "Healthy"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notifications",
|
||||
|
||||
@@ -809,12 +809,19 @@
|
||||
},
|
||||
"topology": {
|
||||
"title": "拓撲圖",
|
||||
"subtitle": "主機架構視圖",
|
||||
"subtitle": "服務依賴與健康狀態",
|
||||
"noHosts": "目前無主機資料",
|
||||
"fetchError": "無法取得主機資料",
|
||||
"services": "服務",
|
||||
"cpu": "CPU",
|
||||
"ram": "RAM"
|
||||
"ram": "RAM",
|
||||
"groupInfra": "基礎設施",
|
||||
"groupSecurity": "安全中心",
|
||||
"groupK3s": "K3s 叢集",
|
||||
"groupAiData": "AI/數據中心",
|
||||
"allHealthy": "全部健康",
|
||||
"warning": "異常",
|
||||
"healthy": "健康"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "通知",
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
} from '@xyflow/react'
|
||||
import '@xyflow/react/dist/style.css'
|
||||
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { useTopologyData } from './hooks/useTopologyData'
|
||||
import { ServiceNode } from './nodes/ServiceNode'
|
||||
import { GroupNode } from './nodes/GroupNode'
|
||||
@@ -72,6 +73,7 @@ export function ServiceTopology({
|
||||
showMiniMap = false,
|
||||
height = '100%',
|
||||
}: ServiceTopologyProps) {
|
||||
const t = useTranslations('dashboard')
|
||||
const { nodes, edges, hostCount, serviceCount, healthyCount, warningCount } = useTopologyData()
|
||||
|
||||
// 空狀態
|
||||
@@ -82,9 +84,9 @@ export function ServiceTopology({
|
||||
height: typeof height === 'number' ? height : '100%',
|
||||
color: '#87867f', fontSize: 13,
|
||||
}}>
|
||||
<div style={{ fontSize: 28, opacity: 0.3, marginBottom: 8 }}>🌐</div>
|
||||
<div>等待主機資料...</div>
|
||||
<div style={{ fontSize: 10, marginTop: 4 }}>Dashboard API 連線中</div>
|
||||
<div style={{ fontSize: 28, opacity: 0.3, marginBottom: 8 }}>--</div>
|
||||
<div>{t('waitingHostData')}</div>
|
||||
<div style={{ fontSize: 10, marginTop: 4 }}>{t('dashboardConnecting')}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -99,10 +101,10 @@ export function ServiceTopology({
|
||||
background: 'rgba(250,249,243,0.9)', padding: '4px 10px', borderRadius: 6,
|
||||
border: '0.5px solid #e0ddd4',
|
||||
}}>
|
||||
<span>{hostCount} 主機</span>
|
||||
<span>{serviceCount} 服務</span>
|
||||
<span style={{ color: '#22C55E' }}>{healthyCount} 健康</span>
|
||||
{warningCount > 0 && <span style={{ color: '#F59E0B' }}>{warningCount} 異常</span>}
|
||||
<span>{hostCount} {t('hostsLabel')}</span>
|
||||
<span>{serviceCount}</span>
|
||||
<span style={{ color: '#22C55E' }}>{healthyCount}</span>
|
||||
{warningCount > 0 && <span style={{ color: '#F59E0B' }}>{warningCount}</span>}
|
||||
</div>
|
||||
|
||||
<ReactFlow
|
||||
|
||||
@@ -21,11 +21,12 @@ import type { Node, Edge } from '@xyflow/react'
|
||||
// 群組配置 (對應 host_aggregator.py HostRole)
|
||||
// =============================================================================
|
||||
|
||||
const GROUP_CONFIG: Record<string, { label: string; color: string; borderColor: string; bgColor: string }> = {
|
||||
devops: { label: '基礎設施', color: '#3B82F6', borderColor: 'rgba(59,130,246,0.25)', bgColor: 'rgba(59,130,246,0.02)' },
|
||||
security: { label: '安全中心', color: '#cc2200', borderColor: 'rgba(204,34,0,0.25)', bgColor: 'rgba(204,34,0,0.02)' },
|
||||
k3s: { label: 'K3s 叢集', color: '#A855F7', borderColor: 'rgba(168,85,247,0.25)', bgColor: 'rgba(168,85,247,0.02)' },
|
||||
ai_web: { label: 'AI/數據中心', color: '#F97316', borderColor: 'rgba(249,115,22,0.25)', bgColor: 'rgba(249,115,22,0.02)' },
|
||||
// C2: 群組標籤用 key 而非硬編碼中文(i18n 在元件層處理)
|
||||
const GROUP_CONFIG: Record<string, { labelKey: string; color: string; borderColor: string; bgColor: string }> = {
|
||||
devops: { labelKey: 'infra', color: '#3B82F6', borderColor: 'rgba(59,130,246,0.25)', bgColor: 'rgba(59,130,246,0.02)' },
|
||||
security: { labelKey: 'security', color: '#cc2200', borderColor: 'rgba(204,34,0,0.25)', bgColor: 'rgba(204,34,0,0.02)' },
|
||||
k3s: { labelKey: 'k3s', color: '#A855F7', borderColor: 'rgba(168,85,247,0.25)', bgColor: 'rgba(168,85,247,0.02)' },
|
||||
ai_web: { labelKey: 'aiData', color: '#F97316', borderColor: 'rgba(249,115,22,0.25)', bgColor: 'rgba(249,115,22,0.02)' },
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -85,7 +86,8 @@ export function useTopologyData(): TopologyData {
|
||||
type: 'groupNode',
|
||||
position: { x: (hostIdx % 2) * 350, y: Math.floor(hostIdx / 2) * 300 },
|
||||
data: {
|
||||
label: `${config.label} (${host.ip.split('.').pop()})`,
|
||||
labelKey: config.labelKey,
|
||||
ipSuffix: host.ip.split('.').pop(),
|
||||
role: host.role,
|
||||
status: host.status,
|
||||
ip: host.ip,
|
||||
|
||||
@@ -13,8 +13,15 @@
|
||||
|
||||
import { memo, type CSSProperties } from 'react'
|
||||
import { type NodeProps } from '@xyflow/react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
|
||||
// C2: 群組標籤 i18n
|
||||
const GROUP_LABELS: Record<string, string> = {
|
||||
infra: 'groupInfra', security: 'groupSecurity', k3s: 'groupK3s', aiData: 'groupAiData',
|
||||
}
|
||||
|
||||
function GroupNodeInner({ data }: NodeProps) {
|
||||
const t = useTranslations('topology')
|
||||
const d = data as Record<string, any>
|
||||
const borderColor = d.borderColor || '#e0ddd4'
|
||||
const bgColor = d.bgColor || 'rgba(0,0,0,0.01)'
|
||||
@@ -39,15 +46,15 @@ function GroupNodeInner({ data }: NodeProps) {
|
||||
{/* 群組標題 */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
|
||||
<span style={{ fontSize: 12, fontWeight: 600, color: '#141413' }}>
|
||||
{d.label}
|
||||
{t(GROUP_LABELS[d.labelKey] || 'groupInfra')} (.{d.ipSuffix})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 摘要 */}
|
||||
<div style={{ fontSize: 10, color: '#555550' }}>
|
||||
{totalSvc} 服務 · {hasWarning
|
||||
? <span style={{ color: '#F59E0B' }}>⚠ {totalSvc - healthySvc} 異常</span>
|
||||
: <span style={{ color: '#22C55E' }}>✓ 全部健康</span>
|
||||
{totalSvc} {t('services')} · {hasWarning
|
||||
? <span style={{ color: '#F59E0B' }}>{totalSvc - healthySvc} {t('warning')}</span>
|
||||
: <span style={{ color: '#22C55E' }}>{t('allHealthy')}</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user