- 600줄에 달하는 project-analysis.js 파일을 API, Data, UI, Controller 네 개의 모듈로 분리 - 복잡한 데이터 처리 로직을 data 모듈로 위임하고, UI 렌더링 코드를 ui 모듈로 분리하여 관심사 분리 원칙(SoC) 적용 - 전역 상태를 최소화하고 데이터 흐름을 명확하게 개선하여 유지보수성 및 안정성 향상
128 lines
3.6 KiB
JavaScript
128 lines
3.6 KiB
JavaScript
// /js/project-analysis.js
|
|
import { getMasterData, getWorkReports } from './project-analysis-api.js';
|
|
import { processRawData, applyFilters, getAnalysis } from './project-analysis-data.js';
|
|
import {
|
|
setDefaultDates,
|
|
setUIState,
|
|
updateFilterOptions,
|
|
renderSummary,
|
|
renderAnalysisTables,
|
|
renderDetailTable,
|
|
switchTab,
|
|
getCurrentFilters,
|
|
} from './project-analysis-ui.js';
|
|
|
|
// 애플리케이션 상태 (전역 변수 최소화)
|
|
const state = {
|
|
masterData: null,
|
|
processedData: [],
|
|
filteredData: [],
|
|
};
|
|
|
|
// DOM 요소 참조 (이벤트 리스너 설정용)
|
|
const DOM = {
|
|
startDate: document.getElementById('startDate'),
|
|
endDate: document.getElementById('endDate'),
|
|
analyzeBtn: document.getElementById('analyzeBtn'),
|
|
quickMonthBtn: document.getElementById('quickMonth'),
|
|
quickLastMonthBtn: document.getElementById('quickLastMonth'),
|
|
applyFilterBtn: document.getElementById('applyFilter'),
|
|
tabButtons: document.querySelectorAll('.tab-button'),
|
|
};
|
|
|
|
/**
|
|
* 분석 실행 버튼 클릭 이벤트 핸들러
|
|
*/
|
|
async function handleAnalysis() {
|
|
const startDate = DOM.startDate.value;
|
|
const endDate = DOM.endDate.value;
|
|
|
|
if (!startDate || !endDate || startDate > endDate) {
|
|
alert('올바른 분석 기간을 설정해주세요.');
|
|
return;
|
|
}
|
|
|
|
setUIState('loading');
|
|
try {
|
|
const rawReports = await getWorkReports(startDate, endDate);
|
|
state.processedData = processRawData(rawReports, state.masterData);
|
|
|
|
if (state.processedData.length === 0) {
|
|
setUIState('no-data');
|
|
updateFilterOptions([]);
|
|
return;
|
|
}
|
|
|
|
updateFilterOptions(state.processedData);
|
|
handleFilterChange(); // 필터 적용 및 렌더링
|
|
setUIState('data');
|
|
|
|
} catch (error) {
|
|
console.error('분석 처리 중 오류:', error);
|
|
setUIState('error');
|
|
alert(error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 필터 적용 버튼 클릭 또는 분석 후 자동 실행되는 핸들러
|
|
*/
|
|
function handleFilterChange() {
|
|
const filters = getCurrentFilters();
|
|
state.filteredData = applyFilters(state.processedData, filters);
|
|
|
|
const analysisResult = getAnalysis(state.filteredData);
|
|
|
|
renderSummary(analysisResult.summary);
|
|
renderAnalysisTables(analysisResult);
|
|
renderDetailTable(state.filteredData);
|
|
}
|
|
|
|
/**
|
|
* 빠른 날짜 설정 버튼 핸들러
|
|
* @param {'this' | 'last'} monthType - 이번 달 또는 지난 달
|
|
*/
|
|
function handleQuickDate(monthType) {
|
|
const now = new Date();
|
|
const year = now.getFullYear();
|
|
const month = now.getMonth();
|
|
const firstDay = monthType === 'this' ? new Date(year, month, 1) : new Date(year, month - 1, 1);
|
|
const lastDay = monthType === 'this' ? new Date(year, month + 1, 0) : new Date(year, month, 0);
|
|
|
|
DOM.startDate.value = firstDay.toISOString().split('T')[0];
|
|
DOM.endDate.value = lastDay.toISOString().split('T')[0];
|
|
}
|
|
|
|
/**
|
|
* 이벤트 리스너 설정
|
|
*/
|
|
function setupEventListeners() {
|
|
DOM.analyzeBtn.addEventListener('click', handleAnalysis);
|
|
DOM.applyFilterBtn.addEventListener('click', handleFilterChange);
|
|
DOM.quickMonthBtn.addEventListener('click', () => handleQuickDate('this'));
|
|
DOM.quickLastMonthBtn.addEventListener('click', () => handleQuickDate('last'));
|
|
|
|
DOM.tabButtons.forEach(btn => {
|
|
btn.addEventListener('click', () => switchTab(btn.dataset.tab));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 페이지 초기화 함수
|
|
*/
|
|
async function initialize() {
|
|
setDefaultDates();
|
|
setupEventListeners();
|
|
|
|
try {
|
|
state.masterData = await getMasterData();
|
|
// 페이지 로드 시 바로 분석 실행
|
|
await handleAnalysis();
|
|
} catch (error) {
|
|
alert(error.message);
|
|
setUIState('error');
|
|
}
|
|
}
|
|
|
|
// 초기화 실행
|
|
document.addEventListener('DOMContentLoaded', initialize); |