/** * 선박 24h 특이운항 판별 구간 상세 패널. * 연속된 동일 카테고리 신호는 1개 구간으로 병합하여 시작~종료 시각과 함께 표시한다. */ import { Loader2, AlertTriangle, ShieldAlert } from 'lucide-react'; import { Card, CardContent } from '@shared/components/ui/card'; import { Badge } from '@shared/components/ui/badge'; import { formatDateTime } from '@shared/utils/dateFormat'; import { type AnomalySegment, getAnomalyCategoryIntent, getAnomalyCategoryLabel, } from './vesselAnomaly'; interface Props { segments: AnomalySegment[]; loading?: boolean; error?: string; totalHistoryCount: number; } function formatDuration(min: number): string { if (min <= 0) return '단일 샘플'; if (min < 60) return `${min}분`; const h = Math.floor(min / 60); const m = min % 60; return m === 0 ? `${h}시간` : `${h}시간 ${m}분`; } export function VesselAnomalyPanel({ segments, loading, error, totalHistoryCount }: Props) { const criticalCount = segments.filter((s) => s.severity === 'critical').length; const warningCount = segments.filter((s) => s.severity === 'warning').length; const infoCount = segments.filter((s) => s.severity === 'info').length; const totalSamples = segments.reduce((sum, s) => sum + s.pointCount, 0); return (
특이운항 판별 구간 {segments.length > 0 && ( · {segments.length}구간 {criticalCount > 0 && CRITICAL {criticalCount}} {warningCount > 0 && WARNING {warningCount}} {infoCount > 0 && INFO {infoCount}} )}
최근 24h 분석 {totalHistoryCount}건 중 {totalSamples}건 포함
{error && (
{error}
)} {loading && (
)} {!loading && !error && segments.length === 0 && totalHistoryCount > 0 && (
최근 24h 동안 Dark / Spoofing / 환적 / 어구위반 / 고위험 신호가 없습니다.
)} {!loading && !error && totalHistoryCount === 0 && (
분석 이력이 없습니다.
)} {!loading && segments.length > 0 && (
{segments.map((s) => (
{formatDateTime(s.startTime)} {s.startTime !== s.endTime && ( <> {formatDateTime(s.endTime)} )} {formatDuration(s.durationMin)} · {s.pointCount}회 연속 감지 {s.representativeLat != null && s.representativeLon != null && ( <> · {s.representativeLat.toFixed(3)}°N, {s.representativeLon.toFixed(3)}°E (이벤트 기준) )}
{s.categories.map((c) => ( {getAnomalyCategoryLabel(c)} ))}
{s.description}
))}
)}
); }