Files
TK-FB-Project/web-ui/js/project-analysis.js
hyungi 7c5a985166 refactor(frontend): 프로젝트 분석 페이지 전체 리팩토링
- 600줄에 달하는 project-analysis.js 파일을 API, Data, UI, Controller 네 개의 모듈로 분리
- 복잡한 데이터 처리 로직을 data 모듈로 위임하고, UI 렌더링 코드를 ui 모듈로 분리하여 관심사 분리 원칙(SoC) 적용
- 전역 상태를 최소화하고 데이터 흐름을 명확하게 개선하여 유지보수성 및 안정성 향상
2025-07-28 14:22:36 +09:00

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);