import { useState, useMemo } from 'react'; import type { AnalysisStats } from '../../hooks/useVesselAnalysis'; interface Props { stats: AnalysisStats; lastUpdated: number; isLoading: boolean; } /** unix ms → HH:MM 형식 */ function formatTime(ms: number): string { if (ms === 0) return '--:--'; const d = new Date(ms); const hh = String(d.getHours()).padStart(2, '0'); const mm = String(d.getMinutes()).padStart(2, '0'); return `${hh}:${mm}`; } export function AnalysisStatsPanel({ stats, lastUpdated, isLoading }: Props) { const [expanded, setExpanded] = useState(true); const isEmpty = useMemo(() => stats.total === 0, [stats.total]); const panelStyle: React.CSSProperties = { position: 'absolute', top: 60, right: 10, zIndex: 10, minWidth: 160, backgroundColor: 'rgba(12, 24, 37, 0.92)', border: '1px solid rgba(99, 179, 237, 0.25)', borderRadius: 8, color: '#e2e8f0', fontFamily: 'monospace, sans-serif', fontSize: 11, boxShadow: '0 4px 16px rgba(0, 0, 0, 0.5)', overflow: 'hidden', }; const headerStyle: React.CSSProperties = { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 10px', borderBottom: expanded ? '1px solid rgba(99, 179, 237, 0.15)' : 'none', cursor: 'default', userSelect: 'none', }; const toggleButtonStyle: React.CSSProperties = { background: 'none', border: 'none', color: '#94a3b8', cursor: 'pointer', fontSize: 10, padding: '0 2px', lineHeight: 1, }; const bodyStyle: React.CSSProperties = { padding: '8px 10px', }; const rowStyle: React.CSSProperties = { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 3, }; const labelStyle: React.CSSProperties = { color: '#94a3b8', }; const valueStyle: React.CSSProperties = { fontWeight: 700, color: '#e2e8f0', }; const dividerStyle: React.CSSProperties = { borderTop: '1px solid rgba(99, 179, 237, 0.15)', margin: '6px 0', }; const riskRowStyle: React.CSSProperties = { display: 'flex', gap: 6, justifyContent: 'space-between', marginTop: 4, }; const riskBadgeStyle: React.CSSProperties = { display: 'flex', alignItems: 'center', gap: 2, color: '#cbd5e1', fontSize: 10, }; return (
{/* 헤더 */}
AI 분석 {isLoading && ( 로딩중... )}
{formatTime(lastUpdated)}
{/* 본문 */} {expanded && (
{isEmpty ? (
분석 데이터 없음
) : ( <>
전체 {stats.total}
다크베셀 {stats.dark}
GPS스푸핑 {stats.spoofing}
선단수 {stats.clusterCount}
{/* 위험도 수치 행 */}
🔴 {stats.critical}
🟠 {stats.high}
🟡 {stats.medium}
🟢 {stats.low}
)}
)}
); }