';
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 = `
No.
${headers.map(h => `
${h}
`).join('')}
${data.map((row: any, idx: number) => `
${idx + 1}
${headers.map(h => `
${row[h] || '-'}
`).join('')}
`).join('')}
`;
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 } });
}
}
}