fix: AppState 타입에 currentUserMobile 선언 추가 및 initializeAppDirectly 인자 누락 오류 수정
All checks were successful
ITAM Code Check / build-and-config-check (push) Successful in 11s
ITAM Docker Build Check / docker-build-check (push) Successful in 16s

This commit is contained in:
이태훈
2026-07-02 15:51:53 +09:00
parent 0e88be4755
commit f22e27b14a
2 changed files with 293 additions and 291 deletions

View File

@@ -1,293 +1,295 @@
import { HardwareAsset, SoftwareAsset, SWUser, HardwareLog, MasterAssetData, SystemUser } from './types'; import { HardwareAsset, SoftwareAsset, SWUser, HardwareLog, MasterAssetData, SystemUser } from './types';
import { API_BASE_URL } from './utils'; import { API_BASE_URL } from './utils';
// --- State Definitions --- // --- State Definitions ---
export interface AppState { export interface AppState {
activeCategory: 'dashboard' | 'hw' | 'sw' | 'ops' | 'vip' | 'fac' | 'users' | 'etc'; activeCategory: 'dashboard' | 'hw' | 'sw' | 'ops' | 'vip' | 'fac' | 'users' | 'etc';
activeSubTab: string; activeSubTab: string;
viewMode: 'location' | 'legacy' | 'list'; viewMode: 'location' | 'legacy' | 'list';
masterData: MasterAssetData; masterData: MasterAssetData;
activeCharts: any[]; activeCharts: any[];
currentUserRole: 'admin' | 'user'; currentUserRole: 'admin' | 'user';
listFilters?: Record<string, any>; currentUserMobile?: string;
} listFilters?: Record<string, any>;
}
// 초기 상태
export const state: AppState = { // 초기 상태
activeCategory: 'hw', export const state: AppState = {
activeSubTab: '대시보드', activeCategory: 'hw',
viewMode: 'location', activeSubTab: '대시보드',
activeCharts: [], viewMode: 'location',
currentUserRole: 'user', activeCharts: [],
listFilters: {}, currentUserRole: 'user',
masterData: { currentUserMobile: '',
users: [], listFilters: {},
pc: [], server: [], storage: [], network: [], masterData: {
survey: [], pcParts: [], partsMaster: [], equipment: [], officeSupplies: [], users: [],
swInternal: [], swExternal: [], cloud: [], domain: [], pc: [], server: [], storage: [], network: [],
cost: [], vip: [], survey: [], pcParts: [], partsMaster: [], equipment: [], officeSupplies: [],
hw: [], sw: [], swInternal: [], swExternal: [], cloud: [], domain: [],
swUsers: [], logs: [], cost: [], vip: [],
jobSpecs: [], hw: [], sw: [],
mobile: [] swUsers: [], logs: [],
} jobSpecs: [],
}; mobile: []
}
(window as any).__itam_state = state; };
/** (window as any).__itam_state = state;
* 통합 V2 스키마에 맞춘 데이터 로드
*/ /**
export async function loadMasterDataFromDB() { * 통합 V2 스키마에 맞춘 데이터 로드
try { */
const response = await fetch(`${API_BASE_URL}/api/assets/master`); export async function loadMasterDataFromDB() {
if (!response.ok) throw new Error('Failed to fetch master data'); try {
const response = await fetch(`${API_BASE_URL}/api/assets/master`);
const data = await response.json(); if (!response.ok) throw new Error('Failed to fetch master data');
// DB의 쪼개진 asset_remote 데이터로부터 가상 대표 속성(IP, MAC, 원격도구)을 주입해주는 전처리 함수 const data = await response.json();
const preprocessAssets = (assets: any[]) => {
if (!Array.isArray(assets)) return; // DB의 쪼개진 asset_remote 데이터로부터 가상 대표 속성(IP, MAC, 원격도구)을 주입해주는 전처리 함수
assets.forEach((asset: any) => { const preprocessAssets = (assets: any[]) => {
let ip = ''; if (!Array.isArray(assets)) return;
let mac = ''; assets.forEach((asset: any) => {
let remoteTool = ''; let ip = '';
let remoteId = ''; let mac = '';
let remoteTool = '';
let remoteId = '';
let remotePw = ''; let remotePw = '';
let rems: any[] = []; let rems: any[] = [];
try { try {
rems = asset.remotes ? (typeof asset.remotes === 'string' ? JSON.parse(asset.remotes) : asset.remotes) : []; rems = asset.remotes ? (typeof asset.remotes === 'string' ? JSON.parse(asset.remotes) : asset.remotes) : [];
} catch(e) {} } catch(e) {}
if (Array.isArray(rems)) { if (Array.isArray(rems)) {
rems.forEach((r: any) => { rems.forEach((r: any) => {
if (r.type === 'IP') { if (r.type === 'IP') {
if (!ip) ip = r.val1 || ''; if (!ip) ip = r.val1 || '';
if (r.val2) { if (r.val2) {
if (String(r.val2).trim().startsWith('{')) { if (String(r.val2).trim().startsWith('{')) {
try { try {
const parsed = JSON.parse(r.val2); const parsed = JSON.parse(r.val2);
remoteTool = r.name || '원격접속'; remoteTool = r.name || '원격접속';
remoteId = parsed.id || ''; remoteId = parsed.id || '';
remotePw = parsed.pw || ''; remotePw = parsed.pw || '';
} catch(e) {} } catch(e) {}
} else { } else {
if (!mac) mac = r.val2 || ''; if (!mac) mac = r.val2 || '';
} }
} }
} else if (r.type === 'MAC') { } else if (r.type === 'MAC') {
if (!mac) mac = r.val1 || ''; if (!mac) mac = r.val1 || '';
} else if (r.type === 'REMOTE') { } else if (r.type === 'REMOTE') {
if (!remoteTool) remoteTool = r.name || ''; if (!remoteTool) remoteTool = r.name || '';
if (!remoteId) remoteId = r.val1 || ''; if (!remoteId) remoteId = r.val1 || '';
if (!remotePw) remotePw = r.val2 || ''; if (!remotePw) remotePw = r.val2 || '';
} }
}); });
} }
// 최상위 가상 속성 바인딩 (목록 및 위치보기 뷰어 매핑용) // 최상위 가상 속성 바인딩 (목록 및 위치보기 뷰어 매핑용)
asset.ip_address = ip; asset.ip_address = ip;
asset.mac_address = mac; asset.mac_address = mac;
asset.remote_tool = remoteTool; asset.remote_tool = remoteTool;
asset.remote_id = remoteId; asset.remote_id = remoteId;
asset.remote_pw = remotePw; asset.remote_pw = remotePw;
}); });
}; };
if (data) { if (data) {
const keys = ['pc', 'server', 'storage', 'network', 'survey', 'equipment', 'officeSupplies']; const keys = ['pc', 'server', 'storage', 'network', 'survey', 'equipment', 'officeSupplies'];
keys.forEach(k => { keys.forEach(k => {
if (data[k]) preprocessAssets(data[k]); if (data[k]) preprocessAssets(data[k]);
}); });
} }
// 전역 상태 업데이트 // 전역 상태 업데이트
state.masterData = { state.masterData = {
...state.masterData, ...state.masterData,
...data, ...data,
jobSpecs: data.jobSpecs || [], jobSpecs: data.jobSpecs || [],
logs: (data.logs || []).map((l: any) => ({ logs: (data.logs || []).map((l: any) => ({
...l, ...l,
assetId: l.asset_id || l.assetId, assetId: l.asset_id || l.assetId,
date: l.log_date || l.date, date: l.log_date || l.date,
user: l.log_user || l.user, user: l.log_user || l.user,
log_date: l.log_date || l.date, log_date: l.log_date || l.date,
log_user: l.log_user || l.user log_user: l.log_user || l.user
})) }))
}; };
// Mapping for backward compatibility // Mapping for backward compatibility
(state.masterData as any).equip = state.masterData.equipment; (state.masterData as any).equip = state.masterData.equipment;
(state.masterData as any).subSw = state.masterData.swExternal; (state.masterData as any).subSw = state.masterData.swExternal;
(state.masterData as any).permSw = state.masterData.swInternal; (state.masterData as any).permSw = state.masterData.swInternal;
// 하드웨어 통합 (대시보드 호환용) // 하드웨어 통합 (대시보드 호환용)
state.masterData.hw = [ state.masterData.hw = [
...state.masterData.pc, ...state.masterData.pc,
...state.masterData.server, ...state.masterData.server,
...state.masterData.storage, ...state.masterData.storage,
...state.masterData.network, ...state.masterData.network,
...state.masterData.survey, ...state.masterData.survey,
...state.masterData.equipment, ...state.masterData.equipment,
...state.masterData.officeSupplies ...state.masterData.officeSupplies
]; ];
// 소프트웨어 통합 // 소프트웨어 통합
state.masterData.sw = [ state.masterData.sw = [
...state.masterData.swInternal, ...state.masterData.swInternal,
...state.masterData.swExternal, ...state.masterData.swExternal,
...(state.masterData.cloud || []) ...(state.masterData.cloud || [])
]; ];
console.log('✅ V2 Normalized data loaded successfully'); console.log('✅ V2 Normalized data loaded successfully');
return true; return true;
} catch (err) { } catch (err) {
console.warn('⚠️ Dummy 로드 실패:', err); console.warn('⚠️ Dummy 로드 실패:', err);
} }
return false; return false;
} }
export function updateState(newState: Partial<AppState>) { export function updateState(newState: Partial<AppState>) {
Object.assign(state, newState); Object.assign(state, newState);
} }
/** /**
* 자산 저장 (V2 Normalized API) * 자산 저장 (V2 Normalized API)
*/ */
export async function saveAsset(category: string, asset: any) { export async function saveAsset(category: string, asset: any) {
try { try {
const url = `${API_BASE_URL}/api/asset/${category}/save`; const url = `${API_BASE_URL}/api/asset/${category}/save`;
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(asset) body: JSON.stringify(asset)
}); });
if (response.ok) { if (response.ok) {
await loadMasterDataFromDB(); // 전역 상태 갱신 await loadMasterDataFromDB(); // 전역 상태 갱신
return true; return true;
} }
} catch (err) { } catch (err) {
console.error('자산 저장 실패:', err); console.error('자산 저장 실패:', err);
} }
return false; return false;
} }
/** /**
* 자산 삭제 (V2 API) * 자산 삭제 (V2 API)
*/ */
export async function deleteAsset(category: string, assetId: string) { export async function deleteAsset(category: string, assetId: string) {
try { try {
const url = `${API_BASE_URL}/api/asset/${category}/${assetId}`; const url = `${API_BASE_URL}/api/asset/${category}/${assetId}`;
const response = await fetch(url, { method: 'DELETE' }); const response = await fetch(url, { method: 'DELETE' });
if (response.ok) { if (response.ok) {
await loadMasterDataFromDB(); // 전역 상태 갱신 await loadMasterDataFromDB(); // 전역 상태 갱신
return true; return true;
} }
} catch (err) { } catch (err) {
console.error('자산 삭제 실패:', err); console.error('자산 삭제 실패:', err);
} }
return false; return false;
} }
export async function savePartsMaster(component: any) { export async function savePartsMaster(component: any) {
try { try {
const url = `${API_BASE_URL}/api/hardware-components/save`; const url = `${API_BASE_URL}/api/hardware-components/save`;
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(component) body: JSON.stringify(component)
}); });
if (response.ok) { if (response.ok) {
await loadMasterDataFromDB(); // 전역 상태 갱신 await loadMasterDataFromDB(); // 전역 상태 갱신
return true; return true;
} }
} catch (err) { } catch (err) {
console.error('부품 마스터 저장 실패:', err); console.error('부품 마스터 저장 실패:', err);
} }
return false; return false;
} }
export async function deletePartsMaster(id: number) { export async function deletePartsMaster(id: number) {
try { try {
const url = `${API_BASE_URL}/api/hardware-components/${id}`; const url = `${API_BASE_URL}/api/hardware-components/${id}`;
const response = await fetch(url, { method: 'DELETE' }); const response = await fetch(url, { method: 'DELETE' });
if (response.ok) { if (response.ok) {
await loadMasterDataFromDB(); // 전역 상태 갱신 await loadMasterDataFromDB(); // 전역 상태 갱신
return true; return true;
} }
} catch (err) { } catch (err) {
console.error('부품 마스터 삭제 실패:', err); console.error('부품 마스터 삭제 실패:', err);
} }
return false; return false;
} }
export async function saveSystemUser(user: any) { export async function saveSystemUser(user: any) {
try { try {
const url = `${API_BASE_URL}/api/system-users/save`; const url = `${API_BASE_URL}/api/system-users/save`;
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user) body: JSON.stringify(user)
}); });
if (response.ok) { if (response.ok) {
await loadMasterDataFromDB(); // 전역 상태 갱신 await loadMasterDataFromDB(); // 전역 상태 갱신
return true; return true;
} }
} catch (err) { } catch (err) {
console.error('사용자 정보 저장 실패:', err); console.error('사용자 정보 저장 실패:', err);
} }
return false; return false;
} }
export async function deleteSystemUser(id: string) { export async function deleteSystemUser(id: string) {
try { try {
const url = `${API_BASE_URL}/api/system-users/${id}`; const url = `${API_BASE_URL}/api/system-users/${id}`;
const response = await fetch(url, { method: 'DELETE' }); const response = await fetch(url, { method: 'DELETE' });
if (response.ok) { if (response.ok) {
await loadMasterDataFromDB(); // 전역 상태 갱신 await loadMasterDataFromDB(); // 전역 상태 갱신
return true; return true;
} }
} catch (err) { } catch (err) {
console.error('사용자 정보 삭제 실패:', err); console.error('사용자 정보 삭제 실패:', err);
} }
return false; return false;
} }
export async function saveJobSpec(spec: any) { export async function saveJobSpec(spec: any) {
try { try {
const url = `${API_BASE_URL}/api/job-specs/save`; const url = `${API_BASE_URL}/api/job-specs/save`;
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(spec) body: JSON.stringify(spec)
}); });
if (response.ok) { if (response.ok) {
await loadMasterDataFromDB(); // 전역 상태 갱신 await loadMasterDataFromDB(); // 전역 상태 갱신
return true; return true;
} }
} catch (err) { } catch (err) {
console.error('직무별 기준 사양 저장 실패:', err); console.error('직무별 기준 사양 저장 실패:', err);
} }
return false; return false;
} }
export async function deleteJobSpec(id: number) { export async function deleteJobSpec(id: number) {
try { try {
const url = `${API_BASE_URL}/api/job-specs/${id}`; const url = `${API_BASE_URL}/api/job-specs/${id}`;
const response = await fetch(url, { method: 'DELETE' }); const response = await fetch(url, { method: 'DELETE' });
if (response.ok) { if (response.ok) {
await loadMasterDataFromDB(); // 전역 상태 갱신 await loadMasterDataFromDB(); // 전역 상태 갱신
return true; return true;
} }
} catch (err) { } catch (err) {
console.error('직무별 기준 사양 삭제 실패:', err); console.error('직무별 기준 사양 삭제 실패:', err);
} }
return false; return false;
} }

View File

@@ -385,7 +385,7 @@ function showLoginScreen(errorMessage?: string) {
if (payload.status === 'authenticated') { if (payload.status === 'authenticated') {
clearPhonePollTimer(); clearPhonePollTimer();
initializeAppDirectly(); initializeAppDirectly(loginId);
return; return;
} }