import { useState, useEffect, useCallback } from 'react'; import type { Dispatch, SetStateAction } from 'react'; import { fetchHnsAnalyses, type HnsAnalysisItem } from '../services/hnsApi'; interface HNSAnalysisListTableProps { onTabChange: Dispatch>; onSelectAnalysis?: (sn: number, localRsltData?: Record) => void; } const RISK_LABEL: Record = { CRITICAL: '심각', HIGH: '위험', MEDIUM: '경고', LOW: '관찰', }; const RISK_STYLE: Record = { CRITICAL: { bg: 'rgba(239,68,68,0.15)', color: 'var(--color-danger)' }, HIGH: { bg: 'rgba(249,115,22,0.15)', color: 'var(--color-warning)' }, MEDIUM: { bg: 'rgba(234,179,8,0.15)', color: 'var(--color-caution)' }, LOW: { bg: 'rgba(34,197,94,0.15)', color: 'var(--color-success)' }, }; function formatDate(dtm: string | null, mode: 'full' | 'date') { if (!dtm) return '—'; const d = new Date(dtm); if (mode === 'date') return d.toISOString().slice(0, 10); return d.toISOString().slice(0, 16).replace('T', ' '); } function substanceTag(sbstNm: string | null): string { if (!sbstNm) return '—'; const match = sbstNm.match(/\(([^)]+)\)/); if (match) return match[1]; return sbstNm.length > 6 ? sbstNm.slice(0, 6) : sbstNm; } export function HNSAnalysisListTable({ onTabChange, onSelectAnalysis }: HNSAnalysisListTableProps) { const [analyses, setAnalyses] = useState([]); const [loading, setLoading] = useState(true); const loadData = useCallback(async () => { setLoading(true); try { const items = await fetchHnsAnalyses(); setAnalyses(items); } catch (err) { console.error('[hns] 분석 목록 조회 실패, localStorage fallback:', err); // DB 실패 시 localStorage에서 불러오기 try { const localRaw = localStorage.getItem('hns_saved_analyses'); if (localRaw) { const localItems = JSON.parse(localRaw) as Record[]; const mapped: HnsAnalysisItem[] = localItems.map((entry) => { const rslt = entry.rsltData as Record | null; const inputP = rslt?.inputParams as Record | null; const coord = rslt?.coord as { lon: number; lat: number } | null; const weather = rslt?.weather as Record | null; return { hnsAnlysSn: (entry.id as number) || 0, anlysNm: (entry.anlysNm as string) || '로컬 저장 분석', acdntDtm: (entry.acdntDtm as string) || (inputP?.accidentDate && inputP?.accidentTime ? `${inputP.accidentDate as string}T${inputP.accidentTime as string}:00` : (inputP?.accidentDate as string)) || null, locNm: coord ? `${coord.lat.toFixed(4)} / ${coord.lon.toFixed(4)}` : null, lon: coord?.lon ?? null, lat: coord?.lat ?? null, sbstNm: (entry.sbstNm as string) || null, spilQty: (entry.spilQty as number) ?? null, spilUnitCd: (entry.spilUnitCd as string) || null, fcstHr: (entry.fcstHr as number) ?? null, algoCd: (inputP?.algorithm as string) || null, critMdlCd: (inputP?.criteriaModel as string) || null, windSpd: (weather?.windSpeed as number) ?? null, windDir: weather?.windDirection != null ? String(weather.windDirection) : null, execSttsCd: 'COMPLETED', riskCd: (entry.riskCd as string) || null, analystNm: (entry.analystNm as string) || null, rsltData: rslt ?? null, regDtm: (entry.regDtm as string) || new Date().toISOString(), _isLocal: true, } as HnsAnalysisItem & { _isLocal?: boolean }; }); setAnalyses(mapped); } } catch { // localStorage 파싱 실패 무시 } } finally { setLoading(false); } }, []); useEffect(() => { loadData(); }, [loadData]); return (
{/* Header */}

HNS 대기확산 분석 목록

총 {analyses.length}건

{/* Table */}
{loading ? (
로딩 중...
) : ( {analyses.map((item) => { const rslt = item.rsltData as Record | null; const isLocal = !!(item as HnsAnalysisItem & { _isLocal?: boolean })._isLocal; const riskLabel = RISK_LABEL[item.riskCd || ''] || item.riskCd || '—'; const riskStyle = RISK_STYLE[item.riskCd || ''] || { bg: 'rgba(100,100,100,0.1)', color: 'var(--fg-disabled)', }; const aegl3 = rslt?.aegl3 as boolean | undefined; const aegl2 = rslt?.aegl2 as boolean | undefined; const aegl1 = rslt?.aegl1 as boolean | undefined; const damageRadius = (rslt?.damageRadius as string) || '—'; const amount = item.spilQty != null ? `${item.spilQty} ${item.spilUnitCd || 'KL'}` : '—'; return ( onSelectAnalysis?.(item.hnsAnlysSn, isLocal && rslt ? rslt : undefined) } > ); })}
번호 분석명 물질 사고일시 분석날짜 사고지점 유출량 알고리즘 예측시간
AEGL-3 생명위협
AEGL-2 건강피해
AEGL-1 불쾌감
위험등급 피해반경 분석자
{item.hnsAnlysSn} {item.anlysNm} {substanceTag(item.sbstNm)} {formatDate(item.acdntDtm, 'full')} {formatDate(item.regDtm, 'date')} {item.locNm || '—'} {amount} {item.algoCd || '—'} {item.fcstHr ? `${item.fcstHr}H` : '—'}
{riskLabel} {damageRadius} {item.analystNm || '—'}
)} {!loading && analyses.length === 0 && (
분석 데이터가 없습니다.
)}
); }