import { useState } from 'react'; import type { LeftPanelProps, ExpandedSections } from './leftPanelTypes'; interface CategoryMeta { icon: string; bg: string; } const CATEGORY_ICON_MAP: Record = { // 수산자원 / 양식장 (green) 어장정보: { icon: '🐟', bg: 'rgba(34,197,94,0.15)' }, 양식장: { icon: '🦪', bg: 'rgba(34,197,94,0.15)' }, 양식어업: { icon: '🦪', bg: 'rgba(34,197,94,0.15)' }, 어류양식장: { icon: '🐟', bg: 'rgba(34,197,94,0.15)' }, 패류양식장: { icon: '🦪', bg: 'rgba(34,197,94,0.15)' }, 해조류양식장: { icon: '🌿', bg: 'rgba(34,197,94,0.15)' }, 가두리양식장: { icon: '🔲', bg: 'rgba(34,197,94,0.15)' }, 갑각류양식장: { icon: '🦐', bg: 'rgba(34,197,94,0.15)' }, 기타양식장: { icon: '📦', bg: 'rgba(34,197,94,0.15)' }, 영세어업: { icon: '🎣', bg: 'rgba(34,197,94,0.15)' }, 유어장: { icon: '🎣', bg: 'rgba(34,197,94,0.15)' }, 수산시장: { icon: '🐟', bg: 'rgba(34,197,94,0.15)' }, 인공어초: { icon: '🪸', bg: 'rgba(34,197,94,0.15)' }, 암초: { icon: '🪨', bg: 'rgba(34,197,94,0.15)' }, 침선: { icon: '🚢', bg: 'rgba(34,197,94,0.15)' }, // 관광자원 / 낚시 (yellow) 해수욕장: { icon: '🏖', bg: 'rgba(250,204,21,0.15)' }, 갯바위낚시: { icon: '🪨', bg: 'rgba(250,204,21,0.15)' }, 선상낚시: { icon: '🚤', bg: 'rgba(250,204,21,0.15)' }, 마리나항: { icon: '⛵', bg: 'rgba(250,204,21,0.15)' }, // 항만 / 산업시설 (blue) 무역항: { icon: '🚢', bg: 'rgba(99,179,237,0.15)' }, 연안항: { icon: '⛵', bg: 'rgba(99,179,237,0.15)' }, 국가어항: { icon: '⚓', bg: 'rgba(99,179,237,0.15)' }, 지방어항: { icon: '⚓', bg: 'rgba(99,179,237,0.15)' }, 어항: { icon: '⚓', bg: 'rgba(99,179,237,0.15)' }, 항만구역: { icon: '⚓', bg: 'rgba(99,179,237,0.15)' }, 항로: { icon: '🚢', bg: 'rgba(99,179,237,0.15)' }, 정박지: { icon: '⛵', bg: 'rgba(99,179,237,0.15)' }, 항로표지: { icon: '🔴', bg: 'rgba(99,179,237,0.15)' }, 해수취수시설: { icon: '💧', bg: 'rgba(99,179,237,0.15)' }, '취수구·배수구': { icon: '🚰', bg: 'rgba(99,179,237,0.15)' }, LNG: { icon: '⚡', bg: 'rgba(99,179,237,0.15)' }, 발전소: { icon: '🔌', bg: 'rgba(99,179,237,0.15)' }, '발전소·산단': { icon: '🏭', bg: 'rgba(99,179,237,0.15)' }, 임해공단: { icon: '🏭', bg: 'rgba(99,179,237,0.15)' }, 저유시설: { icon: '🛢', bg: 'rgba(99,179,237,0.15)' }, '해저케이블·배관': { icon: '🔌', bg: 'rgba(99,179,237,0.15)' }, // 환경 / 생태 (lime) 갯벌: { icon: '🪨', bg: 'rgba(163,230,53,0.12)' }, 해안선_ESI: { icon: '🏖', bg: 'rgba(163,230,53,0.12)' }, 보호지역: { icon: '🛡', bg: 'rgba(163,230,53,0.12)' }, 해양보호구역: { icon: '🌿', bg: 'rgba(163,230,53,0.12)' }, 철새도래지: { icon: '🐦', bg: 'rgba(163,230,53,0.12)' }, 습지보호구역: { icon: '🏖', bg: 'rgba(163,230,53,0.12)' }, 보호종서식지: { icon: '🐢', bg: 'rgba(163,230,53,0.12)' }, '보호종 서식지': { icon: '🐢', bg: 'rgba(163,230,53,0.12)' }, }; const FALLBACK_META: CategoryMeta = { icon: '🌊', bg: 'rgba(148,163,184,0.15)' }; import PredictionInputSection from './PredictionInputSection'; import InfoLayerSection from './InfoLayerSection'; import OilBoomSection from './OilBoomSection'; export type { LeftPanelProps }; export function LeftPanel({ selectedAnalysis, enabledLayers, onToggleLayer, accidentTime, onAccidentTimeChange, incidentCoord, onCoordChange, isSelectingLocation, onMapSelectClick, onRunSimulation, isRunningSimulation, selectedModels, onModelsChange, visibleModels, onVisibleModelsChange, hasResults, predictionTime, onPredictionTimeChange, spillType, onSpillTypeChange, oilType, onOilTypeChange, spillAmount, onSpillAmountChange, incidentName, onIncidentNameChange, spillUnit, onSpillUnitChange, boomLines, onBoomLinesChange, oilTrajectory, algorithmSettings, onAlgorithmSettingsChange, isDrawingBoom, onDrawingBoomChange, drawingPoints, onDrawingPointsChange, containmentResult, onContainmentResultChange, layerOpacity, onLayerOpacityChange, layerBrightness, onLayerBrightnessChange, layerColors, onLayerColorChange, sensitiveResources = [], onImageAnalysisResult, validationErrors, }: LeftPanelProps) { const [expandedSections, setExpandedSections] = useState({ predictionInput: true, incident: false, impactResources: false, infoLayer: false, oilBoom: false, }); const toggleSection = (section: keyof ExpandedSections) => { setExpandedSections((prev) => ({ ...prev, [section]: !prev[section], })); }; return (
{/* Scrollable Content */}
{/* Prediction Input Section */} toggleSection('predictionInput')} accidentTime={accidentTime} onAccidentTimeChange={onAccidentTimeChange} incidentCoord={incidentCoord} onCoordChange={onCoordChange} isSelectingLocation={isSelectingLocation} onMapSelectClick={onMapSelectClick} onRunSimulation={onRunSimulation} isRunningSimulation={isRunningSimulation} selectedModels={selectedModels} onModelsChange={onModelsChange} visibleModels={visibleModels} onVisibleModelsChange={onVisibleModelsChange} hasResults={hasResults} predictionTime={predictionTime} onPredictionTimeChange={onPredictionTimeChange} spillType={spillType} onSpillTypeChange={onSpillTypeChange} oilType={oilType} onOilTypeChange={onOilTypeChange} spillAmount={spillAmount} onSpillAmountChange={onSpillAmountChange} incidentName={incidentName} onIncidentNameChange={onIncidentNameChange} spillUnit={spillUnit} onSpillUnitChange={onSpillUnitChange} onImageAnalysisResult={onImageAnalysisResult} validationErrors={validationErrors} /> {/* Incident Section */}
toggleSection('incident')} className="flex items-center justify-between p-4 cursor-pointer hover:bg-[rgba(255,255,255,0.02)]" >

