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, 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} /> {/* 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} />
) }