fix: AppState 타입에 currentUserMobile 선언 추가 및 initializeAppDirectly 인자 누락 오류 수정
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ function showLoginScreen(errorMessage?: string) {
|
|||||||
|
|
||||||
if (payload.status === 'authenticated') {
|
if (payload.status === 'authenticated') {
|
||||||
clearPhonePollTimer();
|
clearPhonePollTimer();
|
||||||
initializeAppDirectly();
|
initializeAppDirectly(loginId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user