import { sanitizeHtml } from '@common/utils/sanitize'; import type { OilSpillReportData } from './OilSpillReportTemplate'; // ─── Report Export Helpers ────────────────────────────── export function generateReportHTML( templateLabel: string, meta: { writeTime: string; author: string; jurisdiction: string }, sections: { title: string; fields: { key: string; label: string }[] }[], getVal: (key: string) => string ) { const rows = sections.map(section => { const fieldRows = section.fields.map(f => { if (f.label) { return `${f.label}${getVal(f.key) || '-'}` } return `${getVal(f.key) || '-'}` }).join('') return `

${section.title}

${fieldRows}
` }).join('') return `${templateLabel}

해양오염방제지원시스템

${templateLabel}

작성일시: ${meta.writeTime} | 작성자: ${meta.author || '-'} | 관할: ${meta.jurisdiction}

${rows}` } // eslint-disable-next-line @typescript-eslint/no-unused-vars export function exportAsPDF(html: string, _filename: string) { const sanitizedHtml = sanitizeHtml(html) const blob = new Blob([sanitizedHtml], { 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.print(), 500) } setTimeout(() => URL.revokeObjectURL(url), 30000) } export async function exportAsHWP( templateLabel: string, meta: { writeTime: string; author: string; jurisdiction: string }, sections: { title: string; fields: { key: string; label: string }[] }[], getVal: (key: string) => string, filename: string, images?: { step3?: string; step6?: string; sensitiveMap?: string; sensitivityMap?: string }, ) { const { exportAsHWPX } = await import('./hwpxExport'); await exportAsHWPX(templateLabel, meta, sections, getVal, filename, images); } export type ViewState = | { screen: 'list' } | { screen: 'templates' } | { screen: 'generate' } | { screen: 'view'; data: OilSpillReportData } | { screen: 'edit'; data: OilSpillReportData } export const typeColors: Record = { '초기보고서': { bg: 'rgba(6,182,212,0.15)', text: '#06b6d4' }, '지휘부 보고': { bg: 'rgba(168,85,247,0.15)', text: '#a855f7' }, '예측보고서': { bg: 'rgba(59,130,246,0.15)', text: '#3b82f6' }, '종합보고서': { bg: 'rgba(249,115,22,0.15)', text: '#f97316' }, '유출유 보고': { bg: 'rgba(234,179,8,0.15)', text: '#eab308' }, } export const statusColors: Record = { '완료': { bg: 'rgba(34,197,94,0.15)', text: '#22c55e' }, '수행중': { bg: 'rgba(249,115,22,0.15)', text: '#f97316' }, '테스트': { bg: 'rgba(138,150,168,0.15)', text: '#8a96a8' }, } export const analysisCatColors: Record = { '유출유 확산예측': { bg: 'rgba(6,182,212,0.12)', text: '#06b6d4', icon: '🛢' }, 'HNS 대기확산': { bg: 'rgba(249,115,22,0.12)', text: '#f97316', icon: '🧪' }, '긴급구난': { bg: 'rgba(239,68,68,0.12)', text: '#ef4444', icon: '🚨' }, } export function inferAnalysisCategory(report: OilSpillReportData): string { if (report.analysisCategory) return report.analysisCategory const t = (report.title || '').toLowerCase() const rt = report.reportType || '' if (t.includes('hns') || t.includes('대기확산') || t.includes('화학') || t.includes('aloha')) return 'HNS 대기확산' if (t.includes('구난') || t.includes('구조') || t.includes('긴급') || t.includes('salvage') || t.includes('rescue')) return '긴급구난' if (t.includes('유출유') || t.includes('확산예측') || t.includes('민감자원') || t.includes('유출사고') || t.includes('오염') || t.includes('방제') || rt === '유출유 보고' || rt === '예측보고서') return '유출유 확산예측' return '' } // ─── PDF/HWP 섹션 포맷 헬퍼 ───────────────────────────────── const TH = 'padding:6px 8px;border:1px solid #d1d5db;background:#f0f4f8;font-weight:600;font-size:11px;' const TD = 'padding:6px 8px;border:1px solid #d1d5db;font-size:11px;' const TABLE = 'width:100%;border-collapse:collapse;' function formatTideTable(tide: OilSpillReportData['tide']): string { if (!tide?.length) return '' const header = `날짜조형간조1만조1간조2만조2` const rows = tide.map(t => `${t.date}${t.tideType}${t.lowTide1}${t.highTide1}${t.lowTide2}${t.highTide2}` ).join('') return `${header}${rows}
` } function formatWeatherTable(weather: OilSpillReportData['weather']): string { if (!weather?.length) return '' const header = `시각풍향풍속유향유속파고` const rows = weather.map(w => `${w.time}${w.windDir}${w.windSpeed}${w.currentDir}${w.currentSpeed}${w.waveHeight}` ).join('') return `${header}${rows}
` } function formatSpreadTable(spread: OilSpillReportData['spread']): string { if (!spread?.length) return '' const header = `경과시간풍화량해상잔유량연안부착량면적` const rows = spread.map(s => `${s.elapsed}${s.weathered}${s.seaRemain}${s.coastAttach}${s.area}` ).join('') return `${header}${rows}
` } function formatSensitiveTable(r: OilSpillReportData): string { const parts: string[] = [] if (r.sensitiveMapImage) { parts.push( '

민감자원 분포 지도

' + `` ) } if (r.aquaculture?.length) { const h = `종류면적거리` const rows = r.aquaculture.map(a => `${a.type}${a.area}${a.distance}`).join('') parts.push(`

