From 4b88ac01a4a4950bb5c54414dd342d17091b2a29 Mon Sep 17 00:00:00 2001 From: JooWangi Date: Thu, 23 Apr 2026 18:36:33 +0900 Subject: [PATCH] fix: resolve all TypeScript build errors after Setting branch merge --- src/components/Modal/DashboardDetailModal.ts | 2 +- src/components/Modal/HWModal.ts | 17 +++++++------- src/components/Modal/SWModal.ts | 2 +- src/components/Modal/SWUserModal.ts | 2 +- src/core/excelHandler.ts | 9 ++++++-- src/core/state.ts | 3 ++- src/main.ts | 6 ++--- src/views/Dashboard/HwDashboard.ts | 6 ++--- src/views/DashboardView.ts | 2 +- src/views/List/StorageListView.ts | 24 ++++++++++---------- src/views/List/SwListView.ts | 20 ++++++++-------- 11 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/components/Modal/DashboardDetailModal.ts b/src/components/Modal/DashboardDetailModal.ts index 7c5fe94..7d43931 100644 --- a/src/components/Modal/DashboardDetailModal.ts +++ b/src/components/Modal/DashboardDetailModal.ts @@ -98,7 +98,7 @@ export function openSwUsageDetail(title: string, list: SoftwareAsset[]) { thead.innerHTML = `No법인제품명수량사용중사용가능`; tbody.innerHTML = ''; list.forEach((sw, idx) => { - const assigned = state.masterData.swUsers.filter(u => u.swId === sw.id).length; + const assigned = state.masterData.swUsers.filter(u => u.sw_id === sw.id).length; const tr = document.createElement('tr'); tr.innerHTML = `${idx+1}${sw.법인}${sw.제품명}${sw.수량}${assigned}${Number(sw.수량) - assigned}`; tbody.appendChild(tr); diff --git a/src/components/Modal/HWModal.ts b/src/components/Modal/HWModal.ts index b87c03f..10370d5 100644 --- a/src/components/Modal/HWModal.ts +++ b/src/components/Modal/HWModal.ts @@ -1,6 +1,6 @@ import { state, saveHardwareAsset, deleteHardwareAsset } from '../../core/state'; import { HardwareAsset } from '../../core/excelHandler'; -import { closeModals } from './BaseModal'; +import { openModal } from './BaseModal'; import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema'; import { createIcons, History, Plus, X, Save, Edit2, RotateCcw, Paperclip } from 'lucide'; import { CORP_LIST, ORG_LIST, HW_TYPE_LIST, LOCATION_DATA, TYPE_PREFIX_MAP } from './SharedData'; @@ -383,11 +383,10 @@ export function initHwModal(onSave: () => void, closeModalsCb: () => void) { { key: ASSET_SCHEMA.MODEL.key, label: ASSET_SCHEMA.MODEL.ui } ]; - const isNewAsset = !currentAsset || !currentAsset.자산코드; - - if (isNewAsset) { + if (!currentAsset || !currentAsset.자산코드) { diffLogs.push('자산 신규 등록'); } else { + const asset = currentAsset!; const newIp = String(getFieldValue('hw-IP주소') || getFieldValue('hw-IP주소-non-server') || '').trim(); const newLocation = String(isOpType ? extracted[ASSET_SCHEMA.STORE_LOC.key] : getCombinedLocation('hw-위치-빌딩', 'hw-위치-상세', 'hw-위치-기타') || '').trim(); @@ -396,19 +395,19 @@ export function initHwModal(onSave: () => void, closeModalsCb: () => void) { let newVal = ''; if (f.key === ASSET_SCHEMA.IP_ADDR.key) { - oldVal = String(currentAsset[ASSET_SCHEMA.IP_ADDR.key] || '').trim(); + oldVal = String(asset[ASSET_SCHEMA.IP_ADDR.key] || '').trim(); newVal = newIp; } else if (f.key === ASSET_SCHEMA.LOCATION.key) { - oldVal = String(currentAsset[ASSET_SCHEMA.LOCATION.key] || '').trim(); + oldVal = String(asset[ASSET_SCHEMA.LOCATION.key] || '').trim(); newVal = newLocation; } else if (f.key === ASSET_SCHEMA.MANAGER_MAIN.key) { - oldVal = String(currentAsset[ASSET_SCHEMA.MANAGER_MAIN.key] || '').trim(); + oldVal = String(asset[ASSET_SCHEMA.MANAGER_MAIN.key] || '').trim(); newVal = String(extracted[ASSET_SCHEMA.MANAGER_MAIN.key] || '').trim(); } else if (f.key === '상세용도') { - oldVal = String(currentAsset.상세용도 || '').trim(); + oldVal = String(asset.상세용도 || '').trim(); newVal = String((extracted.type !== 'PC' && extracted.type !== '개인PC') ? extracted.type : (extracted.상세용도 || '')).trim(); } else { - oldVal = String((currentAsset as any)[f.key] || '').trim(); + oldVal = String((asset as any)[f.key] || '').trim(); newVal = String(extracted[f.key] || '').trim(); } diff --git a/src/components/Modal/SWModal.ts b/src/components/Modal/SWModal.ts index 30c71bb..ca98fe6 100644 --- a/src/components/Modal/SWModal.ts +++ b/src/components/Modal/SWModal.ts @@ -1,6 +1,6 @@ import { state } from '../../core/state'; import { SoftwareAsset } from '../../core/excelHandler'; -import { closeModals } from './BaseModal'; +import { openModal } from './BaseModal'; import { openSwUserModal } from './SWUserModal'; import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema'; import { createIcons, History, Plus, X, Save, Edit2, RotateCcw } from 'lucide'; diff --git a/src/components/Modal/SWUserModal.ts b/src/components/Modal/SWUserModal.ts index 427bd28..89d04c0 100644 --- a/src/components/Modal/SWUserModal.ts +++ b/src/components/Modal/SWUserModal.ts @@ -100,7 +100,7 @@ export function openSwUserModal(asset: SoftwareAsset) { // 기존 사용자 데이터 복사 (원본 보호를 위해 temp 사용) const existingMapping = state.masterData.swUsers.find(u => u.sw_id === asset.id); - tempSwUsers = existingMapping ? JSON.parse(JSON.stringify(existingMapping.userDataList || [])) : []; + tempSwUsers = existingMapping ? JSON.parse(JSON.stringify(existingMapping.userData || [])) : []; renderUserList(); modal.classList.remove('hidden'); diff --git a/src/core/excelHandler.ts b/src/core/excelHandler.ts index 1845f84..dd77a14 100644 --- a/src/core/excelHandler.ts +++ b/src/core/excelHandler.ts @@ -1,6 +1,7 @@ import * as XLSX from 'xlsx'; export interface HardwareAsset { + [key: string]: any; id: string; type: string; // '개인PC', '서버', '스토리지', '전산비품', '모바일기기' 법인: string; @@ -47,13 +48,14 @@ export interface HardwareAsset { } export interface SoftwareAsset { + [key: string]: any; id: string; type: string; // '구독SW', '영구SW', '클라우드' 분야?: string; 법인: string; 부서?: string; 제품명: string; - 구매연월: string; + 구매연월?: string; 구독일?: string; 만료일?: string; 라이선스유형?: string; @@ -100,6 +102,9 @@ export interface MasterAssetData { mobile: HardwareAsset[]; subSw: SoftwareAsset[]; permSw: SoftwareAsset[]; + cloud: SoftwareAsset[]; + hw: HardwareAsset[]; + sw: SoftwareAsset[]; swUsers: any[]; // { sw_id, userData: [] } 형태로 처리 logs: HardwareLog[]; } @@ -168,7 +173,7 @@ export async function parseExcel(file: File): Promise { reader.onload = (e) => { try { const workbook = XLSX.read(e.target?.result, { type: 'binary' }); - const data: MasterAssetData = { pc: [], server: [], storage: [], equip: [], mobile: [], subSw: [], permSw: [], swUsers: [], logs: [] }; + const data: MasterAssetData = { pc: [], server: [], storage: [], equip: [], mobile: [], subSw: [], permSw: [], cloud: [], hw: [], sw: [], swUsers: [], logs: [] }; workbook.SheetNames.forEach(sheetName => { const rows = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName]) as any[]; if (sheetName === '개인PC') { diff --git a/src/core/state.ts b/src/core/state.ts index ed0fb5d..9b59c60 100644 --- a/src/core/state.ts +++ b/src/core/state.ts @@ -19,9 +19,10 @@ export interface MasterAssetData { } export interface AppState { - activeCategory: 'dashboard' | 'hw' | 'sw'; + activeCategory: 'dashboard' | 'hw' | 'sw' | 'ops'; activeSubTab: string; // '대시보드', '개인PC', '서버', '스토리지', '전산비품', '구독SW', '영구SW', '클라우드' masterData: MasterAssetData; + activeCharts?: any[]; } // 초기 상태 diff --git a/src/main.ts b/src/main.ts index ecceb7e..6f4e412 100644 --- a/src/main.ts +++ b/src/main.ts @@ -26,7 +26,7 @@ async function apiBatchSave(url: string, data: any[], label: string) { console.log(`✅ ${label} DB 저장 완료`); } catch (err) { console.error(`❌ ${label} DB 저장 오류:`, err); - alert(`${label} 저장 중 오류가 발생했습니다: ${err.message}`); + alert(`${label} 저장 중 오류가 발생했습니다: ${(err as any).message}`); } } @@ -125,9 +125,9 @@ function initApp() { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { const data = await parseExcel(file); - state.masterData = data; + state.masterData = { ...state.masterData, ...data }; await Promise.all([saveAllHardwareToDB(), saveAllSoftwareToDB()]); - handleTabChange(state.activeSubTab); + refreshView(); } }); diff --git a/src/views/Dashboard/HwDashboard.ts b/src/views/Dashboard/HwDashboard.ts index d0466c7..c1c5e5b 100644 --- a/src/views/Dashboard/HwDashboard.ts +++ b/src/views/Dashboard/HwDashboard.ts @@ -77,10 +77,10 @@ export function renderHwDashboard(container: HTMLElement) {
최신 도입 모델 (${latestYear}년)
-
- ${latestAsset?.모델명 || '정보 없음'} +
+ ${(latestAsset as any)?.모델명 || '정보 없음'}
- +
diff --git a/src/views/DashboardView.ts b/src/views/DashboardView.ts index c2425a4..ffca55d 100644 --- a/src/views/DashboardView.ts +++ b/src/views/DashboardView.ts @@ -11,7 +11,7 @@ export function renderDashboard(mainContent: HTMLElement) { // 기존 차트 리소스 해제 if (state.activeCharts) { - state.activeCharts.forEach(c => { + state.activeCharts.forEach((c: any) => { if (c && typeof c.destroy === 'function') c.destroy(); }); } diff --git a/src/views/List/StorageListView.ts b/src/views/List/StorageListView.ts index 405fe09..335a836 100644 --- a/src/views/List/StorageListView.ts +++ b/src/views/List/StorageListView.ts @@ -14,8 +14,8 @@ export function renderStorageList(container: HTMLElement) { const filterBar = document.createElement('div'); filterBar.className = 'search-bar'; - const corps = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CORP.key]))).filter(Boolean).sort(); - const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.ORG.key]))).filter(Boolean).sort(); + const corps = Array.from(new Set(fullList.map(a => (a as any)[ASSET_SCHEMA.CORP.key]))).filter(Boolean).sort(); + const orgUnits = Array.from(new Set(fullList.map(a => (a as any)[ASSET_SCHEMA.ORG.key]))).filter(Boolean).sort(); filterBar.innerHTML = `
@@ -70,10 +70,10 @@ export function renderStorageList(container: HTMLElement) { const filtered = fullList.filter(asset => { const matchKeyword = !keyword || - String(asset[ASSET_SCHEMA.ASSET_CODE.key]||'').toLowerCase().includes(keyword) || - String(asset[ASSET_SCHEMA.ORG.key]||'').toLowerCase().includes(keyword); - const matchCorp = !corp || asset[ASSET_SCHEMA.CORP.key] === corp; - const matchOrg = !orgUnit || asset[ASSET_SCHEMA.ORG.key] === orgUnit; + String((asset as any)[ASSET_SCHEMA.ASSET_CODE.key]||'').toLowerCase().includes(keyword) || + String((asset as any)[ASSET_SCHEMA.ORG.key]||'').toLowerCase().includes(keyword); + const matchCorp = !corp || (asset as any)[ASSET_SCHEMA.CORP.key] === corp; + const matchOrg = !orgUnit || (asset as any)[ASSET_SCHEMA.ORG.key] === orgUnit; return matchKeyword && matchCorp && matchOrg; }); @@ -87,8 +87,8 @@ export function renderStorageList(container: HTMLElement) { const tr = document.createElement('tr'); tr.style.cursor = 'pointer'; - const mainManager = asset[ASSET_SCHEMA.MANAGER_MAIN.key] || ''; - const subManager = asset[ASSET_SCHEMA.MANAGER_SUB.key] || ''; + const mainManager = (asset as any)[ASSET_SCHEMA.MANAGER_MAIN.key] || ''; + const subManager = (asset as any)[ASSET_SCHEMA.MANAGER_SUB.key] || ''; const managerHtml = [ mainManager ? `${createBadge('정', 'primary')} ${mainManager}` : '', subManager ? `${createBadge('부', 'muted')} ${subManager}` : '' @@ -96,12 +96,12 @@ export function renderStorageList(container: HTMLElement) { tr.innerHTML = ` ${idx+1} - ${asset[ASSET_SCHEMA.CORP.key]} - ${asset[ASSET_SCHEMA.ORG.key]||'-'} - ${asset[ASSET_SCHEMA.ASSET_CODE.key]} + ${(asset as any)[ASSET_SCHEMA.CORP.key]} + ${(asset as any)[ASSET_SCHEMA.ORG.key]||'-'} + ${(asset as any)[ASSET_SCHEMA.ASSET_CODE.key]} ${formatInline(asset.용도)} ${formatInline(asset.상세)} - ${formatInline(asset[ASSET_SCHEMA.LOCATION.key])} + ${formatInline((asset as any)[ASSET_SCHEMA.LOCATION.key])} ${managerHtml || '-'} `; tr.addEventListener('click', () => openHwModal(asset, 'view')); diff --git a/src/views/List/SwListView.ts b/src/views/List/SwListView.ts index abd3a9a..875e620 100644 --- a/src/views/List/SwListView.ts +++ b/src/views/List/SwListView.ts @@ -76,9 +76,9 @@ export function renderSwList(container: HTMLElement) { const corp = corpSelect ? corpSelect.value : ''; const filtered = fullList.filter(asset => { - const matchKeyword = !keyword || (asset[ASSET_SCHEMA.PRODUCT.key] || '').toLowerCase().includes(keyword) || (asset.부서 || '').toLowerCase().includes(keyword); + const matchKeyword = !keyword || ((asset as any)[ASSET_SCHEMA.PRODUCT.key] || '').toLowerCase().includes(keyword) || (asset.부서 || '').toLowerCase().includes(keyword); const matchField = !field || asset.분야 === field; - const matchCorp = !corp || asset[ASSET_SCHEMA.CORP.key] === corp; + const matchCorp = !corp || (asset as any)[ASSET_SCHEMA.CORP.key] === corp; return matchKeyword && matchField && matchCorp; }); @@ -90,14 +90,14 @@ export function renderSwList(container: HTMLElement) { filtered.forEach((asset, idx) => { const assigned = state.masterData.swUsers.filter(u => u.sw_id === asset.id).length; - const qty = typeof asset[ASSET_SCHEMA.QTY.key] === 'number' ? asset[ASSET_SCHEMA.QTY.key] : parseInt(asset[ASSET_SCHEMA.QTY.key]||'0', 10); + const qty = typeof (asset as any)[ASSET_SCHEMA.QTY.key] === 'number' ? (asset as any)[ASSET_SCHEMA.QTY.key] : parseInt((asset as any)[ASSET_SCHEMA.QTY.key]||'0', 10); const avail = qty - assigned; let statusBadge = ''; if (isSub) { let isExpired = false; - if (asset[ASSET_SCHEMA.EXPIRY.key]) { - const parts = asset[ASSET_SCHEMA.EXPIRY.key].split('~'); + if ((asset as any)[ASSET_SCHEMA.EXPIRY.key]) { + const parts = (asset as any)[ASSET_SCHEMA.EXPIRY.key].split('~'); const endDateStr = parts[parts.length - 1].trim().replace(/\./g, '-'); const endDate = new Date(endDateStr); if (!isNaN(endDate.getTime())) { @@ -117,12 +117,12 @@ export function renderSwList(container: HTMLElement) { ${idx+1} ${statusBadge} ${asset.분야||''} - ${asset[ASSET_SCHEMA.CORP.key]} + ${(asset as any)[ASSET_SCHEMA.CORP.key]} ${asset.부서||''} - ${asset[ASSET_SCHEMA.PRODUCT.key]} - ${asset[ASSET_SCHEMA.PURCHASE_YM.key]||''} - ${isSub ? `${asset[ASSET_SCHEMA.EXPIRY.key]||''}` : ''} - ${Number(asset[ASSET_SCHEMA.PRICE.key]||0).toLocaleString()} + ${(asset as any)[ASSET_SCHEMA.PRODUCT.key]} + ${(asset as any)[ASSET_SCHEMA.PURCHASE_YM.key]||''} + ${isSub ? `${(asset as any)[ASSET_SCHEMA.EXPIRY.key]||''}` : ''} + ${Number((asset as any)[ASSET_SCHEMA.PRICE.key]||0).toLocaleString()} ${qty} ${avail} `;