import { useState, useRef, useEffect } from 'react'; import type { PredictionModel } from './OilSpillView'; interface RecalcModalProps { isOpen: boolean; onClose: () => void; incidentName: string; oilType: string; spillAmount: number; spillType: string; predictionTime: number; incidentCoord: { lat: number; lon: number }; selectedModels: Set; onSubmit: (params: { oilType: string; spillAmount: number; spillType: string; predictionTime: number; incidentCoord: { lat: number; lon: number }; selectedModels: Set; }) => void; } type RecalcPhase = 'editing' | 'running' | 'done'; const OIL_TYPES = [ '벙커C유', '원유(중질)', '원유(경질)', '디젤유(경유)', '휘발유', '등유', '윤활유', 'HFO 380', 'HFO 180', ]; const SPILL_TYPES = ['연속', '순간', '점진적']; const PREDICTION_TIMES = [6, 12, 24, 48, 72, 96, 120]; const snapToValidTime = (t: number): number => PREDICTION_TIMES.includes(t) ? t : (PREDICTION_TIMES.find((h) => h >= t) ?? PREDICTION_TIMES[0]); export function RecalcModal({ isOpen, onClose, incidentName, oilType: initOilType, spillAmount: initSpillAmount, spillType: initSpillType, predictionTime: initPredictionTime, incidentCoord: initCoord, selectedModels: initModels, onSubmit, }: RecalcModalProps) { const backdropRef = useRef(null); const [oilType, setOilType] = useState(initOilType); const [spillAmount, setSpillAmount] = useState(initSpillAmount); const [spillUnit, setSpillUnit] = useState<'kl' | 'ton' | 'bbl'>('kl'); const [spillType, setSpillType] = useState(initSpillType); const [predictionTime, setPredictionTime] = useState(() => snapToValidTime(initPredictionTime)); const [lat, setLat] = useState(initCoord.lat); const [lon, setLon] = useState(initCoord.lon); const [models, setModels] = useState>(new Set(initModels)); const [phase, setPhase] = useState('editing'); // Sync when modal opens /* eslint-disable react-hooks/set-state-in-effect */ useEffect(() => { if (isOpen) { setOilType(initOilType); setSpillAmount(initSpillAmount); setSpillType(initSpillType); setPredictionTime(snapToValidTime(initPredictionTime)); setLat(initCoord.lat); setLon(initCoord.lon); setModels(new Set(initModels)); setPhase('editing'); } }, [ isOpen, initOilType, initSpillAmount, initSpillType, initPredictionTime, initCoord.lat, initCoord.lon, initModels, ]); /* eslint-enable react-hooks/set-state-in-effect */ useEffect(() => { const handler = (e: MouseEvent) => { if (e.target === backdropRef.current) onClose(); }; if (isOpen) document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, [isOpen, onClose]); const toggleModel = (model: PredictionModel) => { setModels((prev) => { const next = new Set(prev); if (next.has(model)) next.delete(model); else next.add(model); return next; }); }; const handleRun = () => { setPhase('running'); setTimeout(() => { setPhase('done'); setTimeout(() => { onSubmit({ oilType, spillAmount, spillType, predictionTime, incidentCoord: { lat, lon }, selectedModels: models, }); onClose(); }, 1000); }, 2500); }; if (!isOpen) return null; return (
{/* Header */}
🔄

확산예측 재계산

유출유·유출량 등 파라미터를 수정하여 재실행
{/* Scrollable Content */}
{/* 현재 분석 정보 */}
현재 분석 정보
{/* 유종 */} {/* 유출량 */}
setSpillAmount(Number(e.target.value))} />
{/* 유출 형태 */} {/* 예측 시간 */} {/* 유출 위치 */}
위도 (N)
setLat(Number(e.target.value))} />
경도 (E)
setLon(Number(e.target.value))} />
{/* 모델 선택 */}
{[ { model: 'KOSPS' as PredictionModel, color: 'var(--color-accent)', ready: false }, { model: 'POSEIDON' as PredictionModel, color: 'var(--color-danger)', ready: true }, { model: 'OpenDrift' as PredictionModel, color: 'var(--color-info)', ready: true }, ].map(({ model, color, ready }) => ( ))}
{/* Footer */}
); } function FieldGroup({ label, children }: { label: string; children: React.ReactNode }) { return (
{label}
{children}
); } function InfoItem({ label, value }: { label: string; value: string }) { return (
{label} {value}
); }