import { openModal, closeModals } from './BaseModal'; import { createIcons, X, Check, Database, Save, FileSpreadsheet, Layers, RefreshCcw } from 'lucide'; import { state, loadMasterDataFromDB } from '../../core/state'; import { TYPE_PREFIX_MAP } from './SharedData'; let parsedData: any = null; let currentTab: string = ''; let onSuccessCallback: (() => void) | null = null; const UPLOAD_PREVIEW_MODAL_HTML = ` `; export function initUploadPreviewModal(onSuccess?: () => void) { if (onSuccess) onSuccessCallback = onSuccess; if (!document.getElementById('upload-preview-modal')) { document.body.insertAdjacentHTML('beforeend', UPLOAD_PREVIEW_MODAL_HTML); } document.getElementById('btn-close-upload-preview')?.addEventListener('click', closeModals); document.getElementById('btn-cancel-upload')?.addEventListener('click', closeModals); document.getElementById('btn-confirm-upload')?.addEventListener('click', () => { confirmUpload(); }); document.getElementById('btn-bulk-generate-codes')?.addEventListener('click', () => { generateBulkCodes(); }); } export function openUploadPreview(data: any) { parsedData = data; const tabNames = Object.keys(data); if (tabNames.length === 0) { alert('업로드할 데이터가 없습니다.'); return; } currentTab = tabNames[0]; renderTabs(); renderCurrentTable(); openModal('upload-preview-modal'); createIcons({ icons: { X, Check, Database, Save, FileSpreadsheet, Layers, RefreshCcw } }); } function renderTabs() { const container = document.getElementById('upload-tabs-container'); if (!container) return; container.innerHTML = ''; Object.keys(parsedData).forEach(tab => { const btn = document.createElement('div'); btn.className = `upload-tab-btn ${tab === currentTab ? 'active' : ''}`; btn.style.cssText = ` padding: 0.75rem 1rem; border-radius: 8px; cursor: pointer; font-size: 13px; font-weight: 500; display: flex; justify-content: space-between; align-items: center; transition: all 0.2s; background: ${tab === currentTab ? 'white' : 'transparent'}; color: ${tab === currentTab ? 'var(--primary-color)' : 'var(--text-main)'}; box-shadow: ${tab === currentTab ? '0 2px 4px rgba(0,0,0,0.05)' : 'none'}; border: 1px solid ${tab === currentTab ? 'var(--border-color)' : 'transparent'}; `; btn.innerHTML = ` ${tab} ${parsedData[tab].length} `; btn.onclick = () => { currentTab = tab; renderTabs(); renderCurrentTable(); }; container.appendChild(btn); }); } function renderCurrentTable() { const tableWrapper = document.getElementById('upload-preview-table-wrapper'); const tabNameEl = document.getElementById('current-tab-name'); const tabCountEl = document.getElementById('current-tab-count'); if (!tableWrapper || !tabNameEl || !tabCountEl) return; const data = parsedData[currentTab]; tabNameEl.textContent = currentTab; tabCountEl.textContent = `${data.length}건`; const generateBtn = document.getElementById('btn-bulk-generate-codes'); const isHwTab = ['개인PC', '서버', '스토리지', '전산비품', '모바일기기'].includes(currentTab); if (generateBtn) { if (isHwTab) generateBtn.classList.remove('hidden'); else generateBtn.classList.add('hidden'); } if (!data || data.length === 0) { tableWrapper.innerHTML = '
표시할 데이터가 없습니다.
'; return; } // Get headers from first item keys, excluding 'id' and 'type' for cleaner view const headers = Object.keys(data[0]).filter(k => k !== 'id' && k !== 'type'); let tableHTML = ` ${headers.map(h => ``).join('')} ${data.map((row: any, idx: number) => ` ${headers.map(h => ``).join('')} `).join('')}
No.${h}
${idx + 1}${row[h] || '-'}
`; tableWrapper.innerHTML = tableHTML; } async function confirmUpload() { const confirmBtn = document.getElementById('btn-confirm-upload') as HTMLButtonElement; if (confirmBtn) { confirmBtn.disabled = true; confirmBtn.innerHTML = ' 저장 중...'; createIcons({ icons: { Save } }); } try { const tabNames = Object.keys(parsedData); let successCount = 0; for (const tab of tabNames) { const data = parsedData[tab]; let endpoint = ''; const API_BASE = `http://${location.hostname}:3000`; if (tab === '개인PC') endpoint = `${API_BASE}/api/pc/batch`; else if (tab === '서버') endpoint = `${API_BASE}/api/server/batch`; else if (tab === '스토리지') endpoint = `${API_BASE}/api/storage/batch`; else if (tab === '전산비품') endpoint = `${API_BASE}/api/equip/batch`; else if (tab === '모바일기기') endpoint = `${API_BASE}/api/mobile/batch`; else if (tab === '구독SW') endpoint = `${API_BASE}/api/sw/sub/batch`; else if (tab === '영구SW') endpoint = `${API_BASE}/api/sw/perm/batch`; else if (tab === '클라우드') endpoint = `${API_BASE}/api/cloud/batch`; else if (tab === '도메인') endpoint = `${API_BASE}/api/ops/domain/batch`; if (endpoint) { try { const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (response.ok) { successCount++; } else { const errRes = await response.json(); throw new Error(`[${tab}] ${errRes.error || '저장 실패'}`); } } catch (e: any) { alert(`카테고리 '${tab}' 저장 중 오류: ${e.message}`); throw e; // Stop processing further tabs } } } if (successCount > 0) { if (onSuccessCallback) onSuccessCallback(); closeModals(); alert(`${successCount}개 카테고리의 데이터가 성공적으로 업로드되었습니다.`); } else { alert('데이터 업로드에 실패했습니다.'); } } catch (err) { console.error(err); // 상세 에러는 내부 catch에서 이미 alert으로 띄움 } finally { if (confirmBtn) { confirmBtn.disabled = false; confirmBtn.innerHTML = ' 최종 데이터 저장하기'; createIcons({ icons: { Save } }); } } } async function generateBulkCodes() { const data = parsedData[currentTab]; if (!data) return; const generateBtn = document.getElementById('btn-bulk-generate-codes') as HTMLButtonElement; if (generateBtn) { generateBtn.disabled = true; generateBtn.innerHTML = ' 생성 중...'; createIcons({ icons: { RefreshCcw } }); } try { // Group rows by prefix (type + purchase_ym) const rowsToProcess = data.filter((r: any) => !r.자산코드); if (rowsToProcess.length === 0) { alert('이미 모든 항목에 자산코드가 부여되어 있습니다.'); return; } const groups: Record = {}; rowsToProcess.forEach((r: any) => { const type = r.비품유형 || r.기기유형 || r.type || 'ETC'; const typeCode = TYPE_PREFIX_MAP[type] || 'ETC'; const purchaseYM = String(r.구매연월 || '').replace(/[^0-9]/g, ''); if (purchaseYM.length < 6) { // Fallback or skip return; } const prefix = `${typeCode}-${purchaseYM.substring(0, 6)}-`; if (!groups[prefix]) groups[prefix] = []; groups[prefix].push(r); }); for (const prefix in groups) { const rows = groups[prefix]; // Fetch current next code for this prefix const res = await fetch(`http://${location.hostname}:3000/api/generate-asset-code?prefix=${prefix}`); const result = await res.json(); if (result.nextCode) { let baseNum = parseInt(result.nextCode.replace(prefix, '')); rows.forEach((r, idx) => { r.자산코드 = `${prefix}${(baseNum + idx).toString().padStart(4, '0')}`; }); } } renderCurrentTable(); alert(`${rowsToProcess.length}건의 자산코드가 생성되었습니다.`); } catch (err) { console.error(err); alert('자산코드 생성 중 오류가 발생했습니다.'); } finally { if (generateBtn) { generateBtn.disabled = false; generateBtn.innerHTML = ' 자산코드 일괄 생성'; createIcons({ icons: { RefreshCcw } }); } } }