import { useState } from 'react' import { useReplayStore } from '../stores/replayStore' import { replayWebSocket } from '../services/replayWebSocket' import type maplibregl from 'maplibre-gl' import { getViewportBounds } from '../../vessel-map/utils/viewport' interface ReplaySetupPanelProps { map: maplibregl.Map | null } function getDefaultTimeRange(): { start: string; end: string } { const now = new Date() const hoursAgo = new Date(now.getTime() - 3 * 60 * 60 * 1000) const pad = (n: number) => String(n).padStart(2, '0') const fmt = (d: Date) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}` return { start: fmt(hoursAgo), end: fmt(now) } } export default function ReplaySetupPanel({ map }: ReplaySetupPanelProps) { const { start, end } = getDefaultTimeRange() const [startTime, setStartTime] = useState(start) const [endTime, setEndTime] = useState(end) const connectionState = useReplayStore((s) => s.connectionState) const querying = useReplayStore((s) => s.querying) const queryCompleted = useReplayStore((s) => s.queryCompleted) const receivedChunks = useReplayStore((s) => s.receivedChunks) const totalChunks = useReplayStore((s) => s.totalChunks) const handleConnect = async () => { const wsUrl = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/signal-batch/ws-tracks` try { await replayWebSocket.connect(wsUrl) } catch (err) { console.error('[Replay] Connection failed:', err) } } const handleQuery = () => { if (!map) return const bounds = getViewportBounds(map) const zoom = map.getZoom() const startISO = startTime.replace('T', ' ') + ':00' const endISO = endTime.replace('T', ' ') + ':00' replayWebSocket.executeQuery(startISO, endISO, bounds, zoom) } const handleCancel = () => { replayWebSocket.cancelQuery() } const isConnected = connectionState === 'connected' return (
{/* 연결 상태 */}
{connectionState === 'connected' ? '연결됨' : connectionState === 'connecting' ? '연결 중...' : connectionState === 'error' ? '연결 실패' : '미연결'}
{!isConnected && ( )}
{/* 시간 범위 */}
setStartTime(e.target.value)} className="w-full rounded-md border border-border bg-surface px-2 py-1.5 text-xs text-foreground focus:border-primary focus:outline-none" />
setEndTime(e.target.value)} className="w-full rounded-md border border-border bg-surface px-2 py-1.5 text-xs text-foreground focus:border-primary focus:outline-none" />
{/* 쿼리 버튼 */} {/* 진행 상태 */} {(querying || queryCompleted) && (
{querying && (
0 ? `${(receivedChunks / totalChunks) * 100}%` : '50%', animation: totalChunks === 0 ? 'pulse 1.5s ease-in-out infinite' : undefined, }} />
{receivedChunks} 청크
)} {queryCompleted && !querying && ( {receivedChunks} 청크 수신 완료 )}
)}
) }