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>
245 lines
11 KiB
TypeScript
245 lines
11 KiB
TypeScript
interface TheoryItem {
|
||
title: string
|
||
source: string
|
||
desc: string
|
||
tags?: { label: string; color: string }[]
|
||
highlight?: boolean
|
||
}
|
||
|
||
interface TheorySection {
|
||
icon: string
|
||
title: string
|
||
color: string
|
||
bgTint: string
|
||
items: TheoryItem[]
|
||
dividerAfter?: number
|
||
dividerLabel?: string
|
||
}
|
||
|
||
const THEORY_SECTIONS: TheorySection[] = [
|
||
{
|
||
icon: '🚢', title: '방제선 성능 기준', color: 'var(--blue)', bgTint: 'rgba(59,130,246,.08)',
|
||
items: [
|
||
{
|
||
title: '해양경찰청 방제선 성능기준 고시',
|
||
source: '해양경찰청 고시 제2022-11호 | 방제선·방제정 등급별 회수용량·속력·펌프사양 기준 정의',
|
||
desc: '1~5등급 방제선 기준 · 회수능력(㎥/h) · 오일펜스 전장 탑재량 · WING 자산 등급 필터링 근거',
|
||
},
|
||
{
|
||
title: 'IMO OPRC 1990 — 방제자원 비축 기준',
|
||
source: 'International Convention on Oil Pollution Preparedness, Response and Co-operation | IMO, 1990',
|
||
desc: '국가 방제역량 비축 최저 기준 · 항만별 Tier 1/2/3 대응자원 분류 · 국내 방제자원 DB 설계 기초',
|
||
},
|
||
{
|
||
title: '해양오염방제업 등록기준 (해양환경관리법 시행규칙)',
|
||
source: '해양수산부령 | 별표 9 — 방제업 종류별 방제선·기자재 보유기준',
|
||
desc: '제1종·제2종 방제업 자산 보유기준 · 오일펜스 전장·회수기 용량 법적 최저기준 · WING 자산현황 적법성 검증 기준',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
icon: '🪢', title: '오일펜스·흡착재 규격', color: 'var(--boom, #f59e0b)', bgTint: 'rgba(245,158,11,.08)',
|
||
items: [
|
||
{
|
||
title: 'ASTM F625 — Standard Guide for Selecting Mechanical Oil Spill Equipment',
|
||
source: 'ASTM International | 오일펜스·회수기·흡착재 성능시험·선정 기준 가이드',
|
||
desc: '오일펜스 인장강도·부력기준 · 흡착포 흡수율(g/g) 측정법 · WING 자산 성능등급 분류 참조 기준',
|
||
},
|
||
{
|
||
title: '기름오염방제시 오일펜스 사용지침 (ITOPF TIP 03 한국어판)',
|
||
source: 'ITOPF | 해양경찰청·해양환경관리공단 번역, 2011',
|
||
desc: '커튼형·펜스형·해안용 규격분류 · 유속별 운용한계(0.7~3.0 kt) · 힘 계산식 F=100·A·V² · 앵커 파지력 기준표',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
icon: '⚙️', title: '방제자원 배치·동원 이론', color: 'var(--purple)', bgTint: 'rgba(168,85,247,.08)',
|
||
dividerAfter: 2, dividerLabel: '📐 최적화 수리모델 참고문헌',
|
||
items: [
|
||
{
|
||
title: 'An Emergency Scheduling Model for Oil Containment Boom in Dynamically Changing Marine Oil Spills',
|
||
source: 'Xu, Y. et al. | Ningbo Univ. | Systems 2025, 13, 716 · DOI: 10.3390/systems13080716',
|
||
desc: 'IMOGWO 다목적 최적화 · 스케줄링 시간+경제·생태손실 동시 최소화 · 동적 오일필름 기반 방제정 라우팅',
|
||
highlight: true,
|
||
},
|
||
{
|
||
title: 'Dynamic Resource Allocation to Support Oil Spill Response Planning',
|
||
source: 'Garrett, R.A. et al. | Eur. J. Oper. Res. 257:272–286, 2017',
|
||
desc: '불확실성 하 방제자원 동적 배분 최적화 · 시나리오별 비축량 산정 · WING 자산 우선순위 배치 알고리즘 이론 기반',
|
||
},
|
||
{
|
||
title: '해양오염방제 국가긴급방제계획 (NOSCP)',
|
||
source: '해양경찰청 | 국가긴급방제계획, 2023년판',
|
||
desc: 'Tier 3급 대형사고 자원 동원체계 · 기관별 역할분담·지휘계통 · WING 방제자산 연계 법적 근거',
|
||
},
|
||
{
|
||
title: 'A Mixed Integer Programming Approach to Improve Oil Spill Response Resource Allocation in the Canadian Arctic',
|
||
source: 'Das, T., Goerlandt, F. & Pelot, R. | Multimodal Transportation Vol.3 No.1, 100110, 2023',
|
||
desc: '혼합정수계획법으로 응급 방제자원 거점 위치 선택 + 자원 할당 동시 최적화. 비용·응답시간 트레이드오프 파레토 분석.',
|
||
highlight: true,
|
||
tags: [
|
||
{ label: 'MIP 수리모델', color: 'var(--purple)' },
|
||
{ label: '자원 위치 선택', color: 'var(--blue)' },
|
||
{ label: '북극해 적용', color: 'var(--cyan)' },
|
||
],
|
||
},
|
||
{
|
||
title: '유전알고리즘을 이용하여 최적화된 방제자원 배치안의 분포도 분석',
|
||
source: '김혜진, 김용혁 | 한국융합학회논문지 Vol.11 No.4, pp.11–16, 2020',
|
||
desc: 'GA(유전알고리즘)로 방제자원 배치 최적화 및 시뮬레이션 분포도 분석. 국내 해역 실정에 맞는 자원 배치 패턴 도출.',
|
||
highlight: true,
|
||
tags: [
|
||
{ label: 'GA 메타휴리스틱', color: 'var(--purple)' },
|
||
{ label: '국내 연구', color: 'var(--green, #22c55e)' },
|
||
{ label: '배치 분포도 분석', color: 'var(--boom, #f59e0b)' },
|
||
],
|
||
},
|
||
{
|
||
title: 'A Two-Stage Stochastic Optimization Framework for Environmentally Sensitive Oil Spill Response Resource Allocation',
|
||
source: 'Rahman, M.A., Kuhel, M.T. & Novoa, C. | arXiv preprint arXiv:2511.22218, 2025',
|
||
desc: '확률적 MILP 2단계 프레임워크로 불확실성 포함 최적 자원 배치. 환경민감구역 가중치 반영.',
|
||
highlight: true,
|
||
tags: [
|
||
{ label: '확률적 MILP', color: 'var(--purple)' },
|
||
{ label: '2단계 최적화', color: 'var(--blue)' },
|
||
{ label: '환경민감구역', color: 'var(--green, #22c55e)' },
|
||
],
|
||
},
|
||
{
|
||
title: 'Mixed-Integer Dynamic Optimization for Oil-Spill Response Planning with Integration of Dynamic Oil Weathering Model',
|
||
source: 'You, F. & Leyffer, S. | Argonne National Laboratory Technical Note, 2008',
|
||
desc: '동적 최적화(MINLP/MILP) 프레임워크로 오일스필 대응 스케줄링 + 오일 풍화·거동 물리모델 통합.',
|
||
highlight: true,
|
||
tags: [
|
||
{ label: 'MINLP 동적 최적화', color: 'var(--purple)' },
|
||
{ label: '오일 풍화 모델 통합', color: 'var(--boom, #f59e0b)' },
|
||
],
|
||
},
|
||
],
|
||
},
|
||
{
|
||
icon: '🗄', title: '자산 현행화·데이터 관리', color: 'var(--green, #22c55e)', bgTint: 'rgba(34,197,94,.08)',
|
||
items: [
|
||
{
|
||
title: '해양오염방제자원 현황관리 지침',
|
||
source: '해양경찰청 예규 | 방제자원 등록·현행화·이력관리 절차 규정',
|
||
desc: '분기별 자산 실사 기준 · 자산분류코드 체계 · WING 업로드 양식(xlsx) 필드 정의 근거',
|
||
},
|
||
{
|
||
title: 'ISO 55000 — Asset Management: Overview, Principles and Terminology',
|
||
source: 'International Organization for Standardization | ISO 55000:2014',
|
||
desc: '자산 생애주기 관리 원칙 · 자산가치·상태 평가 프레임워크 · WING 자산 노후도·교체주기 산정 이론 기준',
|
||
},
|
||
],
|
||
},
|
||
]
|
||
|
||
const TAG_COLORS: Record<string, { bg: string; bd: string; fg: string }> = {
|
||
'var(--purple)': { bg: 'rgba(168,85,247,0.08)', bd: 'rgba(168,85,247,0.2)', fg: '#a855f7' },
|
||
'var(--blue)': { bg: 'rgba(59,130,246,0.08)', bd: 'rgba(59,130,246,0.2)', fg: '#3b82f6' },
|
||
'var(--cyan)': { bg: 'rgba(6,182,212,0.08)', bd: 'rgba(6,182,212,0.2)', fg: '#06b6d4' },
|
||
'var(--green, #22c55e)': { bg: 'rgba(34,197,94,0.08)', bd: 'rgba(34,197,94,0.2)', fg: '#22c55e' },
|
||
'var(--boom, #f59e0b)': { bg: 'rgba(245,158,11,0.08)', bd: 'rgba(245,158,11,0.2)', fg: '#f59e0b' },
|
||
}
|
||
|
||
function TheoryCard({ section }: { section: TheorySection }) {
|
||
const badgeBg = section.bgTint.replace(/[\d.]+\)$/, '0.15)')
|
||
return (
|
||
<div className="bg-bg-3 border border-border rounded-md overflow-hidden">
|
||
{/* Section Header */}
|
||
<div className="px-4 py-3 border-b border-border flex items-center gap-2" style={{ background: section.bgTint }}>
|
||
<span className="text-sm">{section.icon}</span>
|
||
<span className="text-xs font-bold" style={{ color: section.color }}>
|
||
{section.title}
|
||
</span>
|
||
</div>
|
||
|
||
{/* Items */}
|
||
<div className="px-4 py-3.5 flex flex-col gap-2 text-[9px]">
|
||
{section.items.map((item, i) => (
|
||
<div key={i}>
|
||
{/* Divider */}
|
||
{section.dividerAfter !== undefined && i === section.dividerAfter + 1 && (
|
||
<div className="mt-1 mb-3 pt-2" style={{ borderTop: '1px dashed var(--bd)' }}>
|
||
<div className="text-[8px] font-bold mb-1.5 opacity-70" style={{ color: section.color }}>
|
||
{section.dividerLabel}
|
||
</div>
|
||
</div>
|
||
)}
|
||
<div className="grid gap-2 px-2.5 py-2 bg-bg-0 rounded-md" style={{
|
||
gridTemplateColumns: '24px 1fr',
|
||
borderLeft: item.highlight ? `2px solid ${section.color}` : undefined,
|
||
}}>
|
||
{/* Number badge */}
|
||
<div className="w-5 h-5 rounded flex items-center justify-center text-[9px] shrink-0"
|
||
style={{
|
||
background: badgeBg,
|
||
fontWeight: item.highlight ? 700 : 400,
|
||
color: item.highlight ? section.color : undefined,
|
||
}}>
|
||
{['①','②','③','④','⑤','⑥','⑦','⑧','⑨','⑩'][i]}
|
||
</div>
|
||
<div>
|
||
<div className="font-bold mb-0.5">
|
||
{item.title}
|
||
</div>
|
||
<div className="text-text-3 leading-[1.6]">
|
||
{item.source}
|
||
</div>
|
||
{/* Tags */}
|
||
{item.tags && (
|
||
<div className="mt-0.5 flex flex-wrap gap-0.5">
|
||
{item.tags.map((tag, ti) => {
|
||
const tc = TAG_COLORS[tag.color] || { bg: 'rgba(107,114,128,0.08)', bd: 'rgba(107,114,128,0.2)', fg: '#6b7280' }
|
||
return (
|
||
<span key={ti} className="px-1 py-px rounded text-[8px]" style={{
|
||
color: tc.fg, background: tc.bg, border: `1px solid ${tc.bd}`,
|
||
}}>
|
||
{tag.label}
|
||
</span>
|
||
)
|
||
})}
|
||
</div>
|
||
)}
|
||
<div className="mt-0.5 text-text-2">
|
||
{item.desc}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
function AssetTheory() {
|
||
return (
|
||
<div className="flex flex-col gap-0">
|
||
<div className="text-[18px] font-bold mb-1">
|
||
📚 방제자원 이론
|
||
</div>
|
||
<div className="text-xs text-text-3 mb-6">
|
||
방제자산 운용 기준·성능 이론 및 관련 법령·고시 근거 문헌
|
||
</div>
|
||
|
||
<div className="grid gap-[18px] items-start" style={{ gridTemplateColumns: '1fr 1fr' }}>
|
||
{/* Left column */}
|
||
<div className="flex flex-col gap-3.5">
|
||
{THEORY_SECTIONS.slice(0, 2).map((sec) => (
|
||
<TheoryCard key={sec.title} section={sec} />
|
||
))}
|
||
</div>
|
||
{/* Right column */}
|
||
<div className="flex flex-col gap-3.5">
|
||
{THEORY_SECTIONS.slice(2).map((sec) => (
|
||
<TheoryCard key={sec.title} section={sec} />
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default AssetTheory
|