wing-ops/frontend/src/tabs/hns/components/HNSSubstanceView.tsx

1024 lines
84 KiB
TypeScript
Executable File
Raw Blame 히스토리

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useRef, useEffect, useCallback } from 'react'
import { sanitizeHtml } from '@common/utils/sanitize'
import { api } from '@common/services/api'
import type { HNSSearchSubstance } from '@common/types/hns'
/* ═══ HNS 물질 데이터베이스 ═══ */
interface HNSSubstance {
name: string
nameEn: string
formula: string
unNumber: string
casNumber: string
imdgClass: string
sebc: string
flashPoint: string
boilingPoint: string
specificGravity: string
vaporPressure: string
solubility: string
idlh: string
twa: string
aegl1: string
aegl2: string
aegl3: string
category: string
color: string
}
const substances: HNSSubstance[] = [
{ name: '톨루엔', nameEn: 'Toluene', formula: 'C₇H₈', unNumber: 'UN1294', casNumber: '108-88-3', imdgClass: 'Class 3', sebc: 'E (증발)', flashPoint: '4°C', boilingPoint: '111°C', specificGravity: '0.867', vaporPressure: '28.4 mmHg', solubility: '0.05%', idlh: '500 ppm', twa: '20 ppm', aegl1: '37 ppm', aegl2: '150 ppm', aegl3: '500 ppm', category: 'flammable_liquid', color: '#06b6d4' },
{ name: '벤젠', nameEn: 'Benzene', formula: 'C₆H₆', unNumber: 'UN1114', casNumber: '71-43-2', imdgClass: 'Class 3', sebc: 'E (증발)', flashPoint: '-11°C', boilingPoint: '80°C', specificGravity: '0.879', vaporPressure: '94.8 mmHg', solubility: '0.18%', idlh: '500 ppm', twa: '0.5 ppm', aegl1: '9 ppm', aegl2: '800 ppm', aegl3: '4,000 ppm', category: 'flammable_liquid', color: '#ef4444' },
{ name: '암모니아', nameEn: 'Ammonia', formula: 'NH₃', unNumber: 'UN1005', casNumber: '7664-41-7', imdgClass: 'Class 2.3', sebc: 'G (가스)', flashPoint: '-', boilingPoint: '-33°C', specificGravity: '0.73', vaporPressure: '8,585 mmHg', solubility: '31%', idlh: '300 ppm', twa: '25 ppm', aegl1: '30 ppm', aegl2: '160 ppm', aegl3: '1,100 ppm', category: 'toxic_gas', color: '#a855f7' },
{ name: '메탄올', nameEn: 'Methanol', formula: 'CH₃OH', unNumber: 'UN1230', casNumber: '67-56-1', imdgClass: 'Class 3', sebc: 'D (용해)', flashPoint: '11°C', boilingPoint: '65°C', specificGravity: '0.791', vaporPressure: '127 mmHg', solubility: '완전 용해', idlh: '6,000 ppm', twa: '200 ppm', aegl1: '-', aegl2: '2,100 ppm', aegl3: '14,000 ppm', category: 'flammable_liquid', color: '#3b82f6' },
{ name: '자일렌', nameEn: 'Xylene', formula: 'C₈H₁₀', unNumber: 'UN1307', casNumber: '1330-20-7', imdgClass: 'Class 3', sebc: 'F (부유)', flashPoint: '27°C', boilingPoint: '144°C', specificGravity: '0.864', vaporPressure: '8.8 mmHg', solubility: '0.02%', idlh: '900 ppm', twa: '100 ppm', aegl1: '130 ppm', aegl2: '920 ppm', aegl3: '2,500 ppm', category: 'flammable_liquid', color: '#f97316' },
{ name: '스타이렌', nameEn: 'Styrene', formula: 'C₈H₈', unNumber: 'UN2055', casNumber: '100-42-5', imdgClass: 'Class 3', sebc: 'F (부유)', flashPoint: '31°C', boilingPoint: '145°C', specificGravity: '0.906', vaporPressure: '6.4 mmHg', solubility: '0.03%', idlh: '700 ppm', twa: '20 ppm', aegl1: '20 ppm', aegl2: '130 ppm', aegl3: '1,100 ppm', category: 'flammable_liquid', color: '#eab308' },
{ name: '아세톤', nameEn: 'Acetone', formula: 'C₃H₆O', unNumber: 'UN1090', casNumber: '67-64-1', imdgClass: 'Class 3', sebc: 'E (증발)', flashPoint: '-20°C', boilingPoint: '56°C', specificGravity: '0.784', vaporPressure: '231 mmHg', solubility: '완전 용해', idlh: '2,500 ppm', twa: '500 ppm', aegl1: '200 ppm', aegl2: '3,200 ppm', aegl3: '13,000 ppm', category: 'flammable_liquid', color: '#22c55e' },
{ name: '염소', nameEn: 'Chlorine', formula: 'Cl₂', unNumber: 'UN1017', casNumber: '7782-50-5', imdgClass: 'Class 2.3', sebc: 'G (가스)', flashPoint: '-', boilingPoint: '-34°C', specificGravity: '1.56', vaporPressure: '6,627 mmHg', solubility: '0.7%', idlh: '10 ppm', twa: '0.5 ppm', aegl1: '0.5 ppm', aegl2: '2 ppm', aegl3: '20 ppm', category: 'toxic_gas', color: '#ef4444' },
{ name: '수소', nameEn: 'Hydrogen', formula: 'H₂', unNumber: 'UN1049', casNumber: '1333-74-0', imdgClass: 'Class 2.1', sebc: 'G (가스)', flashPoint: '-', boilingPoint: '-253°C', specificGravity: '0.07', vaporPressure: '-', solubility: '0.0016%', idlh: '-', twa: '-', aegl1: '-', aegl2: '-', aegl3: '-', category: 'flammable_gas', color: '#06b6d4' },
{ name: 'LPG', nameEn: 'LPG (Propane/Butane)', formula: 'C₃H₈/C₄H₁₀', unNumber: 'UN1075', casNumber: '68476-85-7', imdgClass: 'Class 2.1', sebc: 'G (가스)', flashPoint: '-104°C', boilingPoint: '-42°C', specificGravity: '0.50', vaporPressure: '8,460 mmHg', solubility: '0.01%', idlh: '2,100 ppm', twa: '1,000 ppm', aegl1: '-', aegl2: '17,000 ppm', aegl3: '33,000 ppm', category: 'flammable_gas', color: '#f97316' },
{ name: '에틸렌', nameEn: 'Ethylene', formula: 'C₂H₄', unNumber: 'UN1962', casNumber: '74-85-1', imdgClass: 'Class 2.1', sebc: 'G (가스)', flashPoint: '-', boilingPoint: '-104°C', specificGravity: '0.57', vaporPressure: '-', solubility: '0.01%', idlh: '-', twa: '-', aegl1: '-', aegl2: '-', aegl3: '-', category: 'flammable_gas', color: '#a855f7' },
{ name: '1,2-디클로로에탄', nameEn: '1,2-Dichloroethane (EDC)', formula: 'C₂H₄Cl₂', unNumber: 'UN1184', casNumber: '107-06-2', imdgClass: 'Class 3', sebc: 'S (침강)', flashPoint: '13°C', boilingPoint: '83°C', specificGravity: '1.253', vaporPressure: '87 mmHg', solubility: '0.87%', idlh: '50 ppm', twa: '1 ppm', aegl1: '-', aegl2: '20 ppm', aegl3: '200 ppm', category: 'toxic_liquid', color: '#ef4444' },
{ name: '페놀', nameEn: 'Phenol', formula: 'C₆H₅OH', unNumber: 'UN2312', casNumber: '108-95-2', imdgClass: 'Class 6.1', sebc: 'S/SD (침강/용해)', flashPoint: '79°C', boilingPoint: '182°C', specificGravity: '1.07', vaporPressure: '0.35 mmHg', solubility: '8.4%', idlh: '250 ppm', twa: '5 ppm', aegl1: '19 ppm', aegl2: '29 ppm', aegl3: '57 ppm', category: 'toxic_liquid', color: '#22c55e' },
]
const categories = [
{ id: 'all', label: '전체', icon: '📋' },
{ id: 'toxic_liquid', label: '유독성 액체', icon: '🧪' },
{ id: 'toxic_gas', label: '유독성 가스', icon: '☠️' },
{ id: 'flammable_liquid', label: '인화성 액체', icon: '🔥' },
{ id: 'flammable_gas', label: '인화성 가스', icon: '💨' },
]
export function HNSSubstanceView() {
const [activeTab, setActiveTab] = useState(0)
const [selectedCategory, setSelectedCategory] = useState('all')
const [searchQuery, setSearchQuery] = useState('')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [detailSearchName, setDetailSearchName] = useState('')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [detailSearchCas, setDetailSearchCas] = useState('')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [detailSearchSebc, setDetailSearchSebc] = useState('전체 거동분류')
/* Panel 3: 물질 상세검색 state */
const [hmsSearchType, setHmsSearchType] = useState<'all' | 'abbr' | 'korName' | 'engName' | 'cas' | 'un'>('all')
const [hmsSearchInput, setHmsSearchInput] = useState('')
const [hmsFilterSebc, setHmsFilterSebc] = useState('전체 거동분류')
const [hmsSelectedId, setHmsSelectedId] = useState<number | null>(null)
const [hmsDetailTab, setHmsDetailTab] = useState(0)
const [hmsPage, setHmsPage] = useState(1)
const [hmsResults, setHmsResults] = useState<HNSSearchSubstance[]>([])
const [hmsTotal, setHmsTotal] = useState(0)
const [hmsLoading, setHmsLoading] = useState(false)
const [hmsSelectedSubstance, setHmsSelectedSubstance] = useState<HNSSearchSubstance | null>(null)
const contentRef = useRef<HTMLDivElement>(null)
// 검색 타입 매핑 (프론트엔드 → API)
const searchTypeMap: Record<string, string> = {
abbr: 'abbreviation', korName: 'nameKr', engName: 'nameEn', cas: 'casNumber', un: 'unNumber',
}
// HNS 물질 검색 API 호출
const fetchHnsSubstances = useCallback(async () => {
setHmsLoading(true)
try {
const params: Record<string, string | number> = { page: hmsPage, limit: 10 }
if (hmsSearchInput.trim()) {
params.q = hmsSearchInput.trim()
if (hmsSearchType !== 'all') {
params.type = searchTypeMap[hmsSearchType] || 'abbreviation'
}
}
if (hmsFilterSebc !== '전체 거동분류') {
params.sebc = hmsFilterSebc.split(' ')[0]
}
const { data } = await api.get('/hns', { params })
setHmsResults(data.items)
setHmsTotal(data.total)
} catch (err) {
console.error('[HNS] 물질 검색 오류:', err)
setHmsResults([])
setHmsTotal(0)
} finally {
setHmsLoading(false)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hmsSearchInput, hmsSearchType, hmsFilterSebc, hmsPage])
// 검색 조건 변경 시 API 호출 (디바운스)
useEffect(() => {
const timer = setTimeout(fetchHnsSubstances, 300)
return () => clearTimeout(timer)
}, [fetchHnsSubstances])
// 물질 선택 시 상세 정보 조회
useEffect(() => {
if (hmsSelectedId === null) {
setHmsSelectedSubstance(null)
return
}
api.get(`/hns/${hmsSelectedId}`).then(({ data }) => {
setHmsSelectedSubstance(data)
}).catch(() => {
setHmsSelectedSubstance(null)
})
}, [hmsSelectedId])
const handleExportPDF = () => {
if (!contentRef.current) return
const clone = contentRef.current.cloneNode(true) as HTMLElement
clone.querySelectorAll('[data-html2pdf-ignore]').forEach(el => el.remove())
const content = sanitizeHtml(clone.innerHTML)
const styles = Array.from(document.querySelectorAll('style, link[rel="stylesheet"]'))
.map(el => el.outerHTML).join('\n')
const fullHtml = `<!DOCTYPE html>
<html><head><meta charset="utf-8"/>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'none';"/>
<title>HNS 물질정보</title>
${styles}
<style>
:root { --t1: #ffffff !important; --t2: #d0d6e6 !important; --t3: #a8b0c8 !important; }
@media print { @page { size: A4; margin: 10mm; } body { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; } }
body { background: var(--bg-base); color: var(--fg-default); font-family: 'Noto Sans KR', sans-serif; padding: 20px 24px; }
</style>
</head><body>${content}</body></html>`
const blob = new Blob([fullHtml], { type: 'text/html; charset=utf-8' })
const url = URL.createObjectURL(blob)
const win = window.open(url, '_blank')
if (win) {
win.addEventListener('afterprint', () => URL.revokeObjectURL(url))
setTimeout(() => { win.document.title = 'HNS_물질정보'; win.print() }, 500)
}
setTimeout(() => URL.revokeObjectURL(url), 30000)
}
const filtered = substances.filter(s => {
const matchCategory = selectedCategory === 'all' || s.category === selectedCategory
const q = searchQuery.toLowerCase()
const matchSearch = !q || s.name.toLowerCase().includes(q) || s.nameEn.toLowerCase().includes(q) || s.unNumber.toLowerCase().includes(q) || s.casNumber.includes(q) || s.formula.toLowerCase().includes(q)
return matchCategory && matchSearch
})
/* Detail search filter for Panel 3 (legacy) */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const detailFiltered = substances.filter(s => {
const qName = detailSearchName.toLowerCase()
const qCas = detailSearchCas.toLowerCase()
const matchName = !qName || s.name.toLowerCase().includes(qName) || s.nameEn.toLowerCase().includes(qName)
const matchCas = !qCas || s.casNumber.includes(qCas)
const matchSebc = detailSearchSebc === '전체 거동분류' || s.sebc.includes(detailSearchSebc.split(' ')[0])
return matchName && matchCas && matchSebc
})
/* Panel 3: HNS API 기반 검색 결과 */
const HMS_PER_PAGE = 10
const hmsTotalPages = Math.max(1, Math.ceil(hmsTotal / HMS_PER_PAGE))
const hmsPageData = hmsResults
const tabLabels = [
{ icon: '📊', label: 'SEBC 거동분류' },
{ icon: '🧪', label: '주요 물질 특성' },
{ icon: '⚡', label: '위험도 기준' },
{ icon: '🔍', label: '물질 상세검색' },
]
return (
<div className="flex flex-col h-full w-full flex-1 overflow-hidden bg-bg-base">
<div ref={contentRef} className="flex-1 overflow-y-auto p-5" style={{ scrollbarWidth: 'thin', scrollbarColor: 'var(--stroke-light) transparent' }}>
{/* 헤더 */}
<div className="flex items-center justify-between mb-5">
<div className="flex items-center gap-3">
<div className="flex items-center justify-center text-[20px] shrink-0" style={{ width: 42, height: 42, borderRadius: 10, background: 'linear-gradient(135deg,rgba(249,115,22,.2),rgba(239,68,68,.15))', border: '1px solid rgba(249,115,22,.3)' }}>🧬</div>
<div>
<div className="text-base font-bold">HNS </div>
<div className="text-[10px] text-fg-disabled mt-0.5">SEBC · CHRIS/CAMEO DB · AEGL/ERPG/IDLH · 6,500+ </div>
</div>
</div>
<div className="flex gap-1.5" data-html2pdf-ignore>
<input
type="text"
placeholder="물질명 또는 CAS 번호 검색..."
value={searchQuery}
onChange={e => setSearchQuery(e.target.value)}
className="rounded-sm border border-stroke text-[10px] outline-none bg-bg-card px-3 py-1.5 w-[200px]"
/>
<button
onClick={handleExportPDF}
className="rounded-sm text-[10px] font-semibold cursor-pointer text-color-warning px-3.5 py-1.5"
style={{ border: '1px solid rgba(249,115,22,.3)', background: 'rgba(249,115,22,.08)' }}
>📥 DB </button>
</div>
</div>
{/* 서브탭 */}
<div className="flex rounded-md border border-stroke mb-5 bg-bg-card p-1 gap-[3px]" data-html2pdf-ignore>
{tabLabels.map((tab, idx) => (
<button
key={idx}
onClick={() => setActiveTab(idx)}
className={activeTab === idx ? 'wx-tbtn on' : 'wx-tbtn'}
style={{ flex: 1, padding: 8, fontSize: 11 }}
>{tab.icon} {tab.label}</button>
))}
</div>
{/* ═══ MAT PANEL 0: SEBC 거동분류 ═══ */}
{activeTab === 0 && (
<div>
<div className="rounded-[12px] p-4 mb-4 relative overflow-hidden" style={{ background: 'linear-gradient(135deg,rgba(249,115,22,.05),rgba(6,182,212,.03))', border: '1px solid rgba(249,115,22,.2)' }}>
<div className="absolute top-0 left-0 right-0" style={{ height: 3, background: 'linear-gradient(90deg,var(--color-warning),var(--color-accent),var(--color-success),var(--color-tertiary))' }} />
<div className="flex items-center gap-2 mb-3">
<span className="text-[13px] font-bold text-color-warning">SEBC </span>
<span className="text-[8px] font-semibold text-color-warning py-[2px] px-2 rounded-md" style={{ background: 'rgba(249,115,22,.1)' }}>Standard European Behaviour Classification</span>
</div>
<div className="text-[10px] text-fg-sub leading-[1.7] mb-[14px]">
HNS <b className="text-color-warning">· </b> . , , 5 , .
</div>
<div className="grid mb-[14px]" style={{ gridTemplateColumns: 'repeat(5,1fr)', gap: 8 }}>
{/* G: Gas */}
<div className="p-3 rounded-md text-center" style={{ background: 'rgba(168,85,247,.06)', border: '1px solid rgba(168,85,247,.2)' }}>
<div className="flex items-center justify-center text-[18px] mx-auto mb-2" style={{ width: 36, height: 36, borderRadius: '50%', background: 'rgba(168,85,247,.15)' }}>💨</div>
<div className="text-[13px] font-mono font-extrabold text-color-tertiary">G</div>
<div className="text-[11px] font-bold my-1">Gas</div>
<div className="text-[8px] text-fg-sub leading-normal"> . </div>
<div className="mt-1.5 text-[7px] font-semibold text-color-tertiary p-[3px]" style={{ background: 'rgba(168,85,247,.08)', borderRadius: 3 }}> </div>
</div>
{/* E: Evaporator */}
<div className="p-3 rounded-md text-center" style={{ background: 'rgba(239,68,68,.06)', border: '1px solid rgba(239,68,68,.2)' }}>
<div className="flex items-center justify-center text-[18px] mx-auto mb-2" style={{ width: 36, height: 36, borderRadius: '50%', background: 'rgba(239,68,68,.15)' }}>🔥</div>
<div className="text-[13px] font-mono font-extrabold text-color-danger">E</div>
<div className="text-[11px] font-bold my-1">Evaporator</div>
<div className="text-[8px] text-fg-sub leading-normal"> . </div>
<div className="mt-1.5 text-[7px] font-semibold text-color-danger p-[3px]" style={{ background: 'rgba(239,68,68,.08)', borderRadius: 3 }}>+ </div>
</div>
{/* F: Floater */}
<div className="p-3 rounded-md text-center" style={{ background: 'rgba(234,179,8,.06)', border: '1px solid rgba(234,179,8,.2)' }}>
<div className="flex items-center justify-center text-[18px] mx-auto mb-2" style={{ width: 36, height: 36, borderRadius: '50%', background: 'rgba(234,179,8,.15)' }}>🟡</div>
<div className="text-[13px] font-mono font-extrabold text-color-caution">F</div>
<div className="text-[11px] font-bold my-1">Floater</div>
<div className="text-[8px] text-fg-sub leading-normal">{'해수면 위에 부유. 비중 < 1.0, 불용성 물질'}</div>
<div className="mt-1.5 text-[7px] font-semibold text-color-caution p-[3px]" style={{ background: 'rgba(234,179,8,.08)', borderRadius: 3 }}> </div>
</div>
{/* D: Dissolver */}
<div className="p-3 rounded-md text-center" style={{ background: 'rgba(6,182,212,.06)', border: '1px solid rgba(6,182,212,.2)' }}>
<div className="flex items-center justify-center text-[18px] mx-auto mb-2" style={{ width: 36, height: 36, borderRadius: '50%', background: 'rgba(6,182,212,.15)' }}>💧</div>
<div className="text-[13px] font-mono font-extrabold text-color-accent">D</div>
<div className="text-[11px] font-bold my-1">Dissolver</div>
<div className="text-[8px] text-fg-sub leading-normal"> . </div>
<div className="mt-1.5 text-[7px] font-semibold text-color-accent p-[3px]" style={{ background: 'rgba(6,182,212,.08)', borderRadius: 3 }}> </div>
</div>
{/* S: Sinker */}
<div className="p-3 rounded-md text-center" style={{ background: 'rgba(34,197,94,.06)', border: '1px solid rgba(34,197,94,.2)' }}>
<div className="flex items-center justify-center text-[18px] mx-auto mb-2" style={{ width: 36, height: 36, borderRadius: '50%', background: 'rgba(34,197,94,.15)' }}></div>
<div className="text-[13px] font-mono font-extrabold text-color-success">S</div>
<div className="text-[11px] font-bold my-1">Sinker</div>
<div className="text-[8px] text-fg-sub leading-normal">{'해저로 침강. 비중 > 1.0, 저층 오염 축적'}</div>
<div className="mt-1.5 text-[7px] font-semibold text-color-success p-[3px]" style={{ background: 'rgba(34,197,94,.08)', borderRadius: 3 }}> 3D </div>
</div>
</div>
{/* 복합 거동 */}
<div className="rounded-md p-3 border border-stroke bg-bg-card">
<div className="text-[10px] font-bold mb-2">🔀 </div>
<div className="grid text-center text-[8px]" style={{ gridTemplateColumns: 'repeat(5,1fr)', gap: 6 }}>
<div className="rounded p-1.5 bg-bg-base"><span className="font-bold text-color-tertiary">GD</span><br/><span className="text-fg-disabled">+</span></div>
<div className="rounded p-1.5 bg-bg-base"><span className="font-bold text-color-danger">ED</span><br/><span className="text-fg-disabled">+</span></div>
<div className="rounded p-1.5 bg-bg-base"><span className="font-bold text-color-caution">FE</span><br/><span className="text-fg-disabled">+</span></div>
<div className="rounded p-1.5 bg-bg-base"><span className="font-bold text-color-accent">FED</span><br/><span className="text-fg-disabled">++</span></div>
<div className="rounded p-1.5 bg-bg-base"><span className="font-bold text-color-success">SD</span><br/><span className="text-fg-disabled">+</span></div>
</div>
</div>
</div>
</div>
)}
{/* ═══ MAT PANEL 1: 주요 물질 특성 ═══ */}
{activeTab === 1 && (
<div>
{/* 카테고리 필터 */}
<div className="flex gap-1.5 mb-[14px]" data-html2pdf-ignore>
{categories.map(cat => (
<button key={cat.id} onClick={() => setSelectedCategory(cat.id)}
className="rounded-sm text-[10px] font-semibold cursor-pointer"
style={{
padding: '6px 12px',
border: selectedCategory === cat.id ? '1px solid var(--color-warning)' : '1px solid var(--stroke-default)',
color: selectedCategory === cat.id ? 'var(--color-warning)' : 'var(--fg-disabled)',
background: selectedCategory === cat.id ? 'rgba(249,115,22,.08)' : 'var(--bg-card)',
}}
>{cat.icon} {cat.label}</button>
))}
</div>
{/* 주요 HNS 물질 카드 */}
<div className="grid grid-cols-2 gap-[14px] mb-4">
{/* 암모니아 */}
{filtered.find(s => s.casNumber === '7664-41-7') && (
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card" style={{ borderLeft: '4px solid var(--color-tertiary)' }}>
<div className="flex items-center justify-between mb-[10px]">
<div><span className="text-sm font-mono font-extrabold text-color-tertiary">NH</span> <span className="text-xs font-bold"></span></div>
<div className="flex gap-1">
<span className="text-[8px] font-semibold text-color-tertiary px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(168,85,247,.1)' }}>G/GD</span>
<span className="text-[8px] font-semibold text-color-danger px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(239,68,68,.1)' }}></span>
</div>
</div>
<div className="grid grid-cols-2 gap-1 text-[8px] mb-2">
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">CAS:</span> <span className="font-mono">7664-41-7</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">17.03</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-accent">-33.4°C</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">0.73</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">N/A ()</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-accent"> </span></div>
</div>
<div className="grid text-center text-[7px]" style={{ gridTemplateColumns: '1fr 1fr 1fr', gap: 4 }}>
<div style={{ padding: 4, background: 'rgba(34,197,94,.06)', border: '1px solid rgba(34,197,94,.12)', borderRadius: 3 }}><span className="text-color-success">AEGL-2</span><br/><b>160 ppm</b></div>
<div style={{ padding: 4, background: 'rgba(249,115,22,.06)', border: '1px solid rgba(249,115,22,.12)', borderRadius: 3 }}><span className="text-color-warning">ERPG-2</span><br/><b>150 ppm</b></div>
<div style={{ padding: 4, background: 'rgba(239,68,68,.06)', border: '1px solid rgba(239,68,68,.12)', borderRadius: 3 }}><span className="text-color-danger">IDLH</span><br/><b>300 ppm</b></div>
</div>
<div className="mt-1.5 text-[8px] text-fg-disabled leading-normal"> . . .</div>
</div>
)}
{/* 메탄올 */}
{filtered.find(s => s.casNumber === '67-56-1') && (
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card" style={{ borderLeft: '4px solid var(--color-accent)' }}>
<div className="flex items-center justify-between mb-[10px]">
<div><span className="text-sm font-mono font-extrabold text-color-accent">CHOH</span> <span className="text-xs font-bold"></span></div>
<div className="flex gap-1">
<span className="text-[8px] font-semibold text-color-accent px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(6,182,212,.1)' }}>ED</span>
<span className="text-[8px] font-semibold text-color-warning px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(249,115,22,.1)' }}></span>
</div>
</div>
<div className="grid grid-cols-2 gap-1 text-[8px] mb-2">
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">CAS:</span> <span className="font-mono">67-56-1</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">32.04</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-accent">64.7°C</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">0.79</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-warning">11°C</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-accent"> </span></div>
</div>
<div className="grid text-center text-[7px]" style={{ gridTemplateColumns: '1fr 1fr 1fr', gap: 4 }}>
<div style={{ padding: 4, background: 'rgba(34,197,94,.06)', border: '1px solid rgba(34,197,94,.12)', borderRadius: 3 }}><span className="text-color-success">AEGL-2</span><br/><b>2,100 ppm</b></div>
<div style={{ padding: 4, background: 'rgba(249,115,22,.06)', border: '1px solid rgba(249,115,22,.12)', borderRadius: 3 }}><span className="text-color-warning">ERPG-2</span><br/><b>1,000 ppm</b></div>
<div style={{ padding: 4, background: 'rgba(239,68,68,.06)', border: '1px solid rgba(239,68,68,.12)', borderRadius: 3 }}><span className="text-color-danger">IDLH</span><br/><b>6,000 ppm</b></div>
</div>
<div className="mt-1.5 text-[8px] text-fg-disabled leading-normal"> . . . 2007 FODDANGER호 95L .</div>
</div>
)}
{/* 수소 */}
{filtered.find(s => s.casNumber === '1333-74-0') && (
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card" style={{ borderLeft: '4px solid var(--color-danger)' }}>
<div className="flex items-center justify-between mb-[10px]">
<div><span className="text-sm font-mono font-extrabold text-color-danger">H</span> <span className="text-xs font-bold"></span></div>
<div className="flex gap-1">
<span className="text-[8px] font-semibold text-color-tertiary px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(168,85,247,.1)' }}>G</span>
<span className="text-[8px] font-semibold text-color-danger px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(239,68,68,.1)' }}></span>
</div>
</div>
<div className="grid grid-cols-2 gap-1 text-[8px] mb-2">
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">CAS:</span> <span className="font-mono">1333-74-0</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">2.016</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-accent">-252.9°C</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">():</span> <span className="font-mono">0.07</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">LFL:</span> <span className="font-mono text-color-danger">4.0%</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">UFL:</span> <span className="font-mono text-color-danger">75.0%</span></div>
</div>
<div className="mt-1.5 text-[8px] text-fg-disabled leading-normal"> (4~75%). · . BLEVE . .</div>
</div>
)}
{/* LNG */}
{filtered.find(s => s.nameEn === 'LPG (Propane/Butane)' || s.casNumber === '74-82-8') && (
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card" style={{ borderLeft: '4px solid var(--color-warning)' }}>
<div className="flex items-center justify-between mb-[10px]">
<div><span className="text-sm font-mono font-extrabold text-color-warning">CH</span> <span className="text-xs font-bold">LNG ()</span></div>
<div className="flex gap-1">
<span className="text-[8px] font-semibold text-color-tertiary px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(168,85,247,.1)' }}>G</span>
<span className="text-[8px] font-semibold text-color-warning px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(249,115,22,.1)' }}>/</span>
</div>
</div>
<div className="grid grid-cols-2 gap-1 text-[8px] mb-2">
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">CAS:</span> <span className="font-mono">74-82-8</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">16.04</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-accent">-161.5°C</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">():</span> <span className="font-mono">0.42</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">LFL:</span> <span className="font-mono text-color-warning">5.0%</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">UFL:</span> <span className="font-mono text-color-warning">15.0%</span></div>
</div>
<div className="mt-1.5 text-[8px] text-fg-disabled leading-normal">(-162°C) RPT(), Pool Fire . Flash . · LNG .</div>
</div>
)}
{/* 페놀 */}
{filtered.find(s => s.casNumber === '108-95-2' || s.name === '페놀') && (
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card" style={{ borderLeft: '4px solid var(--color-success)' }}>
<div className="flex items-center justify-between mb-[10px]">
<div><span className="text-sm font-mono font-extrabold text-color-success">CHOH</span> <span className="text-xs font-bold"></span></div>
<div className="flex gap-1">
<span className="text-[8px] font-semibold text-color-success px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(34,197,94,.1)' }}>S/SD</span>
<span className="text-[8px] font-semibold text-color-danger px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(239,68,68,.1)' }}></span>
</div>
</div>
<div className="grid grid-cols-2 gap-1 text-[8px] mb-2">
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">CAS:</span> <span className="font-mono">108-95-2</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">94.11</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">181.7°C</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-success">1.07 ()</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-warning">79°C</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-accent">84 g/L</span></div>
</div>
<div className="mt-1.5 text-[8px] text-fg-disabled leading-normal"> 1.07 <b className="text-color-success">Sinker </b> . ROMS 3.5. HNS (31.8kg/).</div>
</div>
)}
{/* 톨루엔 */}
{filtered.find(s => s.casNumber === '108-88-3') && (
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card" style={{ borderLeft: '4px solid var(--color-caution)' }}>
<div className="flex items-center justify-between mb-[10px]">
<div><span className="text-sm font-mono font-extrabold text-color-caution">CH</span> <span className="text-xs font-bold"></span></div>
<div className="flex gap-1">
<span className="text-[8px] font-semibold text-color-caution px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(234,179,8,.1)' }}>FE</span>
<span className="text-[8px] font-semibold text-color-warning px-1.5 py-0.5" style={{ borderRadius: 3, background: 'rgba(249,115,22,.1)' }}></span>
</div>
</div>
<div className="grid grid-cols-2 gap-1 text-[8px] mb-2">
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">CAS:</span> <span className="font-mono">108-88-3</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">92.14</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">110.6°C</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-caution">0.87 ()</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono text-color-warning">4°C</span></div>
<div className="rounded bg-bg-base px-1.5 py-1"><span className="text-fg-disabled">:</span> <span className="font-mono">0.52 g/L</span></div>
</div>
<div className="mt-1.5 text-[8px] text-fg-disabled leading-normal"> . (4°C) . HNS. .</div>
</div>
)}
</div>
</div>
)}
{/* ═══ MAT PANEL 2: 위험도 기준 ═══ */}
{activeTab === 2 && (
<div>
<div className="grid grid-cols-2 gap-4 mb-4">
{/* AEGL 기준 */}
<div className="rounded-[10px] p-4 border border-stroke bg-bg-card" style={{ borderTop: '3px solid var(--color-success)' }}>
<div className="text-[13px] font-bold text-color-success mb-[10px]">🟢 AEGL (Acute Exposure Guideline Level)</div>
<div className="text-[9px] text-fg-disabled mb-[10px]"> EPA (10, 30, 60, 4, 8)</div>
<div className="flex flex-col gap-1.5">
<div className="rounded-sm px-2.5 py-2" style={{ background: 'rgba(34,197,94,.04)', border: '1px solid rgba(34,197,94,.12)', borderLeft: '4px solid var(--color-success)' }}>
<div className="text-[11px] font-bold text-color-success">AEGL-1 ()</div>
<div className="text-[9px] text-fg-sub leading-normal"> , . .</div>
</div>
<div className="rounded-sm px-2.5 py-2" style={{ background: 'rgba(249,115,22,.04)', border: '1px solid rgba(249,115,22,.12)', borderLeft: '4px solid var(--color-warning)' }}>
<div className="text-[11px] font-bold text-color-warning">AEGL-2 ( )</div>
<div className="text-[9px] text-fg-sub leading-normal">· . <b className="text-color-warning"> </b>.</div>
</div>
<div className="rounded-sm px-2.5 py-2" style={{ background: 'rgba(239,68,68,.04)', border: '1px solid rgba(239,68,68,.12)', borderLeft: '4px solid var(--color-danger)' }}>
<div className="text-[11px] font-bold text-color-danger">AEGL-3 ( )</div>
<div className="text-[9px] text-fg-sub leading-normal"> . <b className="text-color-danger"> · </b>.</div>
</div>
</div>
</div>
{/* ERPG / IDLH */}
<div className="rounded-[10px] p-4 border border-stroke bg-bg-card" style={{ borderTop: '3px solid var(--color-danger)' }}>
<div className="text-[13px] font-bold text-color-danger mb-[10px]">🔴 ERPG &amp; IDLH</div>
<div className="flex flex-col gap-2">
<div>
<div className="text-[10px] font-bold text-color-warning mb-1">ERPG (Emergency Response Planning Guideline)</div>
<div className="text-[9px] text-fg-disabled mb-1.5">AIHA 1 </div>
<div className="flex flex-col gap-1">
<div className="text-[8px] rounded px-2 py-1.5" style={{ background: 'rgba(34,197,94,.03)', border: '1px solid rgba(34,197,94,.1)', borderLeft: '3px solid var(--color-success)' }}><b className="text-color-success">ERPG-1</b> , </div>
<div className="text-[8px] rounded px-2 py-1.5" style={{ background: 'rgba(249,115,22,.03)', border: '1px solid rgba(249,115,22,.1)', borderLeft: '3px solid var(--color-warning)' }}><b className="text-color-warning">ERPG-2</b> , </div>
<div className="text-[8px] rounded px-2 py-1.5" style={{ background: 'rgba(239,68,68,.03)', border: '1px solid rgba(239,68,68,.1)', borderLeft: '3px solid var(--color-danger)' }}><b className="text-color-danger">ERPG-3</b> </div>
</div>
</div>
<div className="mt-1 rounded-sm p-2.5" style={{ background: 'rgba(239,68,68,.04)', border: '1px solid rgba(239,68,68,.15)' }}>
<div className="text-[10px] font-bold text-color-danger mb-1">IDLH (Immediately Dangerous to Life or Health)</div>
<div className="text-[9px] text-fg-disabled mb-1">NIOSH 30 · </div>
<div className="text-[9px] text-fg-sub leading-normal"><b className="text-color-danger"> </b> (SCBA) . WING .</div>
</div>
</div>
</div>
</div>
{/* 물질별 위험도 비교표 */}
<div className="rounded-[10px] p-4 border border-stroke bg-bg-card">
<div className="text-xs font-bold mb-3">📊 HNS (ppm)</div>
<table className="w-full border-collapse text-[9px]">
<thead>
<tr style={{ background: 'rgba(168,85,247,.06)' }}>
<th className="text-color-tertiary text-left" style={{ padding: '7px 8px', borderBottom: '2px solid var(--stroke-light)' }}></th>
<th className="text-color-success text-center" style={{ padding: '7px 8px', borderBottom: '2px solid var(--stroke-light)' }}>AEGL-1</th>
<th className="text-color-warning text-center" style={{ padding: '7px 8px', borderBottom: '2px solid var(--stroke-light)' }}>AEGL-2</th>
<th className="text-color-danger text-center" style={{ padding: '7px 8px', borderBottom: '2px solid var(--stroke-light)' }}>AEGL-3</th>
<th className="text-color-warning text-center" style={{ padding: '7px 8px', borderBottom: '2px solid var(--stroke-light)' }}>ERPG-2</th>
<th className="text-color-danger text-center" style={{ padding: '7px 8px', borderBottom: '2px solid var(--stroke-light)' }}>IDLH</th>
<th className="text-color-warning text-center" style={{ padding: '7px 8px', borderBottom: '2px solid var(--stroke-light)' }}>LFL(%)</th>
<th className="text-fg-sub text-center" style={{ padding: '7px 8px', borderBottom: '2px solid var(--stroke-light)' }}>SEBC</th>
</tr>
</thead>
<tbody>
<tr style={{ borderBottom: '1px solid rgba(255,255,255,.04)' }}>
<td className="font-semibold text-color-tertiary py-1.5 px-2">NH </td>
<td className="text-center font-mono text-color-success">30</td>
<td className="text-center font-mono text-color-warning">160</td>
<td className="text-center font-mono text-color-danger">1,100</td>
<td className="text-center font-mono">150</td>
<td className="text-center font-mono text-color-danger">300</td>
<td className="text-center font-mono">15.0</td>
<td className="text-center font-semibold text-color-tertiary">G/GD</td>
</tr>
<tr style={{ borderBottom: '1px solid rgba(255,255,255,.04)' }}>
<td className="font-semibold text-color-accent py-1.5 px-2">CHOH </td>
<td className="text-center font-mono text-color-success">530</td>
<td className="text-center font-mono text-color-warning">2,100</td>
<td className="text-center font-mono text-color-danger">14,000</td>
<td className="text-center font-mono">1,000</td>
<td className="text-center font-mono text-color-danger">6,000</td>
<td className="text-center font-mono">6.0</td>
<td className="text-center font-semibold text-color-accent">ED</td>
</tr>
<tr style={{ borderBottom: '1px solid rgba(255,255,255,.04)' }}>
<td className="font-semibold text-color-danger py-1.5 px-2">H </td>
<td className="text-center text-fg-disabled">-</td>
<td className="text-center text-fg-disabled">-</td>
<td className="text-center text-fg-disabled">-</td>
<td className="text-center text-fg-disabled">-</td>
<td className="text-center text-fg-disabled">-</td>
<td className="text-center font-mono font-bold text-color-danger">4.0</td>
<td className="text-center font-semibold text-color-tertiary">G</td>
</tr>
<tr style={{ borderBottom: '1px solid rgba(255,255,255,.04)' }}>
<td className="font-semibold text-color-warning py-1.5 px-2">CH LNG</td>
<td className="text-center text-fg-disabled">-</td>
<td className="text-center text-fg-disabled">-</td>
<td className="text-center text-fg-disabled">-</td>
<td className="text-center text-fg-disabled">-</td>
<td className="text-center text-fg-disabled">-</td>
<td className="text-center font-mono text-color-warning">5.0</td>
<td className="text-center font-semibold text-color-tertiary">G</td>
</tr>
<tr style={{ borderBottom: '1px solid rgba(255,255,255,.04)' }}>
<td className="font-semibold text-color-success py-1.5 px-2">CHOH </td>
<td className="text-center font-mono text-color-success">19</td>
<td className="text-center font-mono text-color-warning">29</td>
<td className="text-center font-mono text-color-danger">57</td>
<td className="text-center font-mono">50</td>
<td className="text-center font-mono text-color-danger">250</td>
<td className="text-center font-mono">1.8</td>
<td className="text-center font-semibold text-color-success">S/SD</td>
</tr>
<tr>
<td className="font-semibold text-color-caution py-1.5 px-2">CH </td>
<td className="text-center font-mono text-color-success">67</td>
<td className="text-center font-mono text-color-warning">560</td>
<td className="text-center font-mono text-color-danger">3,700</td>
<td className="text-center font-mono">300</td>
<td className="text-center font-mono text-color-danger">500</td>
<td className="text-center font-mono">1.1</td>
<td className="text-center font-semibold text-color-caution">FE</td>
</tr>
</tbody>
</table>
<div className="mt-2 text-[7px] text-fg-disabled"> AEGL: 60분 / ERPG: 1시간 / IDLH: 30분 / LFL: 폭발하한</div>
</div>
</div>
)}
{/* ═══ MAT PANEL 3: 물질 상세검색 ═══ */}
{activeTab === 3 && (
<div>
{/* ── 검색창 ── */}
<div className="rounded-[10px] p-4 mb-4 border border-stroke bg-bg-card">
<div className="flex items-center justify-between mb-[14px]">
<div className="text-[13px] font-bold">🔍 HNS <span className="text-[9px] font-normal text-fg-disabled">(···CAS·UN번호 )</span></div>
<div className="flex gap-1">
<button className="text-[9px] font-semibold cursor-pointer text-color-warning rounded px-2.5 py-[5px]" style={{ border: '1px solid rgba(249,115,22,.25)', background: 'rgba(249,115,22,.08)' }}>📥 DB </button>
<button className="text-[9px] font-semibold cursor-pointer text-color-accent rounded px-2.5 py-[5px]" style={{ border: '1px solid rgba(6,182,212,.25)', background: 'rgba(6,182,212,.08)' }}>🔗 Port-MIS </button>
</div>
</div>
<div className="flex gap-2 mb-[10px] items-center">
<div className="shrink-0 flex items-center gap-1">
<span className="text-[9px] font-semibold text-fg-disabled">:</span>
<select value={hmsSearchType} onChange={e => { setHmsSearchType(e.target.value as typeof hmsSearchType); setHmsPage(1) }} className="rounded-sm border border-stroke text-[12px] outline-none bg-bg-base px-3 py-2" style={{ minWidth: 140 }}>
<option value="all"> </option>
<option value="abbr">/</option>
<option value="korName"></option>
<option value="engName"></option>
<option value="cas">CAS번호</option>
<option value="un">UN번호</option>
</select>
</div>
<input type="text" value={hmsSearchInput} onChange={e => { setHmsSearchInput(e.target.value); setHmsPage(1) }} placeholder={hmsSearchType === 'all' ? '물질명, 약자, CAS, UN번호 통합 검색' : hmsSearchType === 'abbr' ? '약자/제품명 입력' : hmsSearchType === 'cas' ? 'CAS번호 입력 (예: 71-43-2)' : hmsSearchType === 'un' ? 'UN번호 입력 (예: 1114)' : '검색어 입력'} className="flex-1 rounded-sm border border-stroke text-[13px] outline-none bg-bg-base px-3 py-2" />
<button onClick={() => setHmsPage(1)} className="text-[13px] font-bold cursor-pointer shrink-0 rounded-sm text-white px-5 py-2" style={{ border: 'none', background: 'linear-gradient(135deg,var(--color-warning),var(--color-danger))' }}>🔎 </button>
</div>
<div className="text-[8px] text-fg-disabled leading-[1.6]">
· <b className="text-color-warning"> </b> &nbsp;|&nbsp; / <b className="text-color-warning">, </b> &nbsp;|&nbsp; <b className="text-color-accent">1,222</b>
</div>
</div>
{/* ── 검색 결과 테이블 ── */}
<div className="rounded-[10px] p-4 mb-4 border border-stroke bg-bg-card">
<div className="flex items-center justify-between mb-[10px]">
<div className="text-[13px] font-bold">📋 <span className="text-[11px] font-normal text-fg-disabled"> {hmsTotal} </span></div>
<select value={hmsFilterSebc} onChange={e => { setHmsFilterSebc(e.target.value); setHmsPage(1) }} className="rounded border border-stroke text-[11px] text-fg-sub outline-none bg-bg-base px-2.5 py-1">
<option> </option><option>G (Gas)</option><option>E (Evaporator)</option><option>F (Floater)</option><option>D (Dissolver)</option><option>S (Sinker)</option>
</select>
</div>
<div className="overflow-x-auto">
<table className="w-full border-collapse text-[11px]">
<thead>
<tr style={{ background: 'rgba(249,115,22,.06)' }}>
<th className="text-color-warning text-left" style={{ padding: '8px 8px', borderBottom: '2px solid var(--stroke-light)', width: 36 }}>No.</th>
<th className="text-color-warning text-left" style={{ padding: '8px 8px', borderBottom: '2px solid var(--stroke-light)' }}>/</th>
<th className="text-fg-sub text-left" style={{ padding: '8px 8px', borderBottom: '2px solid var(--stroke-light)' }}></th>
<th className="text-fg-sub text-left" style={{ padding: '8px 8px', borderBottom: '2px solid var(--stroke-light)' }}> </th>
<th className="text-color-accent text-left" style={{ padding: '8px 8px', borderBottom: '2px solid var(--stroke-light)' }}></th>
<th className="text-fg-sub text-left text-[10px]" style={{ padding: '8px 8px', borderBottom: '2px solid var(--stroke-light)' }}> / </th>
<th className="text-fg-sub text-center" style={{ padding: '8px 8px', borderBottom: '2px solid var(--stroke-light)', width: 68 }}>UN번호</th>
<th className="text-fg-sub text-center" style={{ padding: '8px 8px', borderBottom: '2px solid var(--stroke-light)', width: 80 }}>CAS번호</th>
</tr>
</thead>
<tbody>
{hmsLoading ? (
<tr><td colSpan={8} className="text-center text-fg-disabled py-5 px-2"> ...</td></tr>
) : hmsPageData.length > 0 ? hmsPageData.map((s: HNSSearchSubstance, idx: number) => {
const isSel = hmsSelectedId === s.id
return (
<tr key={s.id} onClick={() => { setHmsSelectedId(isSel ? null : s.id); setHmsDetailTab(0) }}
style={{ borderBottom: '1px solid rgba(255,255,255,.04)', cursor: 'pointer', background: isSel ? 'rgba(6,182,212,.04)' : undefined }}
onMouseOver={e => { if (!isSel) e.currentTarget.style.background = 'rgba(249,115,22,.03)' }}
onMouseOut={e => { if (!isSel) e.currentTarget.style.background = '' }}
>
<td className="font-mono text-fg-disabled px-2 py-2">{(hmsPage - 1) * HMS_PER_PAGE + idx + 1}</td>
<td className="font-semibold font-mono text-color-warning px-2 py-2">{s.abbreviation}</td>
<td className="px-2 py-2">{s.nameEn}</td>
<td className="text-fg-disabled text-[10px] px-2 py-2">{s.synonymsEn}</td>
<td className="font-semibold px-2 py-2"><span className="text-color-accent underline cursor-pointer" onClick={e => { e.stopPropagation(); setHmsSelectedId(s.id); setHmsDetailTab(0) }}>{s.nameKr}</span></td>
<td className="text-fg-disabled text-[10px] px-2 py-2">{s.synonymsKr}</td>
<td className="text-center font-mono">{s.unNumber}</td>
<td className="text-center font-mono">{s.casNumber}</td>
</tr>
)
}) : (
<tr><td colSpan={8} className="text-center text-fg-disabled py-5 px-2"> .</td></tr>
)}
</tbody>
</table>
</div>
<div className="mt-[10px] text-center text-[11px] text-fg-disabled">
<div className="flex items-center justify-center gap-1 mb-1.5">
<button onClick={() => setHmsPage(1)} disabled={hmsPage <= 1} className="rounded cursor-pointer border border-stroke text-[11px] text-fg-sub bg-bg-base px-2.5 py-1" style={{ opacity: hmsPage <= 1 ? 0.4 : 1 }}></button>
<button onClick={() => setHmsPage(p => Math.max(1, p - 1))} disabled={hmsPage <= 1} className="rounded cursor-pointer border border-stroke text-[11px] text-fg-sub bg-bg-base px-3 py-1" style={{ opacity: hmsPage <= 1 ? 0.4 : 1 }}></button>
{(() => {
const range = 2;
let start = Math.max(1, hmsPage - range);
let end = Math.min(hmsTotalPages, hmsPage + range);
if (end - start < range * 2) {
start = Math.max(1, end - range * 2);
end = Math.min(hmsTotalPages, start + range * 2);
}
const pages = [];
for (let p = start; p <= end; p++) pages.push(p);
return pages.map(p => (
<button key={p} onClick={() => setHmsPage(p)} className="rounded cursor-pointer border border-stroke text-[11px] px-3 py-1" style={{ background: p === hmsPage ? 'rgba(249,115,22,.15)' : 'var(--bg-base)', color: p === hmsPage ? 'var(--color-warning)' : 'var(--fg-sub)', fontWeight: p === hmsPage ? 700 : 400 }}>{p}</button>
));
})()}
<button onClick={() => setHmsPage(p => Math.min(hmsTotalPages, p + 1))} disabled={hmsPage >= hmsTotalPages} className="rounded cursor-pointer border border-stroke text-[11px] text-fg-sub bg-bg-base px-3 py-1" style={{ opacity: hmsPage >= hmsTotalPages ? 0.4 : 1 }}></button>
<button onClick={() => setHmsPage(hmsTotalPages)} disabled={hmsPage >= hmsTotalPages} className="rounded cursor-pointer border border-stroke text-[11px] text-fg-sub bg-bg-base px-2.5 py-1" style={{ opacity: hmsPage >= hmsTotalPages ? 0.4 : 1 }}></button>
<span className="ml-2 text-fg-disabled">{hmsPage} / {hmsTotalPages} </span>
</div>
<span> <b className="text-color-warning">{hmsTotal.toLocaleString()}</b> </span>
</div>
</div>
{/* ── 물질 상세정보 패널 ── */}
{hmsSelectedSubstance && <HmsDetailPanel substance={hmsSelectedSubstance} activeTab={hmsDetailTab} onTabChange={setHmsDetailTab} />}
</div>
)}
</div>
</div>
)
}
/* ═══ HmsDetailPanel: 물질 상세정보 4-tab 패널 ═══ */
function HmsDetailPanel({ substance: s, activeTab, onTabChange }: { substance: HNSSearchSubstance; activeTab: number; onTabChange: (t: number) => void }) {
const tabLabels = ['📊 물질특성·위험정보', '🛡 방제거리·PPE·MSDS', '⚓ IBC CODE·EmS 대응', '🔗 화물적부도·항구별 코드']
const nfpa = s.nfpa
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const sebcColor = s.sebc.startsWith('G') ? 'var(--color-tertiary)' : s.sebc.startsWith('E') ? 'var(--color-danger)' : s.sebc.startsWith('F') ? 'var(--color-caution)' : s.sebc.startsWith('D') ? 'var(--color-accent)' : s.sebc.startsWith('S') ? 'var(--color-success)' : 'var(--fg-sub)'
return (
<div className="rounded-[10px] overflow-hidden bg-bg-card" style={{ border: '1px solid rgba(6,182,212,.25)', boxShadow: '0 4px 20px rgba(6,182,212,.08)' }}>
{/* Tab Navigation */}
<div className="flex border-b border-stroke" style={{ background: 'linear-gradient(90deg,rgba(6,182,212,.06),rgba(249,115,22,.04))' }}>
{tabLabels.map((label, i) => (
<button key={i} onClick={() => onTabChange(i)} style={{ flex: 1, padding: 10, fontSize: 10, fontWeight: activeTab === i ? 700 : 600, cursor: 'pointer', border: 'none', borderBottom: `2px solid ${activeTab === i ? 'var(--color-accent)' : 'transparent'}`, background: activeTab === i ? 'rgba(6,182,212,.06)' : 'transparent', color: activeTab === i ? 'var(--color-accent)' : 'var(--fg-disabled)' }}>{label}</button>
))}
</div>
{/* TAB 0: 물질특성·위험정보 */}
{activeTab === 0 && (
<div className="p-4">
{/* Header */}
<div className="flex items-center gap-[14px] mb-4 pb-[14px] border-b border-stroke">
<div className="flex items-center justify-center text-[22px] shrink-0" style={{ width: 52, height: 52, borderRadius: 10, background: 'linear-gradient(135deg,rgba(6,182,212,.15),rgba(249,115,22,.1))', border: '1px solid rgba(6,182,212,.25)' }}>🧪</div>
<div className="flex-1">
<div style={{ fontSize: 18, fontWeight: 800 }}>{s.nameKr} <span className="text-xs font-normal text-fg-disabled">({s.nameEn})</span></div>
<div className="flex flex-wrap gap-1.5 mt-1.5">
<span className="text-[9px] font-semibold font-mono text-color-accent rounded py-[2px] px-2" style={{ background: 'rgba(6,182,212,.1)', border: '1px solid rgba(6,182,212,.2)' }}>CAS: {s.casNumber}</span>
<span className="text-[9px] font-semibold font-mono text-color-warning rounded py-[2px] px-2" style={{ background: 'rgba(249,115,22,.1)', border: '1px solid rgba(249,115,22,.2)' }}>UN: {s.unNumber}</span>
<span className="text-[9px] font-semibold text-color-tertiary rounded py-[2px] px-2" style={{ background: 'rgba(168,85,247,.1)', border: '1px solid rgba(168,85,247,.2)' }}>: {s.transportMethod}</span>
<span className="text-[9px] font-semibold text-color-success rounded py-[2px] px-2" style={{ background: 'rgba(34,197,94,.1)', border: '1px solid rgba(34,197,94,.2)' }}>SEBC: {s.sebc}</span>
</div>
<div className="text-[9px] text-fg-disabled mt-1"><b>:</b> {s.synonymsKr} &nbsp;|&nbsp; <b>:</b> {s.hazardClass}</div>
</div>
</div>
<div className="grid grid-cols-2 gap-[14px]">
{/* Left: 물리·화학적 특성 */}
<div>
<div className="text-[11px] font-bold text-color-warning mb-2"> · </div>
<div className="grid grid-cols-2 gap-1 text-[9px]">
{([
['용도', s.usage, 'var(--color-accent)'],
['상태', s.state, 'var(--color-accent)'],
['색상', s.color, 'var(--color-warning)'],
['냄새', s.odor, 'var(--color-warning)'],
['인화점', s.flashPoint, 'var(--color-danger)'],
['발화점', s.autoIgnition, 'var(--color-danger)'],
['끓는점', s.boilingPoint, 'var(--color-tertiary)'],
['비중 (물=1)', s.density, 'var(--color-tertiary)'],
['용해도', s.solubility, 'var(--color-success)'],
['증기압', s.vaporPressure, 'var(--color-success)'],
['증기밀도 (공기=1)', s.vaporDensity, 'var(--color-caution)'],
['폭발범위', s.explosionRange, 'var(--color-caution)'],
] as [string, string, string][]).map(([label, value, borderColor]) => (
<div key={label} style={{ padding: '6px 8px', background: 'var(--bg-base)', borderRadius: 4, borderLeft: `3px solid ${borderColor}` }}>
<span className="text-fg-disabled">{label}</span><br />
<b style={{ color: label.includes('인화') || label.includes('폭발') ? 'var(--color-danger)' : label.includes('용해') ? 'var(--color-accent)' : 'var(--fg-default)' }}>{value}</b>
</div>
))}
</div>
</div>
{/* Right: NFPA + 위험등급 */}
<div>
<div className="text-[11px] font-bold text-color-danger mb-2"> ·</div>
<div className="flex gap-[14px] items-start mb-3">
<div className="relative shrink-0" style={{ width: 80, height: 80 }}>
<svg viewBox="0 0 100 100" width={80} height={80}>
<polygon points="50,5 95,50 50,95 5,50" fill="none" stroke="rgba(255,255,255,.15)" strokeWidth={1.5} />
<polygon points="50,5 72,28 50,50 28,28" fill="rgba(239,68,68,.2)" stroke="rgba(239,68,68,.5)" strokeWidth={1} />
<text x={50} y={32} textAnchor="middle" fill="#f87171" fontSize={16} fontWeight={800} fontFamily="monospace">{nfpa.health}</text>
<polygon points="72,28 95,50 72,72 50,50" fill="rgba(234,179,8,.2)" stroke="rgba(234,179,8,.5)" strokeWidth={1} />
<text x={77} y={55} textAnchor="middle" fill="#fbbf24" fontSize={16} fontWeight={800} fontFamily="monospace">{nfpa.fire}</text>
<polygon points="50,50 72,72 50,95 28,72" fill="rgba(255,255,255,.08)" stroke="rgba(255,255,255,.2)" strokeWidth={1} />
<text x={50} y={78} textAnchor="middle" fill="#e2e8f0" fontSize={12} fontWeight={700} fontFamily="sans-serif">{nfpa.special}</text>
<polygon points="5,50 28,28 50,50 28,72" fill="rgba(59,130,246,.2)" stroke="rgba(59,130,246,.5)" strokeWidth={1} />
<text x={23} y={55} textAnchor="middle" fill="#60a5fa" fontSize={16} fontWeight={800} fontFamily="monospace">{nfpa.reactivity}</text>
</svg>
<div className="text-center text-[7px] font-semibold text-fg-disabled mt-0.5">NFPA 704</div>
</div>
<div className="flex-1 flex flex-col gap-1 text-[8px]">
<div style={{ padding: '4px 8px', background: 'rgba(239,68,68,.06)', border: '1px solid rgba(239,68,68,.12)', borderRadius: 4 }}><span style={{ color: 'var(--color-danger)', fontWeight: 700 }}>() {nfpa.health}</span> {nfpa.health >= 4 ? '치명적' : nfpa.health >= 3 ? '중상' : nfpa.health >= 2 ? '장해' : nfpa.health >= 1 ? '경미한 손상' : '무해'}</div>
<div style={{ padding: '4px 8px', background: 'rgba(234,179,8,.06)', border: '1px solid rgba(234,179,8,.12)', borderRadius: 4 }}><span style={{ color: '#fbbf24', fontWeight: 700 }}>() {nfpa.fire}</span> {nfpa.fire >= 4 ? '93°F 미만' : nfpa.fire >= 3 ? '100°F 미만' : nfpa.fire >= 2 ? '200°F 미만' : nfpa.fire >= 1 ? '200°F 이상' : '비가연'}</div>
<div style={{ padding: '4px 8px', background: 'rgba(59,130,246,.06)', border: '1px solid rgba(59,130,246,.12)', borderRadius: 4 }}><span style={{ color: '#60a5fa', fontWeight: 700 }}>() {nfpa.reactivity}</span> {nfpa.reactivity >= 3 ? '폭발 가능' : nfpa.reactivity >= 2 ? '격렬 반응' : nfpa.reactivity >= 1 ? '불안정 가능' : '안정'}</div>
</div>
</div>
<div className="flex flex-col gap-1 text-[9px]">
<InfoBoxRow label="위험 분류" value={s.hazardClass} bg="rgba(239,68,68,.06)" border="rgba(239,68,68,.12)" labelColor="var(--color-danger)" valueColor="var(--color-danger)" />
<InfoBoxRow label="ERG 번호" value={s.ergNumber} bg="rgba(249,115,22,.06)" border="rgba(249,115,22,.12)" labelColor="var(--color-warning)" valueColor="var(--color-warning)" />
<InfoBoxRow label="IDLH" value={s.idlh} bg="rgba(168,85,247,.06)" border="rgba(168,85,247,.12)" labelColor="var(--color-tertiary)" valueColor="var(--color-danger)" />
<InfoBoxRow label="AEGL-2 (60분)" value={s.aegl2} bg="rgba(34,197,94,.06)" border="rgba(34,197,94,.12)" labelColor="var(--color-success)" valueColor="var(--color-warning)" />
<InfoBoxRow label="ERPG-2" value={s.erpg2} bg="rgba(6,182,212,.06)" border="rgba(6,182,212,.12)" labelColor="var(--color-accent)" valueColor="var(--color-warning)" />
</div>
</div>
</div>
</div>
)}
{/* TAB 1: 방제거리·PPE·MSDS */}
{activeTab === 1 && (
<div className="p-4">
<div className="grid grid-cols-2 gap-[14px]">
{/* 방제거리 */}
<div className="rounded-md overflow-hidden" style={{ border: '1px solid rgba(239,68,68,.2)' }}>
<div style={{ padding: '10px 12px', background: 'linear-gradient(135deg,rgba(239,68,68,.08),transparent)', borderBottom: '1px solid rgba(239,68,68,.12)' }}>
<div className="text-xs font-bold text-color-danger">🚧 (ERG {s.ergNumber})</div>
</div>
<div className="p-3 flex flex-col gap-2">
<div className="rounded-sm" style={{ padding: 10, background: 'rgba(249,115,22,.04)', border: '1px solid rgba(249,115,22,.12)' }}>
<div className="text-[10px] font-bold text-color-warning mb-1">🔥 </div>
<div className="text-[9px] text-fg-sub leading-[1.6]">: <b className="text-color-danger">{s.responseDistanceFire}</b> </div>
</div>
<div className="rounded-sm" style={{ padding: 10, background: 'rgba(168,85,247,.04)', border: '1px solid rgba(168,85,247,.12)' }}>
<div className="text-[10px] font-bold text-color-tertiary mb-1">💨 ()</div>
<div className="text-[9px] text-fg-sub leading-[1.6]"> : <b className="text-color-tertiary">{s.responseDistanceSpillDay}</b><br /> : <b className="text-color-danger">{s.responseDistanceSpillNight}</b></div>
</div>
<div className="rounded-sm" style={{ padding: 10, background: 'rgba(6,182,212,.04)', border: '1px solid rgba(6,182,212,.12)' }}>
<div className="text-[10px] font-bold text-color-accent mb-1">🌊 </div>
<div className="text-[9px] text-fg-sub leading-[1.6]">{s.marineResponse}</div>
</div>
</div>
</div>
<div>
{/* PPE */}
<div className="rounded-md overflow-hidden mb-3" style={{ border: '1px solid rgba(34,197,94,.2)' }}>
<div style={{ padding: '10px 12px', background: 'linear-gradient(135deg,rgba(34,197,94,.08),transparent)', borderBottom: '1px solid rgba(34,197,94,.12)' }}>
<div className="text-xs font-bold text-color-success">🛡 (PPE) </div>
</div>
<div className="grid grid-cols-2 gap-1.5 text-[9px] p-3">
<div className="text-center rounded" style={{ padding: 8, background: 'rgba(239,68,68,.04)', border: '1px solid rgba(239,68,68,.1)' }}>
<div className="text-base mb-[3px]">🧑🚒</div>
<div className="font-bold text-color-danger"></div>
<div className="text-[8px] text-fg-disabled mt-0.5">{s.ppeClose}</div>
</div>
<div className="text-center rounded" style={{ padding: 8, background: 'rgba(249,115,22,.04)', border: '1px solid rgba(249,115,22,.1)' }}>
<div className="text-base mb-[3px]">🦺</div>
<div className="font-bold text-color-warning"></div>
<div className="text-[8px] text-fg-disabled mt-0.5">{s.ppeFar}</div>
</div>
</div>
</div>
{/* MSDS */}
<div className="rounded-md overflow-hidden" style={{ border: '1px solid rgba(59,130,246,.2)' }}>
<div className="flex items-center justify-between" style={{ padding: '10px 12px', background: 'linear-gradient(135deg,rgba(59,130,246,.08),transparent)', borderBottom: '1px solid rgba(59,130,246,.12)' }}>
<div className="text-xs font-bold" style={{ color: '#60a5fa' }}>📄 MSDS </div>
<button className="text-[8px] font-semibold cursor-pointer rounded" style={{ padding: '3px 10px', background: 'rgba(59,130,246,.1)', border: '1px solid rgba(59,130,246,.2)', color: '#60a5fa' }}>📥 </button>
</div>
<div className="text-[8px] text-fg-sub leading-[1.7] p-2.5">
<b>§2 ·:</b> {s.msds.hazard}<br />
<b>§4 :</b> {s.msds.firstAid}<br />
<b>§5 :</b> {s.msds.fireFighting}<br />
<b>§6 :</b> {s.msds.spillResponse}<br />
<b>§8 :</b> {s.msds.exposure}<br />
<b>§15 :</b> {s.msds.regulation}
</div>
</div>
</div>
</div>
</div>
)}
{/* TAB 2: IBC CODE·EmS 대응 */}
{activeTab === 2 && (
<div className="p-4">
<div className="grid grid-cols-2 gap-[14px]">
{/* IBC CODE */}
<div className="rounded-md overflow-hidden" style={{ border: '1px solid rgba(249,115,22,.2)' }}>
<div style={{ padding: '10px 12px', background: 'linear-gradient(135deg,rgba(249,115,22,.08),transparent)', borderBottom: '1px solid rgba(249,115,22,.12)' }}>
<div className="text-xs font-bold text-color-warning"> IBC CODE </div>
</div>
<div className="p-3 flex flex-col gap-2 text-[9px]">
<div style={{ display: 'grid', gridTemplateColumns: '120px 1fr', gap: 4 }}>
{([
['위험성', s.ibcHazard],
['선박형식', s.ibcShipType],
['탱크형식', s.ibcTankType],
['탐지장비', s.ibcDetection],
['소화설비', s.ibcFireFighting],
['최소적재요건', s.ibcMinRequirement],
] as [string, string][]).map(([label, value]) => (
<React.Fragment key={label}>
<div className="rounded text-fg-disabled" style={{ padding: '6px 8px', background: 'var(--bg-base)' }}>{label}</div>
<div className="rounded font-semibold" style={{ padding: '6px 8px', background: 'var(--bg-base)' }}>{value}</div>
</React.Fragment>
))}
</div>
{/* Tank diagram SVG */}
<div className="rounded-sm text-center" style={{ padding: 10, background: 'var(--bg-base)' }}>
<svg viewBox="0 0 200 80" width={200} height={80} style={{ display: 'inline-block' }}>
<rect x={10} y={10} width={180} height={50} rx={4} fill="none" stroke="rgba(249,115,22,.4)" strokeWidth={1.5} />
<line x1={70} y1={10} x2={70} y2={60} stroke="rgba(249,115,22,.2)" strokeWidth={1} strokeDasharray="3" />
<line x1={130} y1={10} x2={130} y2={60} stroke="rgba(249,115,22,.2)" strokeWidth={1} strokeDasharray="3" />
<text x={40} y={38} textAnchor="middle" fill="var(--color-warning)" fontSize={7} fontFamily="var(--font-korean)">CARGO</text>
<text x={40} y={48} textAnchor="middle" fill="var(--fg-disabled)" fontSize={6} fontFamily="var(--font-mono)">Tank 1</text>
<text x={100} y={38} textAnchor="middle" fill="var(--color-warning)" fontSize={7} fontFamily="var(--font-korean)">CARGO</text>
<text x={100} y={48} textAnchor="middle" fill="var(--fg-disabled)" fontSize={6} fontFamily="var(--font-mono)">Tank 2</text>
<text x={160} y={38} textAnchor="middle" fill="var(--color-warning)" fontSize={7} fontFamily="var(--font-korean)">CARGO</text>
<text x={160} y={48} textAnchor="middle" fill="var(--fg-disabled)" fontSize={6} fontFamily="var(--font-mono)">Tank 3</text>
<text x={100} y={75} textAnchor="middle" fill="var(--fg-disabled)" fontSize={7} fontFamily="var(--font-korean)">{s.ibcShipType} {s.ibcTankType}</text>
</svg>
</div>
</div>
</div>
{/* EmS */}
<div className="rounded-md overflow-hidden" style={{ border: '1px solid rgba(239,68,68,.2)' }}>
<div style={{ padding: '10px 12px', background: 'linear-gradient(135deg,rgba(239,68,68,.08),transparent)', borderBottom: '1px solid rgba(239,68,68,.12)' }}>
<div className="text-xs font-bold text-color-danger">🆘 (EmS) ERG {s.ergNumber}</div>
</div>
<div className="p-3 flex flex-col gap-2 text-[9px]">
<div className="rounded-sm" style={{ padding: 8, background: 'rgba(239,68,68,.04)', border: '1px solid rgba(239,68,68,.1)' }}>
<div className="font-bold text-color-danger mb-[3px]">🔥 </div>
<div className="text-fg-sub leading-[1.6]">{s.emsFire}</div>
</div>
<div className="rounded-sm" style={{ padding: 8, background: 'rgba(168,85,247,.04)', border: '1px solid rgba(168,85,247,.1)' }}>
<div className="font-bold text-color-tertiary mb-[3px]">💧 </div>
<div className="text-fg-sub leading-[1.6]">{s.emsSpill}</div>
</div>
<div className="rounded-sm" style={{ padding: 8, background: 'rgba(6,182,212,.04)', border: '1px solid rgba(6,182,212,.1)' }}>
<div className="font-bold text-color-accent mb-[3px]">🏥 </div>
<div className="text-fg-sub leading-[1.6]">{s.emsFirstAid}</div>
</div>
<div className="text-center">
<button className="text-[10px] font-semibold cursor-pointer rounded-sm text-color-danger" style={{ padding: '6px 16px', background: 'rgba(239,68,68,.1)', border: '1px solid rgba(239,68,68,.2)' }}>📋 EmS </button>
</div>
</div>
</div>
</div>
</div>
)}
{/* TAB 3: 화물적부도·항구별 코드 */}
{activeTab === 3 && (
<div className="p-4">
<div className="grid grid-cols-2 gap-[14px]">
{/* 화물적부도 */}
<div className="rounded-lg overflow-hidden" style={{ border: '1px solid rgba(168,85,247,.2)' }}>
<div style={{ padding: '10px 12px', background: 'linear-gradient(135deg,rgba(168,85,247,.08),transparent)', borderBottom: '1px solid rgba(168,85,247,.12)' }}>
<div className="text-[12px] font-bold text-color-tertiary">📋 </div>
<div className="text-[8px] text-fg-disabled"> </div>
</div>
<div className="p-3">
<table className="w-full border-collapse text-[9px]">
<thead><tr style={{ background: 'rgba(168,85,247,.05)' }}>
<th className="text-color-tertiary text-left" style={{ padding: '6px 8px', borderBottom: '1px solid var(--stroke-light)' }}></th>
<th className="text-fg-sub text-left" style={{ padding: '6px 8px', borderBottom: '1px solid var(--stroke-light)' }}>/</th>
<th className="text-fg-sub text-left" style={{ padding: '6px 8px', borderBottom: '1px solid var(--stroke-light)' }}>/</th>
<th className="text-fg-sub text-center" style={{ padding: '6px 8px', borderBottom: '1px solid var(--stroke-light)' }}></th>
</tr></thead>
<tbody>
{s.cargoCodes.map((c, i) => {
const srcColor = c.source === '적부도' ? 'var(--color-warning)' : c.source === '용선자' ? 'var(--color-accent)' : 'var(--color-success)'
const srcBg = c.source === '적부도' ? 'rgba(249,115,22,.1)' : c.source === '용선자' ? 'rgba(6,182,212,.1)' : 'rgba(34,197,94,.1)'
return (
<tr key={i} style={{ borderBottom: i < s.cargoCodes.length - 1 ? '1px solid rgba(255,255,255,.04)' : undefined }}>
<td className="font-mono text-color-tertiary font-semibold cursor-pointer py-[5px] px-2">{c.code}</td>
<td className="py-[5px] px-2">{c.name}</td>
<td className="text-fg-disabled py-[5px] px-2">{c.company}</td>
<td className="text-center"><span style={{ padding: '1px 6px', borderRadius: 3, background: srcBg, color: srcColor, fontSize: 7 }}>{c.source}</span></td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
{/* 항구별 코드 */}
<div className="rounded-lg overflow-hidden" style={{ border: '1px solid rgba(6,182,212,.2)' }}>
<div style={{ padding: '10px 12px', background: 'linear-gradient(135deg,rgba(6,182,212,.08),transparent)', borderBottom: '1px solid rgba(6,182,212,.12)' }}>
<div className="text-[12px] font-bold text-color-accent">🏗 </div>
<div className="text-[8px] text-fg-disabled">Port-MIS </div>
</div>
<div className="p-3">
<table className="w-full border-collapse text-[9px]">
<thead><tr style={{ background: 'rgba(6,182,212,.05)' }}>
<th className="text-color-accent text-left" style={{ padding: '6px 8px', borderBottom: '1px solid var(--stroke-light)' }}></th>
<th className="text-fg-sub text-left" style={{ padding: '6px 8px', borderBottom: '1px solid var(--stroke-light)' }}></th>
<th className="text-fg-sub text-center" style={{ padding: '6px 8px', borderBottom: '1px solid var(--stroke-light)' }}> </th>
<th className="text-fg-sub text-center" style={{ padding: '6px 8px', borderBottom: '1px solid var(--stroke-light)' }}></th>
</tr></thead>
<tbody>
{s.portFrequency.map((p, i) => {
const freqColor = p.frequency === '높음' ? 'var(--color-danger)' : p.frequency === '중간' ? 'var(--color-warning)' : 'var(--color-success)'
const freqBg = p.frequency === '높음' ? 'rgba(239,68,68,.1)' : p.frequency === '중간' ? 'rgba(249,115,22,.1)' : 'rgba(34,197,94,.1)'
return (
<tr key={i} style={{ borderBottom: i < s.portFrequency.length - 1 ? '1px solid rgba(255,255,255,.04)' : undefined }}>
<td className="font-semibold py-[5px] px-2">{p.port}</td>
<td className="font-mono text-color-accent py-[5px] px-2">{p.portCode}</td>
<td className="text-center font-mono">{p.lastImport}</td>
<td className="text-center"><span style={{ padding: '1px 6px', borderRadius: 3, background: freqBg, color: freqColor, fontWeight: 600, fontSize: 8 }}>{p.frequency}</span></td>
</tr>
)
})}
</tbody>
</table>
<div className="mt-2.5 text-center">
<button className="text-color-accent text-[10px] font-semibold cursor-pointer rounded-sm" style={{ padding: '6px 16px', background: 'rgba(6,182,212,.1)', border: '1px solid rgba(6,182,212,.2)' }}>🔗 Port-MIS </button>
</div>
</div>
</div>
</div>
</div>
)}
</div>
)
}
function InfoBoxRow({ label, value, bg, border, labelColor, valueColor }: { label: string; value: string; bg: string; border: string; labelColor: string; valueColor: string }) {
return (
<div className="flex items-center justify-between rounded" style={{ padding: '6px 8px', background: bg, border: `1px solid ${border}` }}>
<span><b style={{ color: labelColor }}>{label}</b></span>
<span className="font-mono font-bold" style={{ color: valueColor }}>{value}</span>
</div>
)
}