From 1fbd297988e59fdd8f7919fa7f794ce87c2618fa Mon Sep 17 00:00:00 2001 From: JooWangi Date: Thu, 23 Apr 2026 20:25:58 +0900 Subject: [PATCH] feat: Add bulk asset code generation in upload review modal --- src/components/Modal/UploadPreviewModal.ts | 78 +++++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/src/components/Modal/UploadPreviewModal.ts b/src/components/Modal/UploadPreviewModal.ts index ff8ffaf..4c9fe56 100644 --- a/src/components/Modal/UploadPreviewModal.ts +++ b/src/components/Modal/UploadPreviewModal.ts @@ -1,6 +1,7 @@ import { openModal, closeModals } from './BaseModal'; -import { createIcons, X, Check, Database, Save, FileSpreadsheet, Layers } from 'lucide'; +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 = ''; @@ -37,6 +38,9 @@ const UPLOAD_PREVIEW_MODAL_HTML = `
선택된 탭 없음 0건 +
* 아래 데이터가 신규로 추가되거나 기존 데이터가 갱신됩니다. @@ -72,6 +76,9 @@ export function initUploadPreviewModal(onSuccess?: () => void) { document.getElementById('btn-confirm-upload')?.addEventListener('click', () => { confirmUpload(); }); + document.getElementById('btn-bulk-generate-codes')?.addEventListener('click', () => { + generateBulkCodes(); + }); } export function openUploadPreview(data: any) { @@ -87,7 +94,7 @@ export function openUploadPreview(data: any) { renderCurrentTable(); openModal('upload-preview-modal'); - createIcons({ icons: { X, Check, Database, Save, FileSpreadsheet, Layers } }); + createIcons({ icons: { X, Check, Database, Save, FileSpreadsheet, Layers, RefreshCcw } }); } function renderTabs() { @@ -138,6 +145,13 @@ function renderCurrentTable() { 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; @@ -222,3 +236,63 @@ async function confirmUpload() { } } } + +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://172.16.40.100: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 } }); + } + } +}