Phase 2: 정적 인라인 스타일을 Tailwind className으로 변환 - common/: MapView, BacktrackReplayBar, LoginPage, LayerTree, ComboBox, SubMenuBar - hns/: HNSSubstanceView, HNSScenarioView, HNSView, HNSLeftPanel 등 8파일 - prediction/: BoomDeploymentTheoryView, OilBoomSection, RecalcModal, RightPanel 등 8파일 - incidents/: IncidentsView, IncidentsLeftPanel, IncidentsRightPanel - rescue/: RescueScenarioView - aerial/: SatelliteRequest, AerialTheoryView - assets/: ShipInsurance, AssetTheory, AssetManagement 등 5파일 - board/: BoardView - reports/: ReportsView, OilSpillReportTemplate, ReportGenerator - weather/: WeatherMapOverlay, WeatherView, WeatherRightPanel 변환 패턴: color/background/border/borderRadius/display/flex/gap/fontSize/fontWeight → Tailwind 동적 스타일(rgba, gradient, 삼항 조건부, 런타임 변수)은 style prop에 유지 JS 번들: 2,921KB → 2,897KB (-24KB) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
144 lines
5.0 KiB
TypeScript
Executable File
144 lines
5.0 KiB
TypeScript
Executable File
interface HNSRightPanelProps {
|
|
dispersionResult: {
|
|
zones: Array<{
|
|
level: string
|
|
color: string
|
|
radius: number
|
|
angle: number
|
|
}>
|
|
timestamp: string
|
|
windDirection: number
|
|
substance: string
|
|
concentration: {
|
|
'AEGL-3': string
|
|
'AEGL-2': string
|
|
'AEGL-1': string
|
|
}
|
|
} | null
|
|
onOpenRecalc?: () => void
|
|
onOpenReport?: () => void
|
|
}
|
|
|
|
export function HNSRightPanel({ dispersionResult, onOpenRecalc, onOpenReport }: HNSRightPanelProps) {
|
|
if (!dispersionResult) {
|
|
return (
|
|
<div className="w-[300px] bg-bg-1 border-l border-border p-4 overflow-auto">
|
|
<div className="flex flex-col gap-3 items-center justify-center h-full text-text-3 text-xs">
|
|
<div style={{ fontSize: '32px', opacity: 0.3 }}>📊</div>
|
|
<div>예측 실행 후 결과가 표시됩니다</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="w-[300px] bg-bg-1 border-l border-border p-4 overflow-auto flex flex-col gap-4">
|
|
{/* Header */}
|
|
<div>
|
|
<div className="flex items-center gap-1.5 mb-2">
|
|
<div style={{
|
|
width: '6px',
|
|
height: '6px',
|
|
borderRadius: '50%',
|
|
background: 'var(--orange)',
|
|
animation: 'pulse 1.5s infinite'
|
|
}}></div>
|
|
<h3 className="text-[13px] font-bold m-0">
|
|
예측 결과
|
|
</h3>
|
|
</div>
|
|
<div className="text-[10px] text-text-3 font-mono">
|
|
{dispersionResult.substance} · ALOHA v5.4.7
|
|
</div>
|
|
</div>
|
|
|
|
{/* KPI Cards */}
|
|
<div className="flex flex-col gap-2">
|
|
<div className="p-3 bg-bg-3 border border-[rgba(6,182,212,0.2)] rounded-[var(--rS)]">
|
|
<div className="text-[10px] text-text-3 mb-1.5">
|
|
평균 확산 면적
|
|
</div>
|
|
<div className="text-[20px] font-bold font-mono text-primary-cyan">
|
|
8.2 <span className="text-[10px] font-medium">km²</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-3 bg-bg-3 border border-[rgba(249,115,22,0.2)] rounded-[var(--rS)]">
|
|
<div className="text-[10px] text-text-3 mb-1.5">
|
|
고위험 구역
|
|
</div>
|
|
<div className="text-[20px] font-bold font-mono text-status-orange">
|
|
2
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-3 bg-bg-3 border border-border rounded-[var(--rS)]">
|
|
<div className="text-[10px] text-text-3 mb-1.5">
|
|
평균 풍속
|
|
</div>
|
|
<div className="text-[20px] font-bold font-mono">
|
|
5.2 <span className="text-[10px] font-medium">m/s</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-3 bg-bg-3 border border-border rounded-[var(--rS)]">
|
|
<div className="text-[10px] text-text-3 mb-1.5">
|
|
풍향
|
|
</div>
|
|
<div className="text-[20px] font-bold font-mono">
|
|
SW <span className="text-[10px] font-medium">225°</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Zone Details */}
|
|
<div>
|
|
<h4 className="text-[11px] font-semibold text-text-2 mt-0 mb-2.5">
|
|
확산 구역 상세
|
|
</h4>
|
|
<div className="flex flex-col gap-2">
|
|
{dispersionResult.zones.map((zone, idx) => (
|
|
<div
|
|
key={idx}
|
|
className="py-2.5 px-3 bg-bg-2 rounded-[var(--rS)]"
|
|
style={{
|
|
borderLeft: `3px solid ${zone.color.replace('0.4', '1').replace('0.3', '1').replace('0.25', '1')}`
|
|
}}
|
|
>
|
|
<div className="flex justify-between items-center mb-1">
|
|
<span className="text-[11px] font-semibold">
|
|
{zone.level}
|
|
</span>
|
|
<span className="text-[10px] font-mono text-text-3">
|
|
{zone.radius}m
|
|
</span>
|
|
</div>
|
|
<div className="text-[10px] text-text-3">
|
|
{dispersionResult.concentration[zone.level as keyof typeof dispersionResult.concentration]}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Timestamp */}
|
|
<div className="mt-auto pt-3 border-t border-border text-[10px] text-text-3 font-mono">
|
|
예측 시각: {new Date(dispersionResult.timestamp).toLocaleString('ko-KR')}
|
|
</div>
|
|
|
|
{/* Bottom Action Buttons */}
|
|
<div className="flex gap-1.5 pt-3 border-t border-border">
|
|
<button className="flex-1 py-2 px-1 rounded text-[11px] font-semibold bg-gradient-to-r from-boom to-[#d97706] text-black font-korean">
|
|
💾 저장
|
|
</button>
|
|
<button onClick={onOpenRecalc} className="flex-1 py-2 px-1 rounded text-[11px] font-semibold bg-[rgba(249,115,22,0.1)] border border-[rgba(249,115,22,0.3)] text-status-orange font-korean">
|
|
🔄 재계산
|
|
</button>
|
|
<button onClick={onOpenReport} className="flex-1 py-2 px-1 rounded text-[11px] font-semibold bg-gradient-to-r from-primary-cyan to-primary-blue text-white font-korean">
|
|
📄 보고서
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|