사고정보

{expandedSections.incident ? '▼' : '▶'}
{expandedSections.incident && (selectedAnalysis ? (
{/* Status Badge */} {(() => { const statusMap: Record = { ACTIVE: { label: '진행중', style: 'bg-[rgba(239,68,68,0.15)] text-color-danger border border-[rgba(239,68,68,0.3)]', dot: 'bg-color-danger animate-pulse', }, INVESTIGATING: { label: '조사중', style: 'bg-[rgba(249,115,22,0.15)] text-color-warning border border-[rgba(249,115,22,0.3)]', dot: 'bg-color-warning animate-pulse', }, CLOSED: { label: '종료', style: 'bg-[rgba(100,116,139,0.15)] text-fg-disabled border border-[rgba(100,116,139,0.3)]', dot: 'bg-fg-disabled', }, }; const s = statusMap[selectedAnalysis.acdntSttsCd] ?? statusMap['ACTIVE']; return (
{s.label}
); })()} {/* Info Grid */}
사고코드 {selectedAnalysis.acdntSn}
사고명 {selectedAnalysis.acdntNm || '—'}
사고일시 {selectedAnalysis.occurredAt ? selectedAnalysis.occurredAt.slice(0, 16).replace(' ', 'T') : '—'}
유종 {selectedAnalysis.oilType || '—'}
유출량 {selectedAnalysis.volume != null ? `${selectedAnalysis.volume.toFixed(2)} kl` : '—'}
담당자 {selectedAnalysis.analyst || '—'}
위치 {selectedAnalysis.location || '—'}
) : (

선택된 사고정보가 없습니다.

))}
{/* Impact Resources Section */}
toggleSection('impactResources')} className="flex items-center justify-between p-4 cursor-pointer hover:bg-[rgba(255,255,255,0.02)]" >

영향 민감자원

{expandedSections.impactResources ? '▼' : '▶'}
{expandedSections.impactResources && (
{sensitiveResources.length === 0 ? (

영향받는 민감자원 목록

) : (
{sensitiveResources.map(({ category, count, totalArea }) => { const meta = CATEGORY_ICON_MAP[category] ?? FALLBACK_META; return (
{meta.icon} {category}
{totalArea != null ? `${totalArea.toLocaleString('ko-KR', { maximumFractionDigits: 2 })} ha` : `${count}개`}
); })}
)}
)}
{/* Info Layer Section */} toggleSection('infoLayer')} enabledLayers={enabledLayers} onToggleLayer={onToggleLayer} layerOpacity={layerOpacity} onLayerOpacityChange={onLayerOpacityChange} layerBrightness={layerBrightness} onLayerBrightnessChange={onLayerBrightnessChange} layerColors={layerColors} onLayerColorChange={onLayerColorChange} /> {/* Oil Boom Placement Guide Section */} toggleSection('oilBoom')} boomLines={boomLines} onBoomLinesChange={onBoomLinesChange} oilTrajectory={oilTrajectory} incidentCoord={incidentCoord ?? { lat: 0, lon: 0 }} algorithmSettings={algorithmSettings} onAlgorithmSettingsChange={onAlgorithmSettingsChange} isDrawingBoom={isDrawingBoom} onDrawingBoomChange={onDrawingBoomChange} drawingPoints={drawingPoints} onDrawingPointsChange={onDrawingPointsChange} containmentResult={containmentResult} onContainmentResultChange={onContainmentResultChange} />
); }