feat: 다수 기능 개선 - 순찰, 출근, 작업분석, 모바일 UI 등
- 순찰/점검 기능 개선 (zone-detail 페이지 추가) - 출근/근태 시스템 개선 (연차 조회, 근무현황) - 작업분석 대분류 그룹화 및 마이그레이션 스크립트 - 모바일 네비게이션 UI 추가 - NAS 배포 도구 및 문서 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
267
deploy/tkfb-package/web-ui/js/work-analysis/module-loader.js
Normal file
267
deploy/tkfb-package/web-ui/js/work-analysis/module-loader.js
Normal file
@@ -0,0 +1,267 @@
|
||||
/**
|
||||
* 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는 브라우저 환경에서 제거됨
|
||||
Reference in New Issue
Block a user