merge: integrate collaborator features and synchronize with shared DB infrastructure

This commit is contained in:
2026-04-21 10:00:57 +09:00
25 changed files with 5706 additions and 1906 deletions

View File

@@ -13,56 +13,47 @@ import { initSwUserModal } from './components/Modal/SWUserModal';
import { initDashboardDetailModal } from './components/Modal/DashboardDetailModal';
import { createIcons, Download, Upload, FileSpreadsheet, Plus, X, LayoutDashboard, Monitor, Server, Database, Laptop, CalendarClock, Key, Cpu, Layers, Users, Paperclip, Edit2, History, RefreshCcw } from 'lucide';
// --- DB 저장을 위한 헬퍼 함수 ---
async function saveAllHwToDB(assets: HardwareAsset[]) {
// --- DB 저장을 위한 세분화된 헬퍼 함수 ---
async function apiBatchSave(url: string, data: any[], label: string) {
try {
const response = await fetch('http://localhost:3000/api/hw/batch', {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(assets)
body: JSON.stringify(data)
});
if (!response.ok) throw new Error('HW DB 저장 실패');
console.log('✅ HW DB 저장 완료');
if (!response.ok) throw new Error(`${label} DB 저장 실패`);
console.log(`${label} DB 저장 완료`);
} catch (err) {
console.error('❌ HW DB 저장 실패:', err);
console.error(`${label} DB 저장 오류:`, err);
}
}
async function saveAllSwToDB(assets: SoftwareAsset[]) {
try {
const response = await fetch('http://localhost:3000/api/sw/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(assets)
});
if (!response.ok) throw new Error('SW DB 저장 실패');
console.log('✅ SW DB 저장 완료');
} catch (err) {
console.error('❌ SW DB 저장 실패:', err);
}
}
const savePcToDB = () => apiBatchSave('http://localhost:3000/api/pc/batch', state.masterData.pc, '개인PC');
const saveServerToDB = () => apiBatchSave('http://localhost:3000/api/server/batch', state.masterData.server, '서버');
const saveStorageToDB = () => apiBatchSave('http://localhost:3000/api/storage/batch', state.masterData.storage, '스토리지');
const saveEquipToDB = () => apiBatchSave('http://localhost:3000/api/equip/batch', state.masterData.equip, '전산비품');
const saveMobileToDB = () => apiBatchSave('http://localhost:3000/api/mobile/batch', state.masterData.mobile, '모바일기기');
const saveSubSwToDB = () => apiBatchSave('http://localhost:3000/api/sw/sub/batch', state.masterData.subSw, '구독SW');
const savePermSwToDB = () => apiBatchSave('http://localhost:3000/api/sw/perm/batch', state.masterData.permSw, '영구SW');
const saveSwUsersToDB = () => apiBatchSave('http://localhost:3000/api/sw-users/batch', state.masterData.swUsers, 'SW사용자');
async function saveAllSwUsersToDB(users: SWUser[]) {
try {
const response = await fetch('http://localhost:3000/api/sw-users/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(users)
});
if (!response.ok) throw new Error('SW User DB 저장 실패');
console.log('✅ SW User DB 저장 완료');
} catch (err) {
console.error('❌ SW User DB 저장 실패:', err);
}
// 모든 하드웨어 DB 동기화
async function saveAllHardwareToDB() {
await Promise.all([
savePcToDB(),
saveServerToDB(),
saveStorageToDB(),
saveEquipToDB(),
saveMobileToDB()
]);
}
// --- App Initialization ---
function initApp() {
console.log('🚀 ITAM System Initializing...');
console.log('🚀 ITAM Dedicated System Initializing...');
const mainContent = document.getElementById('main-content')!;
if (!mainContent) return;
// 1. 전역 모달 및 내비게이션 초기화
const { closeAllModals } = initBaseModal();
try {
@@ -74,45 +65,29 @@ function initApp() {
}
});
initPcModal(() => {
saveAllHwToDB(state.masterData.hw);
renderSWTable(mainContent);
}, closeAllModals);
initHwModal(() => {
saveAllHwToDB(state.masterData.hw);
renderSWTable(mainContent);
}, closeAllModals);
initStorageModal(() => {
saveAllHwToDB(state.masterData.hw);
renderSWTable(mainContent);
}, closeAllModals);
// 동료의 새로운 UI 방식(renderSWTable)과 우리의 통합 저장 로직 결합
initPcModal(() => { saveAllHardwareToDB(); renderSWTable(mainContent); }, closeAllModals);
initHwModal(() => { saveAllHardwareToDB(); renderSWTable(mainContent); }, closeAllModals);
initStorageModal(() => { saveAllHardwareToDB(); renderSWTable(mainContent); }, closeAllModals);
initSwModal(() => {
saveAllSwToDB(state.masterData.sw);
if (state.activeSubTab === '구독SW') saveSubSwToDB();
else savePermSwToDB();
renderSWTable(mainContent);
}, closeAllModals);
initCloudModal(() => {
saveAllSwToDB(state.masterData.sw);
// 클라우드 저장 로직 추가 필요시 여기에 구현
renderSWTable(mainContent);
}, closeAllModals);
initSwUserModal(() => {
saveAllSwUsersToDB(state.masterData.swUsers);
renderSWTable(mainContent);
}, closeAllModals);
initSwUserModal(() => { saveSwUsersToDB(); renderSWTable(mainContent); }, closeAllModals);
initDashboardDetailModal();
} catch (e) {
console.error('❌ Initialization failed:', e);
}
} catch (e) { console.error('❌ Initialization failed:', e); }
// 2. 초기 렌더링
renderDashboard(mainContent);
// 3. 비동기 데이터 로드
loadMasterDataFromDB().then((success) => {
if (success) {
if (state.activeSubTab === '대시보드') renderDashboard(mainContent);
@@ -120,7 +95,6 @@ function initApp() {
}
});
// 4. 이벤트 바인딩
document.getElementById('btn-download-template')?.addEventListener('click', () => downloadTemplate());
document.getElementById('btn-export-excel')?.addEventListener('click', () => exportToExcel(state.masterData));
@@ -130,11 +104,9 @@ function initApp() {
if (file) {
const data = await parseExcel(file);
state.masterData = data;
// 엑셀 업로드 시 모든 카테고리 일괄 덮어쓰기 저장
await Promise.all([
saveAllHwToDB(data.hw),
saveAllSwToDB(data.sw),
saveAllSwUsersToDB(data.swUsers)
saveAllHardwareToDB(),
saveSubSwToDB(), savePermSwToDB(), saveSwUsersToDB()
]);
renderSWTable(mainContent);
}
@@ -146,7 +118,7 @@ function initApp() {
id: Math.random().toString(36).substring(2, 9),
type: state.activeSubTab,
: '한맥', : '', : '', : '', : '', IP주소: '', MACaddress: '', HW사양: '', OS: '', : '', : ''
} as any);
} as any, 'add');
} else if (state.activeSubTab === '클라우드') {
openCloudModal({ type: '클라우드', : '', : '', 수량: 1, : '', : '', : '', : '한맥', : '' } as any);
} else if (state.activeSubTab === '구독SW' || state.activeSubTab === '영구SW') {