// /js/project-analysis-ui.js const DOM = { // 기간 설정 startDate: document.getElementById('startDate'), endDate: document.getElementById('endDate'), // 카드 및 필터 analysisCard: document.getElementById('analysisCard'), summaryCards: document.getElementById('summaryCards'), projectFilter: document.getElementById('projectFilter'), workerFilter: document.getElementById('workerFilter'), taskFilter: document.getElementById('taskFilter'), // 탭 tabButtons: document.querySelectorAll('.tab-button'), tabContents: document.querySelectorAll('.analysis-content'), // 테이블 본문 projectTableBody: document.getElementById('projectTableBody'), workerTableBody: document.getElementById('workerTableBody'), taskTableBody: document.getElementById('taskTableBody'), detailTableBody: document.getElementById('detailTableBody'), }; /** * 날짜 input 값을 YYYY-MM-DD 형식의 문자열로 반환 * @param {Date} date - 날짜 객체 * @returns {string} - 포맷된 날짜 문자열 */ const formatDate = (date) => date.toISOString().split('T')[0]; /** * UI상의 날짜 선택기를 기본값(이번 달)으로 설정합니다. */ export function setDefaultDates() { const now = new Date(); const firstDay = new Date(now.getFullYear(), now.getMonth(), 1); const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0); DOM.startDate.value = formatDate(firstDay); DOM.endDate.value = formatDate(lastDay); } /** * 분석 실행 전후의 UI 상태를 관리합니다 (로딩 표시 등) * @param {'loading' | 'data' | 'no-data' | 'error'} state - UI 상태 */ export function setUIState(state) { const projectCols = 5; const detailCols = 8; const messages = { loading: '📊 데이터 분석 중...', 'no-data': '해당 기간에 분석할 데이터가 없습니다.', error: '오류가 발생했습니다. 다시 시도해주세요.', }; if (state === 'data') { DOM.analysisCard.style.display = 'block'; } else { const message = messages[state]; const html = `${message}`; const detailHtml = `${message}`; DOM.projectTableBody.innerHTML = html; DOM.workerTableBody.innerHTML = html; DOM.taskTableBody.innerHTML = html; DOM.detailTableBody.innerHTML = detailHtml; DOM.summaryCards.innerHTML = ''; DOM.analysisCard.style.display = 'block'; } } /** * 마스터 데이터를 기반으로 필터 옵션을 채웁니다. * @param {{workers: Array, projects: Array, tasks: Array}} masterData - 마스터 데이터 */ export function updateFilterOptions(masterData) { const createOptions = (items, key, value) => { let html = ''; items.forEach(item => { html += ``; }); return html; }; DOM.projectFilter.innerHTML = createOptions(masterData.projects, 'project_id', 'project_name'); DOM.workerFilter.innerHTML = createOptions(masterData.workers, 'worker_id', 'worker_name'); DOM.taskFilter.innerHTML = createOptions(masterData.tasks, 'task_id', 'category'); } /** * 요약 카드 데이터를 렌더링합니다. * @param {object} summary - 요약 데이터 */ export function renderSummary(summary) { DOM.summaryCards.innerHTML = `

총 투입 시간

${(summary.totalHours || 0).toFixed(1)}h

참여 프로젝트

${summary.totalProjects || 0}개

참여 인원

${summary.totalWorkers || 0}명

작업 분류

${summary.totalTasks || 0}개
`; } /** * 집계된 데이터를 받아 테이블을 렌더링하는 범용 함수 * @param {HTMLElement} tableBodyEl - 렌더링할 테이블의 tbody 요소 * @param {Array} data - 집계된 데이터 배열 * @param {function} rowRenderer - 각 행을 렌더링하는 함수 */ function renderTable(tableBodyEl, data, rowRenderer) { if (!data || data.length === 0) { tableBodyEl.innerHTML = '데이터가 없습니다'; return; } tableBodyEl.innerHTML = data.map(rowRenderer).join(''); } /** * 집계된 데이터를 기반으로 모든 분석 테이블을 렌더링합니다. * @param {object} analysis - 프로젝트/작업자/작업별 집계 데이터 */ export function renderAnalysisTables(analysis) { renderTable(DOM.projectTableBody, analysis.byProject, (p, i) => ` ${i + 1}${p.name}${p.hours}h ${p.percentage}%${p.participants}명`); renderTable(DOM.workerTableBody, analysis.byWorker, (w, i) => ` ${i + 1}${w.name}${w.hours}h ${w.percentage}%${w.participants}개`); renderTable(DOM.taskTableBody, analysis.byTask, (t, i) => ` ${i + 1}${t.name}${t.hours}h ${t.percentage}%${t.participants}명`); } /** * 상세 내역 테이블을 렌더링합니다. * @param {Array} detailData - 필터링된 상세 데이터 */ export function renderDetailTable(detailData) { if (!detailData || detailData.length === 0) { DOM.detailTableBody.innerHTML = '데이터가 없습니다'; return; } DOM.detailTableBody.innerHTML = detailData.map((item, index) => ` ${index + 1}${formatDate(new Date(item.date))} ${item.project_name} ${item.worker_name}${item.task_category} ${item.work_details || '정상근무'} ${item.work_hours}h ${(item.memo || '-').substring(0, 20)}` ).join(''); } /** * 탭 UI를 제어합니다. * @param {string} tabName - 활성화할 탭의 이름 */ export function switchTab(tabName) { DOM.tabButtons.forEach(btn => btn.classList.toggle('active', btn.dataset.tab === tabName)); DOM.tabContents.forEach(content => content.classList.toggle('active', content.id === `${tabName}Tab`)); } /** * 사용자로부터 현재 필터 값을 가져옵니다. * @returns {{project: string, worker: string, task: string}} */ export function getCurrentFilters() { return { project: DOM.projectFilter.value, worker: DOM.workerFilter.value, task: DOM.taskFilter.value, }; }