feat: 자산 관리 시스템 고도화 및 데이터 구조 최적화
- 모바일 자산(Mobile) 카테고리 추가 및 엑셀 업로드/다운로드 지원 - 클라우드 자산(Cloud) 및 변경 이력(Logs) 테이블 및 API 구현 - 데이터베이스 초기화 로직 개선 및 테이블 자동 생성 기능 추가 - 하드웨어 저장 로직 통합 및 카테고리 판별 자동화 - SW 대시보드 사용량 산출 방식 개선 (sw_id 기반 맵핑) - 수동 모달(Storage)을 통합 하드웨어 모달(HWModal)로 통합 및 정리
This commit is contained in:
@@ -9,13 +9,17 @@ export interface MasterAssetData {
|
||||
mobile: HardwareAsset[];
|
||||
subSw: SoftwareAsset[];
|
||||
permSw: SoftwareAsset[];
|
||||
cloud: SoftwareAsset[]; // 클라우드 배열 추가
|
||||
swUsers: SWUser[];
|
||||
logs: HardwareLog[];
|
||||
|
||||
// 동료 코드 호환용 통합 배열 (프론트엔드 로직용)
|
||||
sw: SoftwareAsset[];
|
||||
}
|
||||
|
||||
export interface AppState {
|
||||
activeCategory: 'dashboard' | 'hw' | 'sw';
|
||||
activeSubTab: string; // '대시보드', '개인PC', '서버', '스토리지', '전산비품', '구독SW', '영구SW'
|
||||
activeSubTab: string; // '대시보드', '개인PC', '서버', '스토리지', '전산비품', '구독SW', '영구SW', '클라우드'
|
||||
masterData: MasterAssetData;
|
||||
}
|
||||
|
||||
@@ -31,6 +35,8 @@ export const state: AppState = {
|
||||
mobile: [],
|
||||
subSw: [],
|
||||
permSw: [],
|
||||
cloud: [],
|
||||
sw: [], // 호환용
|
||||
swUsers: [],
|
||||
logs: []
|
||||
}
|
||||
@@ -49,19 +55,42 @@ export async function loadMasterDataFromDB() {
|
||||
{ key: 'mobile', url: 'http://localhost:3000/api/mobile' },
|
||||
{ key: 'subSw', url: 'http://localhost:3000/api/sw/sub' },
|
||||
{ key: 'permSw', url: 'http://localhost:3000/api/sw/perm' },
|
||||
{ key: 'swUsers', url: 'http://localhost:3000/api/sw-users' }
|
||||
{ key: 'cloud', url: 'http://localhost:3000/api/cloud' },
|
||||
{ key: 'swUsers', url: 'http://localhost:3000/api/sw-users' },
|
||||
{ key: 'logs', url: 'http://localhost:3000/api/logs' }
|
||||
];
|
||||
|
||||
const results = await Promise.all(endpoints.map(e => fetch(e.url)));
|
||||
|
||||
// 기존 데이터 초기화 (재분류 전)
|
||||
state.masterData.pc = [];
|
||||
state.masterData.server = [];
|
||||
state.masterData.storage = [];
|
||||
state.masterData.equip = [];
|
||||
state.masterData.mobile = [];
|
||||
|
||||
for (let i = 0; i < endpoints.length; i++) {
|
||||
if (results[i].ok) {
|
||||
const data = await results[i].json();
|
||||
(state.masterData as any)[endpoints[i].key] = data || [];
|
||||
const key = endpoints[i].key;
|
||||
|
||||
if (['pc', 'server', 'storage', 'equip', 'mobile'].includes(key)) {
|
||||
// 하드웨어 데이터는 자동 재분류 로직 통과
|
||||
(data as HardwareAsset[]).forEach(asset => saveHardwareAsset(asset));
|
||||
} else {
|
||||
(state.masterData as any)[key] = data || [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 6개 테이블 데이터 로드 완료');
|
||||
// 동료 코드 호환을 위한 통합 sw 배열 생성
|
||||
state.masterData.sw = [
|
||||
...state.masterData.subSw,
|
||||
...state.masterData.permSw,
|
||||
...state.masterData.cloud
|
||||
];
|
||||
|
||||
console.log('✅ 모든 DB 데이터 로드 및 통합 완료');
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.warn('⚠️ 백엔드 서버 연결 실패. 로컬 데이터를 유지합니다.');
|
||||
@@ -78,18 +107,25 @@ export function updateState(newState: Partial<AppState>) {
|
||||
* 하드웨어 자산 통합 저장 (자동 카테고리 분류)
|
||||
*/
|
||||
export function saveHardwareAsset(updatedAsset: HardwareAsset) {
|
||||
const { type } = updatedAsset;
|
||||
const detailPurpose = (updatedAsset as any).상세용도 || '';
|
||||
const type = updatedAsset.type || '';
|
||||
const detailPurpose = (updatedAsset as any).상세용도 || updatedAsset.detail_purpose || '';
|
||||
|
||||
// 1. 타겟 카테고리 결정
|
||||
// 1. 타겟 카테고리 결정 (유연한 검색)
|
||||
let targetKey: keyof MasterAssetData = 'equip';
|
||||
if (type === '서버' || (type === 'PC' && detailPurpose === '서버')) targetKey = 'server';
|
||||
else if (['NAS', 'DAS', '스토리지'].includes(type)) targetKey = 'storage';
|
||||
else if (['CPU', 'GPU', 'RAM', 'HDD'].includes(type)) targetKey = 'equip';
|
||||
else if (['모바일', '태블릿', '노트북'].includes(type)) targetKey = 'mobile';
|
||||
else if (type === 'PC' && detailPurpose === '개인PC') targetKey = 'pc';
|
||||
|
||||
if (type.includes('서버') || detailPurpose.includes('서버')) {
|
||||
targetKey = 'server';
|
||||
} else if (['NAS', 'DAS', '스토리지'].some(t => type.includes(t))) {
|
||||
targetKey = 'storage';
|
||||
} else if (['모바일', '태블릿', '휴대폰', '핸드폰', '노트북'].some(t => type.includes(t))) {
|
||||
targetKey = 'mobile';
|
||||
} else if (type === 'PC' || type === '개인PC' || detailPurpose === '개인PC') {
|
||||
targetKey = 'pc';
|
||||
} else if (['CPU', 'GPU', 'RAM', 'HDD'].some(t => type.toUpperCase().includes(t))) {
|
||||
targetKey = 'equip';
|
||||
}
|
||||
|
||||
// 2. 모든 카테고리에서 기존 ID 자산 삭제 (이동 가능성 대비)
|
||||
// 2. 모든 카테고리에서 기존 ID 자산 삭제 (중복 방지)
|
||||
const hwKeys: (keyof MasterAssetData)[] = ['pc', 'server', 'storage', 'equip', 'mobile'];
|
||||
hwKeys.forEach(key => {
|
||||
const arr = state.masterData[key] as HardwareAsset[];
|
||||
|
||||
Reference in New Issue
Block a user