🔧 그룹 리더 대시보드 수정사항: - API 호출 방식 수정 (modern-dashboard.js) - 서버 API 요구사항에 맞는 데이터 구조 변경 - work_entries 배열 구조로 변경 - work_type_id → task_id 필드명 매핑 - 400 Bad Request 오류 해결 ⚡ 작업 분석 시스템 성능 최적화: - 중복 함수 제거 (isWeekend, isVacationProject 통합) - WorkAnalysisAPI 캐싱 시스템 구현 (5분 만료) - 네임스페이스 조직화 (utils, ui, analysis, render) - ErrorHandler 통합 에러 처리 시스템 - 성능 모니터링 및 메모리 누수 방지 - GPU 가속 CSS 애니메이션 추가 - 디바운스/스로틀 함수 적용 - 의미 없는 통계 카드 제거 📊 작업 분석 페이지 개선: - 프로그레스 바 애니메이션 - 토스트 알림 시스템 - 부드러운 전환 효과 - 반응형 최적화 - 메모리 사용량 모니터링
268 lines
8.9 KiB
JavaScript
268 lines
8.9 KiB
JavaScript
/**
|
|
* Work Analysis Module Loader
|
|
* 작업 분석 모듈들을 순서대로 로드하고 초기화하는 로더
|
|
*/
|
|
|
|
class WorkAnalysisModuleLoader {
|
|
constructor() {
|
|
this.modules = [
|
|
{ name: 'API Client', path: '/js/work-analysis/api-client.js', loaded: false },
|
|
{ name: 'Data Processor', path: '/js/work-analysis/data-processor.js', loaded: false },
|
|
{ name: 'State Manager', path: '/js/work-analysis/state-manager.js', loaded: false },
|
|
{ name: 'Table Renderer', path: '/js/work-analysis/table-renderer.js', loaded: false },
|
|
{ name: 'Chart Renderer', path: '/js/work-analysis/chart-renderer.js', loaded: false },
|
|
{ name: 'Main Controller', path: '/js/work-analysis/main-controller.js', loaded: false }
|
|
];
|
|
|
|
this.loadingPromise = null;
|
|
}
|
|
|
|
/**
|
|
* 모든 모듈 로드
|
|
* @returns {Promise} 로딩 완료 Promise
|
|
*/
|
|
async loadAll() {
|
|
if (this.loadingPromise) {
|
|
return this.loadingPromise;
|
|
}
|
|
|
|
this.loadingPromise = this._loadModules();
|
|
return this.loadingPromise;
|
|
}
|
|
|
|
/**
|
|
* 모듈들을 순차적으로 로드
|
|
*/
|
|
async _loadModules() {
|
|
console.log('🚀 작업 분석 모듈 로딩 시작');
|
|
|
|
try {
|
|
// 의존성 순서대로 로드
|
|
for (const module of this.modules) {
|
|
await this._loadModule(module);
|
|
}
|
|
|
|
console.log('✅ 모든 작업 분석 모듈 로딩 완료');
|
|
this._onAllModulesLoaded();
|
|
|
|
} catch (error) {
|
|
console.error('❌ 모듈 로딩 실패:', error);
|
|
this._onLoadingError(error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 개별 모듈 로드
|
|
* @param {Object} module - 모듈 정보
|
|
*/
|
|
async _loadModule(module) {
|
|
return new Promise((resolve, reject) => {
|
|
console.log(`📦 로딩 중: ${module.name}`);
|
|
|
|
const script = document.createElement('script');
|
|
script.src = module.path;
|
|
script.type = 'text/javascript';
|
|
|
|
script.onload = () => {
|
|
module.loaded = true;
|
|
console.log(`✅ 로딩 완료: ${module.name}`);
|
|
resolve();
|
|
};
|
|
|
|
script.onerror = (error) => {
|
|
console.error(`❌ 로딩 실패: ${module.name}`, error);
|
|
reject(new Error(`Failed to load ${module.name}: ${module.path}`));
|
|
};
|
|
|
|
document.head.appendChild(script);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 모든 모듈 로딩 완료 시 호출
|
|
*/
|
|
_onAllModulesLoaded() {
|
|
// 전역 변수 확인
|
|
const requiredGlobals = [
|
|
'WorkAnalysisAPI',
|
|
'WorkAnalysisDataProcessor',
|
|
'WorkAnalysisState',
|
|
'WorkAnalysisTableRenderer',
|
|
'WorkAnalysisChartRenderer'
|
|
];
|
|
|
|
const missingGlobals = requiredGlobals.filter(name => !window[name]);
|
|
|
|
if (missingGlobals.length > 0) {
|
|
console.warn('⚠️ 일부 전역 객체가 누락됨:', missingGlobals);
|
|
}
|
|
|
|
// 하위 호환성을 위한 전역 함수들 설정
|
|
this._setupLegacyFunctions();
|
|
|
|
// 모듈 로딩 완료 이벤트 발생
|
|
window.dispatchEvent(new CustomEvent('workAnalysisModulesLoaded', {
|
|
detail: { modules: this.modules }
|
|
}));
|
|
|
|
console.log('🎉 작업 분석 시스템 준비 완료');
|
|
}
|
|
|
|
/**
|
|
* 하위 호환성을 위한 전역 함수 설정
|
|
*/
|
|
_setupLegacyFunctions() {
|
|
// 기존 HTML에서 사용하던 함수들을 새 모듈 시스템으로 연결
|
|
const legacyFunctions = {
|
|
// 기간 확정
|
|
confirmPeriod: () => {
|
|
if (window.WorkAnalysisMainController) {
|
|
window.WorkAnalysisMainController.handlePeriodConfirm();
|
|
}
|
|
},
|
|
|
|
// 분석 모드 변경
|
|
switchAnalysisMode: (mode) => {
|
|
if (window.WorkAnalysisState) {
|
|
window.WorkAnalysisState.setAnalysisMode(mode);
|
|
}
|
|
},
|
|
|
|
// 탭 변경
|
|
switchTab: (tabId) => {
|
|
if (window.WorkAnalysisState) {
|
|
window.WorkAnalysisState.setCurrentTab(tabId);
|
|
}
|
|
},
|
|
|
|
// 개별 분석 함수들
|
|
analyzeWorkStatus: () => {
|
|
if (window.WorkAnalysisMainController) {
|
|
window.WorkAnalysisMainController.analyzeWorkStatus();
|
|
}
|
|
},
|
|
|
|
analyzeProjectDistribution: () => {
|
|
if (window.WorkAnalysisMainController) {
|
|
window.WorkAnalysisMainController.analyzeProjectDistribution();
|
|
}
|
|
},
|
|
|
|
analyzeWorkerPerformance: () => {
|
|
if (window.WorkAnalysisMainController) {
|
|
window.WorkAnalysisMainController.analyzeWorkerPerformance();
|
|
}
|
|
},
|
|
|
|
analyzeErrorAnalysis: () => {
|
|
if (window.WorkAnalysisMainController) {
|
|
window.WorkAnalysisMainController.analyzeErrorAnalysis();
|
|
}
|
|
}
|
|
};
|
|
|
|
// 전역 함수로 등록
|
|
Object.assign(window, legacyFunctions);
|
|
|
|
console.log('🔗 하위 호환성 함수 설정 완료');
|
|
}
|
|
|
|
/**
|
|
* 로딩 에러 처리
|
|
*/
|
|
_onLoadingError(error) {
|
|
// 에러 UI 표시
|
|
const container = document.querySelector('.analysis-container');
|
|
if (container) {
|
|
const errorHTML = `
|
|
<div style="
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 50vh;
|
|
text-align: center;
|
|
color: #ef4444;
|
|
">
|
|
<div style="font-size: 4rem; margin-bottom: 1rem;">⚠️</div>
|
|
<h2 style="margin-bottom: 1rem;">모듈 로딩 실패</h2>
|
|
<p style="margin-bottom: 2rem; color: #666;">
|
|
작업 분석 시스템을 로드하는 중 오류가 발생했습니다.<br>
|
|
페이지를 새로고침하거나 관리자에게 문의하세요.
|
|
</p>
|
|
<button onclick="location.reload()" style="
|
|
padding: 0.75rem 1.5rem;
|
|
background: #3b82f6;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 0.5rem;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
">
|
|
페이지 새로고침
|
|
</button>
|
|
<details style="margin-top: 2rem; text-align: left; max-width: 600px;">
|
|
<summary style="cursor: pointer; color: #666;">기술적 세부사항</summary>
|
|
<pre style="
|
|
background: #f8f9fa;
|
|
padding: 1rem;
|
|
border-radius: 0.5rem;
|
|
margin-top: 1rem;
|
|
overflow-x: auto;
|
|
font-size: 0.875rem;
|
|
">${error.message}</pre>
|
|
</details>
|
|
</div>
|
|
`;
|
|
|
|
container.innerHTML = errorHTML;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 로딩 상태 확인
|
|
* @returns {Object} 로딩 상태 정보
|
|
*/
|
|
getLoadingStatus() {
|
|
const total = this.modules.length;
|
|
const loaded = this.modules.filter(m => m.loaded).length;
|
|
|
|
return {
|
|
total,
|
|
loaded,
|
|
percentage: Math.round((loaded / total) * 100),
|
|
isComplete: loaded === total,
|
|
modules: this.modules.map(m => ({
|
|
name: m.name,
|
|
loaded: m.loaded
|
|
}))
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 특정 모듈 로딩 상태 확인
|
|
* @param {string} moduleName - 모듈명
|
|
* @returns {boolean} 로딩 완료 여부
|
|
*/
|
|
isModuleLoaded(moduleName) {
|
|
const module = this.modules.find(m => m.name === moduleName);
|
|
return module ? module.loaded : false;
|
|
}
|
|
}
|
|
|
|
// 전역 인스턴스 생성
|
|
window.WorkAnalysisModuleLoader = new WorkAnalysisModuleLoader();
|
|
|
|
// 자동 로딩 시작 (DOM이 준비되면)
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
window.WorkAnalysisModuleLoader.loadAll();
|
|
});
|
|
} else {
|
|
// DOM이 이미 준비된 경우 즉시 로딩
|
|
window.WorkAnalysisModuleLoader.loadAll();
|
|
}
|
|
|
|
// Export는 브라우저 환경에서 제거됨
|