Files
LatencyTest/src/client/components/ResultsPanel.tsx

106 lines
3.6 KiB
TypeScript

import { TEST_NODES, LatencyResult, getLatencyColor, getLatencyLevel } from '@shared/types'
import { useLanguage } from '../contexts/LanguageContext'
import './ResultsPanel.css'
interface ResultsPanelProps {
results: Map<string, LatencyResult>
selectedNodeId: string | null
onNodeSelect: (nodeId: string | null) => void
}
export default function ResultsPanel({ results, selectedNodeId, onNodeSelect }: ResultsPanelProps) {
const { t } = useLanguage()
if (results.size === 0) return null
const sortedNodes = [...TEST_NODES].sort((a, b) => {
const aResult = results.get(a.id)
const bResult = results.get(b.id)
const aLatency = aResult?.latency ?? Infinity
const bLatency = bResult?.latency ?? Infinity
return aLatency - bLatency
})
const completedResults = sortedNodes
.map((node) => ({ node, result: results.get(node.id) }))
.filter(({ result }) => result?.status === 'success' || result?.status === 'failed')
const avgLatency =
completedResults.length > 0
? Math.round(
completedResults
.filter(({ result }) => result?.latency !== null)
.reduce((sum, { result }) => sum + (result?.latency ?? 0), 0) /
completedResults.filter(({ result }) => result?.latency !== null).length
)
: null
const regionMap: Record<string, string> = {
'North America': '北美',
'Europe': '欧洲',
'Asia': '亚洲',
'Middle East': '中东',
'South America': '南美',
'Africa': '非洲',
'Oceania': '大洋洲'
}
return (
<div className="results-panel">
<div className="results-header">
<h2>{t('测试结果', 'Test Results')}</h2>
<div className="results-header-right">
<div className="latency-legend">
<div className="legend-gradient"></div>
<div className="legend-labels">
<span>0ms</span>
<span>50</span>
<span>100</span>
<span>150</span>
<span>200</span>
<span>250+</span>
</div>
</div>
{avgLatency !== null && (
<div className="avg-latency">
{t('平均', 'Avg')}: <span style={{ color: getLatencyColor(avgLatency) }}>{avgLatency}ms</span>
</div>
)}
</div>
</div>
<div className="results-grid">
{sortedNodes.map((node) => {
const result = results.get(node.id)
const isTesting = result?.status === 'testing'
const hasResult = result?.status === 'success' || result?.status === 'failed'
const isSelected = selectedNodeId === node.id
return (
<div
key={node.id}
className={`result-card ${hasResult ? getLatencyLevel(result?.latency ?? null) : ''} ${isSelected ? 'selected' : ''}`}
onClick={() => onNodeSelect(isSelected ? null : node.id)}
>
<div className="result-region">{t(regionMap[node.region] || node.region, node.region)}</div>
<div className="result-name">{node.name}</div>
<div className="result-latency">
{isTesting ? (
<span className="testing-indicator">{t('测试中...', 'Testing...')}</span>
) : hasResult ? (
<span style={{ color: getLatencyColor(result?.latency ?? null) }}>
{result?.latency !== null ? `${result.latency}ms` : t('超时', 'Timeout')}
</span>
) : (
<span className="pending"></span>
)}
</div>
</div>
)
})}
</div>
</div>
)
}