양식업

${h}${rows}
`) } if (r.beaches?.length) { const h = `해수욕장명거리` const rows = r.beaches.map(b => `${b.name}${b.distance}`).join('') parts.push(`

해수욕장

${h}${rows}
`) } if (r.markets?.length) { const h = `수산시장명거리` const rows = r.markets.map(m => `${m.name}${m.distance}`).join('') parts.push(`

수산시장

${h}${rows}
`) } if (r.esi?.length) { const h = `코드유형길이` const rows = r.esi.map(e => `${e.code}${e.type}${e.length}`).join('') parts.push(`

ESI 해안선

${h}${rows}
`) } if (r.species?.length) { const h = `분류종명` const rows = r.species.map(s => `${s.category}${s.species}`).join('') parts.push(`

보호생물

${h}${rows}
`) } if (r.habitat?.length) { const h = `유형면적` const rows = r.habitat.map(h2 => `${h2.type}${h2.area}`).join('') parts.push(`

서식지

${h}${rows}
`) } if (r.sensitivityMapImage) { parts.push( '

통합민감도 평가 지도

' + `` ) } if (r.sensitivity?.length) { const h = `민감도면적` const rows = r.sensitivity.map(s => `${s.level}${s.area}`).join('') parts.push(`

민감도 등급

${h}${rows}
`) } return parts.join('') } function formatVesselsTable(vessels: OilSpillReportData['vessels']): string { if (!vessels?.length) return '' const header = `선명기관거리속력톤수회수장비오일붐` const rows = vessels.map(v => `${v.name}${v.org}${v.dist}${v.speed}${v.ton}${v.collectorType} ${v.collectorCap}${v.boomType} ${v.boomLength}` ).join('') return `${header}${rows}
` } function formatRecoveryTable(recovery: OilSpillReportData['recovery']): string { if (!recovery?.length) return '' const header = `선박명회수 기간` const rows = recovery.map(r => `${r.shipName}${r.period}` ).join('') return `${header}${rows}
` } function formatResultTable(result: OilSpillReportData['result']): string { if (!result) return '' return `
유출총량${result.spillTotal}풍화총량${result.weatheredTotal}
회수총량${result.recoveredTotal}해상잔유량${result.seaRemainTotal}
연안부착량${result.coastAttachTotal}
` } export function buildReportGetVal(report: OilSpillReportData) { return (key: string): string => { if (key === 'author') return report.author ?? '' if (key.startsWith('incident.')) { const f = key.split('.')[1] // eslint-disable-next-line @typescript-eslint/no-explicit-any return (report.incident as any)[f] || '' } if (key === '__tide') return formatTideTable(report.tide) if (key === '__weather') return formatWeatherTable(report.weather) if (key === '__spreadMaps') { const img3 = report.step3MapImage const img6 = report.step6MapImage if (!img3 && !img6) return '' const cell = (label: string, src: string) => `

${label}

` + `
` return `
` + (img3 ? cell('3시간 후', img3) : '') + (img6 ? cell('6시간 후', img6) : '') + `
` } if (key === '__spread') return formatSpreadTable(report.spread) if (key === '__sensitive') return formatSensitiveTable(report) if (key === '__vessels') return formatVesselsTable(report.vessels) if (key === '__recovery') return formatRecoveryTable(report.recovery) if (key === '__result') return formatResultTable(report.result) // eslint-disable-next-line @typescript-eslint/no-explicit-any return (report as any)[key] || '' } }