import { useState, useEffect, useCallback } from 'react'; // TODO: 실제 API 연동 시 fetch 호출로 교체 interface VesselMonitorRow { institution: string; institutionCode: string; systemName: string; linkInfo: string; storagePlace: string; linkMethod: string; collectionCycle: string; collectionCount: string; isNormal: boolean; lastMessageTime: string; } /** 기관코드 → 원천기관명 매핑 */ const INSTITUTION_NAMES: Record = { BS: '부산항', BSN: '부산신항', DH: '동해안', DS: '대산항', GI: '경인항', GIC: '경인연안', GS: '군산항', IC: '인천항', JDC: '진도연안', JJ: '제주항', MP: '목포항', }; /** Mock 데이터 정의 (스크린샷 기반) */ const MOCK_DATA: Omit[] = [ { institutionCode: 'BS', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '439 / 499', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'BS', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '133 / 463', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'BSN', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '255 / 278', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'BSN', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '133 / 426', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'DH', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '수신대기중', collectionCount: '0', isNormal: false, lastMessageTime: '' }, { institutionCode: 'DH', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '수신대기중', collectionCount: '0', isNormal: false, lastMessageTime: '' }, { institutionCode: 'DS', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '0', isNormal: false, lastMessageTime: '2026-03-15 15:38:57' }, { institutionCode: 'DS', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '0', isNormal: false, lastMessageTime: '2026-03-15 15:38:56' }, { institutionCode: 'GI', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '120 / 136', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'GI', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '55 / 467', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'GIC', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '180 / 216', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'GIC', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '수신대기중', collectionCount: '0', isNormal: false, lastMessageTime: '' }, { institutionCode: 'GS', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '수신대기중', collectionCount: '0', isNormal: false, lastMessageTime: '' }, { institutionCode: 'GS', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '수신대기중', collectionCount: '0', isNormal: false, lastMessageTime: '' }, { institutionCode: 'IC', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '149 / 176', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'IC', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '55 / 503', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'JDC', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '433 / 524', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'JDC', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '256 / 1619', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'JJ', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '429 / 508', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'JJ', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '00:00:00', collectionCount: '160 / 1592', isNormal: true, lastMessageTime: '2026-03-25 10:29:09' }, { institutionCode: 'MP', systemName: 'VTS_AIS', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '수신대기중', collectionCount: '0', isNormal: false, lastMessageTime: '' }, { institutionCode: 'MP', systemName: 'VTS_RT', linkInfo: 'VTS', storagePlace: 'signal.t_dynamic_all_reply', linkMethod: 'KAFKA', collectionCycle: '수신대기중', collectionCount: '0', isNormal: false, lastMessageTime: '' }, ]; /** Mock fetch — TODO: 실제 API 연동 시 fetch 호출로 교체 */ function fetchVesselMonitorData(): Promise { return new Promise((resolve) => { setTimeout(() => { const rows: VesselMonitorRow[] = MOCK_DATA.map((d) => ({ ...d, institution: INSTITUTION_NAMES[d.institutionCode] ?? d.institutionCode, })); resolve(rows); }, 400); }); } /* ── 상태 뱃지 ── */ function StatusBadge({ loading, onCount, total }: { loading: boolean; onCount: number; total: number }) { if (loading) { return ( 조회 중... ); } const offCount = total - onCount; if (offCount === total) { return ( 전체 OFF ); } if (offCount > 0) { return ( 일부 OFF ({offCount}/{total}) ); } return ( 전체 정상 ); } /* ── 연결상태 셀 ── */ function ConnectionBadge({ isNormal, lastMessageTime }: { isNormal: boolean; lastMessageTime: string }) { if (isNormal) { return (
ON {lastMessageTime && ( {lastMessageTime} )}
); } return (
OFF {lastMessageTime && ( {lastMessageTime} )}
); } /* ── 테이블 ── */ const HEADERS = ['번호', '원천기관', '기관코드', '정보시스템명', '연계정보', '저장장소', '연계방식', '수집주기', '선박건수/신호건수', '연결상태']; function VesselTable({ rows, loading }: { rows: VesselMonitorRow[]; loading: boolean }) { return (
{HEADERS.map((h) => ( ))} {loading && rows.length === 0 ? Array.from({ length: 8 }).map((_, i) => ( {HEADERS.map((_, j) => ( ))} )) : rows.map((row, idx) => ( ))}
{h}
{idx + 1} {row.institution} {row.institutionCode} {row.systemName} {row.linkInfo} {row.storagePlace} {row.linkMethod} {row.collectionCycle} {row.collectionCount}
); } /* ── 메인 패널 ── */ export default function MonitorVesselPanel() { const [rows, setRows] = useState([]); const [loading, setLoading] = useState(false); const [lastUpdate, setLastUpdate] = useState(null); const fetchData = useCallback(async () => { setLoading(true); const data = await fetchVesselMonitorData(); setRows(data); setLoading(false); setLastUpdate(new Date()); }, []); useEffect(() => { let isMounted = true; if (rows.length === 0) { void Promise.resolve().then(() => { if (isMounted) void fetchData(); }); } return () => { isMounted = false; }; }, [rows.length, fetchData]); const onCount = rows.filter((r) => r.isNormal).length; return (
{/* 헤더 */}

선박위치정보 모니터링

{lastUpdate && ( 갱신: {lastUpdate.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit', second: '2-digit' })} )}
{/* 상태 표시줄 */}
연계 채널 {rows.length}개 (ON: {onCount} / OFF: {rows.length - onCount})
{/* 테이블 */}
); }