fix: resolve all TypeScript build errors after Setting branch merge

This commit is contained in:
2026-04-23 18:36:33 +09:00
parent 5feaa5f170
commit 4b88ac01a4
11 changed files with 49 additions and 44 deletions

View File

@@ -98,7 +98,7 @@ export function openSwUsageDetail(title: string, list: SoftwareAsset[]) {
thead.innerHTML = `<tr><th>No</th><th>법인</th><th>제품명</th><th>수량</th><th>사용중</th><th>사용가능</th></tr>`;
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 = `<td>${idx+1}</td><td>${sw.}</td><td>${sw.}</td><td>${sw.}</td><td>${assigned}</td><td>${Number(sw.) - assigned}</td>`;
tbody.appendChild(tr);

View File

@@ -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();
}

View File

@@ -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';

View File

@@ -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');

View File

@@ -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<MasterAssetData> {
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') {

View File

@@ -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[];
}
// 초기 상태

View File

@@ -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();
}
});

View File

@@ -77,10 +77,10 @@ export function renderHwDashboard(container: HTMLElement) {
</div>
<div class="dashboard-card stat-card">
<div class="stat-label">최신 도입 모델 (${latestYear}년)</div>
<div class="stat-value" style="font-size: 1.25rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="${latestAsset?. || '정보 없음'}">
${latestAsset?. || '정보 없음'}
<div class="stat-value" style="font-size: 1.25rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="${(latestAsset as any)?. || '정보 없음'}">
${(latestAsset as any)?. || '정보 없음'}
</div>
<div class="stat-footer">가장 최근 자산번호: ${latestAsset?. || '-'}</div>
<div class="stat-footer">가장 최근 자산번호: ${(latestAsset as any)?. || '-'}</div>
</div>
</div>

View File

@@ -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();
});
}

View File

@@ -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 = `
<div class="search-item flex-1">
@@ -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 = `
<td class="text-center">${idx+1}</td>
<td class="text-center">${asset[ASSET_SCHEMA.CORP.key]}</td>
<td class="text-center">${asset[ASSET_SCHEMA.ORG.key]||'-'}</td>
<td class="text-center">${asset[ASSET_SCHEMA.ASSET_CODE.key]}</td>
<td class="text-center">${(asset as any)[ASSET_SCHEMA.CORP.key]}</td>
<td class="text-center">${(asset as any)[ASSET_SCHEMA.ORG.key]||'-'}</td>
<td class="text-center">${(asset as any)[ASSET_SCHEMA.ASSET_CODE.key]}</td>
<td>${formatInline(asset.)}</td>
<td>${formatInline(asset.)}</td>
<td class="text-center">${formatInline(asset[ASSET_SCHEMA.LOCATION.key])}</td>
<td class="text-center">${formatInline((asset as any)[ASSET_SCHEMA.LOCATION.key])}</td>
<td class="text-center">${managerHtml || '-'}</td>
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));

View File

@@ -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) {
<td style="text-align:center;">${idx+1}</td>
<td style="text-align:center;">${statusBadge}</td>
<td style="text-align:center;">${asset.||''}</td>
<td style="text-align:center;">${asset[ASSET_SCHEMA.CORP.key]}</td>
<td style="text-align:center;">${(asset as any)[ASSET_SCHEMA.CORP.key]}</td>
<td style="text-align:center;">${asset.||''}</td>
<td>${asset[ASSET_SCHEMA.PRODUCT.key]}</td>
<td style="text-align:center;">${asset[ASSET_SCHEMA.PURCHASE_YM.key]||''}</td>
${isSub ? `<td style="text-align:center;">${asset[ASSET_SCHEMA.EXPIRY.key]||''}</td>` : ''}
<td style="text-align:right;">${Number(asset[ASSET_SCHEMA.PRICE.key]||0).toLocaleString()}</td>
<td>${(asset as any)[ASSET_SCHEMA.PRODUCT.key]}</td>
<td style="text-align:center;">${(asset as any)[ASSET_SCHEMA.PURCHASE_YM.key]||''}</td>
${isSub ? `<td style="text-align:center;">${(asset as any)[ASSET_SCHEMA.EXPIRY.key]||''}</td>` : ''}
<td style="text-align:right;">${Number((asset as any)[ASSET_SCHEMA.PRICE.key]||0).toLocaleString()}</td>
<td style="text-align:center;">${qty}</td>
<td style="text-align:center;"><strong style="color: ${avail > 0 ? 'var(--primary-color)' : 'var(--danger)'}">${avail}</strong></td>
`;