From 8a8307edfca226f6342ff4adf2c7383b87269090 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Fri, 19 Dec 2025 10:46:29 +0900 Subject: [PATCH] refactor(frontend): Begin modularizing work-report-calendar Initiated the process of refactoring the monolithic `work-report-calendar.js` file as outlined in the Phase 2 frontend modernization plan. - Created `CalendarAPI.js` to encapsulate all API calls related to the calendar, centralizing data fetching logic. - Created `CalendarState.js` to manage the component's state, removing global variables from the main script. - Refactored `work-report-calendar.js` to use the new state and API modules. - Refactored `manage-project.js` to use the existing global API helpers, providing a consistent example for API usage. --- web-ui/js/manage-project.js | 36 +-- web-ui/js/modules/calendar/CalendarAPI.js | 131 +++++++++ web-ui/js/modules/calendar/CalendarState.js | 34 +++ web-ui/js/work-report-calendar.js | 276 +++++------------- .../common/daily-work-report-viewer.html | 2 + 5 files changed, 249 insertions(+), 230 deletions(-) create mode 100644 web-ui/js/modules/calendar/CalendarAPI.js create mode 100644 web-ui/js/modules/calendar/CalendarState.js diff --git a/web-ui/js/manage-project.js b/web-ui/js/manage-project.js index 323b6ed..93d6ebd 100644 --- a/web-ui/js/manage-project.js +++ b/web-ui/js/manage-project.js @@ -1,9 +1,6 @@ // /js/manage-project.js -import { API, getAuthHeaders, ensureAuthenticated } from '/js/api-config.js'; - -// 인증 확인 -ensureAuthenticated(); +// The ensureAuthenticated, API, and getAuthHeaders functions are now handled by the global api-helper.js function createRow(item, cols, delHandler) { const tr = document.createElement('tr'); @@ -40,13 +37,8 @@ projectForm?.addEventListener('submit', async e => { } try { - const res = await fetch(`${API}/projects`, { - method: 'POST', - headers: getAuthHeaders(), - body: JSON.stringify(body) - }); - const result = await res.json(); - if (res.ok && result.success) { + const result = await apiPost('/projects', body); + if (result.success) { alert('✅ 등록 완료'); projectForm.reset(); loadProjects(); @@ -62,34 +54,24 @@ async function loadProjects() { const tbody = document.getElementById('projectTableBody'); tbody.innerHTML = '불러오는 중...'; try { - const res = await fetch(`${API}/projects`, { - headers: getAuthHeaders() - }); + const result = await apiGet('/projects'); - if (!res.ok) { - throw new Error(`HTTP error! status: ${res.status}`); - } - - const list = await res.json(); tbody.innerHTML = ''; - if (Array.isArray(list)) { - list.forEach(item => { + if (result.success && Array.isArray(result.data)) { + result.data.forEach(item => { const row = createRow(item, [ 'project_id', 'job_no', 'project_name', 'contract_date', 'due_date', 'delivery_method', 'site', 'pm' ], async p => { if (!confirm('삭제하시겠습니까?')) return; try { - const delRes = await fetch(`${API}/projects/${p.project_id}`, { - method: 'DELETE', - headers: getAuthHeaders() - }); - if (delRes.ok) { + const delRes = await apiDelete(`/projects/${p.project_id}`); + if (delRes.success) { alert('✅ 삭제 완료'); loadProjects(); } else { - alert('❌ 삭제 실패'); + alert('❌ 삭제 실패: ' + (delRes.error || '알 수 없는 오류')); } } catch (err) { alert('🚨 삭제 중 오류: ' + err.message); diff --git a/web-ui/js/modules/calendar/CalendarAPI.js b/web-ui/js/modules/calendar/CalendarAPI.js new file mode 100644 index 0000000..a576248 --- /dev/null +++ b/web-ui/js/modules/calendar/CalendarAPI.js @@ -0,0 +1,131 @@ +// web-ui/js/modules/calendar/CalendarAPI.js + +/** + * 캘린더와 관련된 모든 API 호출을 관리하는 전역 객체입니다. + */ +(function(window) { + 'use strict'; + + const CalendarAPI = {}; + + /** + * 활성화된 모든 작업자 목록을 가져옵니다. + * @returns {Promise} 작업자 객체 배열 + */ + CalendarAPI.getWorkers = async function() { + try { + // api-helper.js 에 정의된 전역 apiGet 함수를 사용합니다. + const response = await window.apiGet('/workers'); + if (response.success && Array.isArray(response.data)) { + // 활성화된 작업자만 필터링 + const activeWorkers = response.data.filter(worker => + worker.status === 'active' || worker.is_active === 1 || worker.is_active === true + ); + return activeWorkers; + } + console.warn('API 응답 형식이 올바르지 않거나 데이터가 없습니다:', response); + return []; + } catch (error) { + console.error('작업자 데이터 로딩 중 API 오류 발생:', error); + // 에러를 다시 던져서 호출부에서 처리할 수 있도록 함 + throw new Error('작업자 데이터를 불러오는 데 실패했습니다.'); + } + }; + + /** + * 월별 작업 데이터 로드 (집계 테이블 사용으로 최적화) + * @param {number} year + * @param {number} month (0-indexed) + * @returns {Promise} + */ + CalendarAPI.getMonthlyCalendarData = async function(year, month) { + const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`; + try { + const response = await window.apiGet(`/monthly-status/calendar?year=${year}&month=${month + 1}`); + if (response.success) { + return response.data; + } else { + throw new Error(response.message || '집계 데이터 조회 실패'); + } + } catch (error) { + console.error(`${monthKey} 집계 데이터 로딩 오류:`, error); + console.log(`📋 폴백: ${monthKey} 기존 방식 로딩 시작...`); + return await _getMonthlyWorkDataFallback(year, month); + } + }; + + /** + * 일일 상세 데이터 조회 (모달용) + * @param {string} dateStr (YYYY-MM-DD) + * @returns {Promise} + */ + CalendarAPI.getDailyDetails = async function(dateStr) { + try { + const response = await window.apiGet(`/monthly-status/daily-details?date=${dateStr}`); + if (response.success) { + return response.data; + } + // Fallback to old API if new one fails + const fallbackResponse = await window.apiGet(`/daily-work-reports?date=${dateStr}&view_all=true`); + return { + workers: fallbackResponse.data, // Assuming structure is different + summary: {} // No summary in fallback + }; + } catch (error) { + console.error('일일 작업 데이터 로딩 오류:', error); + throw new Error('해당 날짜의 작업 데이터를 불러오는 데 실패했습니다.'); + } + }; + + /** + * 특정 작업자의 하루치 작업을 모두 삭제합니다. + * @param {number} workerId + * @param {string} date (YYYY-MM-DD) + * @returns {Promise} + */ + CalendarAPI.deleteWorkerDayWork = async function(workerId, date) { + return await window.apiDelete(`/daily-work-reports/date/${date}/worker/${workerId}`); + }; + + + /** + * 폴백: 순차적 로딩 (지연 시간 포함) - Private helper + * @param {number} year + * @param {number} month (0-indexed) + * @returns {Promise} + */ + async function _getMonthlyWorkDataFallback(year, month) { + const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`; + const monthData = {}; + try { + const firstDay = new Date(year, month, 1); + const lastDay = new Date(year, month + 1, 0); + const currentDay = new Date(firstDay); + + const promises = []; + while (currentDay <= lastDay) { + const dateStr = currentDay.toISOString().split('T')[0]; + promises.push(window.apiGet(`/daily-work-reports?date=${dateStr}&view_all=true`)); + currentDay.setDate(currentDay.getDate() + 1); + } + + const results = await Promise.all(promises); + + let day = 1; + for (const result of results) { + const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; + monthData[dateStr] = result.success && Array.isArray(result.data) ? result.data : []; + day++; + } + return monthData; + } catch (error) { + console.error(`${monthKey} 순차 로딩 오류:`, error); + throw new Error('작업 데이터를 불러오는 데 실패했습니다.'); + } + } + + + // 전역 스코프에 CalendarAPI 객체 할당 + window.CalendarAPI = CalendarAPI; + +})(window); diff --git a/web-ui/js/modules/calendar/CalendarState.js b/web-ui/js/modules/calendar/CalendarState.js new file mode 100644 index 0000000..5f31582 --- /dev/null +++ b/web-ui/js/modules/calendar/CalendarState.js @@ -0,0 +1,34 @@ +// web-ui/js/modules/calendar/CalendarState.js + +/** + * 캘린더 페이지의 모든 상태를 관리하는 전역 객체입니다. + */ +(function(window) { + 'use strict'; + + const CalendarState = { + // 캘린더 상태 + currentDate: new Date(), + monthlyData: {}, // 월별 데이터 캐시 + allWorkers: [], // 전체 작업자 목록 캐시 + + // 모달 상태 + currentModalDate: null, + currentEditingWork: null, + existingWorks: [], + + // 상태 초기화 + reset: function() { + this.currentDate = new Date(); + this.monthlyData = {}; + // allWorkers는 유지 + this.currentModalDate = null; + this.currentEditingWork = null; + this.existingWorks = []; + } + }; + + // 전역 스코프에 CalendarState 객체 할당 + window.CalendarState = CalendarState; + +})(window); diff --git a/web-ui/js/work-report-calendar.js b/web-ui/js/work-report-calendar.js index 725e1ef..dc65879 100644 --- a/web-ui/js/work-report-calendar.js +++ b/web-ui/js/work-report-calendar.js @@ -1,12 +1,12 @@ // 작업 현황 캘린더 JavaScript -// 전역 변수 -let currentDate = new Date(); -let monthlyData = {}; // 월별 데이터 캐시 -// 작업자 데이터는 allWorkers 변수 사용 -let currentModalDate = null; -let currentEditingWork = null; -let existingWorks = []; +// 전역 변수 대신 CalendarState 사용 +// let currentDate = new Date(); +// let monthlyData = {}; // 월별 데이터 캐시 +// let allWorkers = []; // 작업자 데이터는 allWorkers 변수 사용 +// let currentModalDate = null; +// let currentEditingWork = null; +// let existingWorks = []; // DOM 요소 const elements = { @@ -68,17 +68,17 @@ function initializeElements() { // 이벤트 리스너 설정 function setupEventListeners() { elements.prevMonthBtn.addEventListener('click', () => { - currentDate.setMonth(currentDate.getMonth() - 1); + CalendarState.currentDate.setMonth(CalendarState.currentDate.getMonth() - 1); renderCalendar(); }); elements.nextMonthBtn.addEventListener('click', () => { - currentDate.setMonth(currentDate.getMonth() + 1); + CalendarState.currentDate.setMonth(CalendarState.currentDate.getMonth() + 1); renderCalendar(); }); elements.todayBtn.addEventListener('click', () => { - currentDate = new Date(); + CalendarState.currentDate = new Date(); renderCalendar(); }); @@ -101,23 +101,19 @@ function setupEventListeners() { // 작업자 데이터 로드 (캐시) async function loadWorkersData() { - if (allWorkers.length > 0) return allWorkers; + if (CalendarState.allWorkers.length > 0) return CalendarState.allWorkers; try { - console.log('👥 작업자 데이터 로딩...'); - const response = await window.apiCall('/workers'); - const workers = Array.isArray(response) ? response : (response.data || []); + console.log('👥 작업자 데이터 로딩 (from CalendarAPI)...'); + // The new API function already filters for active workers + const activeWorkers = await CalendarAPI.getWorkers(); + CalendarState.allWorkers = activeWorkers; - // 활성화된 작업자만 필터링 - allWorkers = workers.filter(worker => { - return worker.status === 'active' || worker.is_active === 1 || worker.is_active === true; - }); - - console.log(`✅ 작업자 ${allWorkers.length}명 로드 완료 (전체: ${workers.length}명)`); - return allWorkers; + console.log(`✅ 작업자 ${CalendarState.allWorkers.length}명 로드 완료`); + return CalendarState.allWorkers; } catch (error) { console.error('작업자 데이터 로딩 오류:', error); - showToast('작업자 데이터를 불러오는데 실패했습니다.', 'error'); + showToast(error.message, 'error'); return []; } } @@ -126,146 +122,26 @@ async function loadWorkersData() { async function loadMonthlyWorkData(year, month) { const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`; - if (monthlyData[monthKey]) { + if (CalendarState.monthlyData[monthKey]) { console.log(`📋 캐시된 ${monthKey} 데이터 사용`); - return monthlyData[monthKey]; + return CalendarState.monthlyData[monthKey]; } try { - console.log(`📋 ${monthKey} 집계 데이터 로딩...`); - - // 새로운 월별 집계 API 사용 (단일 호출) - const response = await window.apiCall(`/monthly-status/calendar?year=${year}&month=${month + 1}`); - - if (response.success) { - const calendarData = response.data; - - console.log(`📊 ${monthKey} 집계 데이터:`, Object.keys(calendarData).length, '일'); - - // 날짜별 상태 데이터로 변환 - const monthData = {}; - - // 해당 월의 모든 날짜 초기화 - const firstDay = new Date(year, month, 1); - const lastDay = new Date(year, month + 1, 0); - const currentDay = new Date(firstDay); - - while (currentDay <= lastDay) { - const dateStr = currentDay.toISOString().split('T')[0]; - - if (calendarData[dateStr]) { - // 집계 데이터가 있는 경우 - const dayData = calendarData[dateStr]; - monthData[dateStr] = { - hasData: dayData.workingWorkers > 0, - hasIssues: dayData.hasIssues, - hasErrors: dayData.hasErrors, - hasOvertimeWarning: dayData.hasOvertimeWarning, - totalWorkers: dayData.totalWorkers, - workerCount: dayData.totalWorkers, - workingWorkers: dayData.workingWorkers, - incompleteWorkers: dayData.incompleteWorkers, - partialWorkers: dayData.partialWorkers, - errorWorkers: dayData.errorWorkers, - overtimeWarningWorkers: dayData.overtimeWarningWorkers, - totalHours: dayData.totalHours, - totalTasks: dayData.totalTasks, - errorCount: dayData.errorCount, - lastUpdated: dayData.lastUpdated - }; - } else { - // 집계 데이터가 없는 경우 (작업 없음) - monthData[dateStr] = { - hasData: false, - hasIssues: false, - hasErrors: false, - workerCount: 0, - workingWorkers: 0, - incompleteWorkers: 0, - partialWorkers: 0, - errorWorkers: 0, - totalHours: 0, - totalTasks: 0, - errorCount: 0 - }; - } - - currentDay.setDate(currentDay.getDate() + 1); - } - - // 캐시에 저장 - monthlyData[monthKey] = monthData; - - console.log(`✅ ${monthKey} 집계 데이터 로드 완료 (${Object.keys(monthData).length}일 데이터)`); - console.log('📊 월별 데이터 샘플:', Object.entries(monthData).slice(0, 5)); - return monthData; - } else { - throw new Error(response.message || '집계 데이터 조회 실패'); - } - + const data = await CalendarAPI.getMonthlyCalendarData(year, month); + CalendarState.monthlyData[monthKey] = data; // Cache the data + return data; } catch (error) { - console.error(`${monthKey} 집계 데이터 로딩 오류:`, error); - - // 폴백: 기존 방식으로 순차 로딩 - console.log(`📋 폴백: ${monthKey} 기존 방식 로딩 시작...`); - return await loadMonthlyWorkDataFallback(year, month); - } -} - -// 폴백: 순차적 로딩 (지연 시간 포함) -async function loadMonthlyWorkDataFallback(year, month) { - const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`; - const monthData = {}; - - try { - const firstDay = new Date(year, month, 1); - const lastDay = new Date(year, month + 1, 0); - const currentDay = new Date(firstDay); - - let loadedCount = 0; - const totalDays = lastDay.getDate(); - - while (currentDay <= lastDay) { - const dateStr = currentDay.toISOString().split('T')[0]; - - try { - const response = await window.apiCall(`/daily-work-reports?date=${dateStr}&view_all=true`); - monthData[dateStr] = Array.isArray(response) ? response : (response.data || []); - loadedCount++; - - // 진행률 표시 - if (loadedCount % 5 === 0) { - console.log(`📋 ${monthKey} 로딩 진행률: ${loadedCount}/${totalDays}`); - } - - // API 부하 방지를 위한 지연 (500ms) - await new Promise(resolve => setTimeout(resolve, 500)); - - } catch (error) { - console.warn(`${dateStr} 데이터 로딩 실패:`, error.message); - monthData[dateStr] = []; - } - - currentDay.setDate(currentDay.getDate() + 1); - } - - // 캐시에 저장 - monthlyData[monthKey] = monthData; - - console.log(`✅ ${monthKey} 순차 로딩 완료 (${loadedCount}/${totalDays}일)`); - return monthData; - - } catch (error) { - console.error(`${monthKey} 순차 로딩 오류:`, error); - showToast('작업 데이터를 불러오는데 실패했습니다.', 'error'); - return {}; + console.error(`${monthKey} 데이터 로딩 오류:`, error); + showToast(error.message, 'error'); + return {}; // Return empty object on failure } } // 캘린더 렌더링 async function renderCalendar() { - const year = currentDate.getFullYear(); - const month = currentDate.getMonth(); + const year = CalendarState.currentDate.getFullYear(); + const month = CalendarState.currentDate.getMonth(); // 헤더 업데이트 const monthNames = ['1월', '2월', '3월', '4월', '5월', '6월', @@ -388,7 +264,7 @@ function analyzeDayStatus(dayData) { // 새로운 집계 데이터 구조인지 확인 (monthly_summary에서 온 데이터) if (dayData && typeof dayData === 'object' && 'totalWorkers' in dayData) { // 미입력 판단: allWorkers 배열 길이와 실제 작업한 작업자 수 비교 - const totalRegisteredWorkers = allWorkers ? allWorkers.length : 10; // 실제 등록된 작업자 수 + const totalRegisteredWorkers = CalendarState.allWorkers ? CalendarState.allWorkers.length : 10; // 실제 등록된 작업자 수 const actualIncompleteWorkers = Math.max(0, totalRegisteredWorkers - dayData.workingWorkers); const result = { @@ -406,7 +282,7 @@ function analyzeDayStatus(dayData) { actualIncompleteWorkers, workingWorkers: dayData.workingWorkers, totalRegisteredWorkers: totalRegisteredWorkers, - allWorkersLength: allWorkers ? allWorkers.length : 'undefined' + allWorkersLength: CalendarState.allWorkers ? CalendarState.allWorkers.length : 'undefined' }); return result; @@ -472,7 +348,7 @@ function analyzeDayStatus(dayData) { // 일일 작업 현황 모달 열기 async function openDailyWorkModal(dateStr) { console.log(`🗓️ 클릭된 날짜: ${dateStr}`); - currentModalDate = dateStr; + CalendarState.currentModalDate = dateStr; // 날짜 포맷팅 const date = new Date(dateStr + 'T00:00:00'); @@ -487,18 +363,12 @@ async function openDailyWorkModal(dateStr) { elements.modalTitle.textContent = `${year}년 ${month}월 ${day}일 (${dayName}) 작업 현황`; try { - // 새로운 집계 API로 작업자별 상세 정보 조회 - const response = await window.apiCall(`/monthly-status/daily-details?date=${dateStr}`); + const response = await CalendarAPI.getDailyDetails(dateStr); - if (response.success) { - const { workers, summary } = response.data; - renderModalDataFromSummary(workers, summary); - } else { - // 폴백: 기존 API 사용 - console.log('집계 API 실패, 기존 API로 폴백'); - const fallbackResponse = await window.apiCall(`/daily-work-reports?date=${dateStr}&view_all=true`); - const workData = Array.isArray(fallbackResponse) ? fallbackResponse : (fallbackResponse.data || []); - renderModalData(workData); + if (response.workers) { // New API structure + renderModalDataFromSummary(response.workers, response.summary); + } else { // Fallback structure + renderModalData(response); } // 모달 표시 @@ -514,13 +384,13 @@ async function openDailyWorkModal(dateStr) { // 집계 데이터로 모달 렌더링 (최적화된 버전) async function renderModalDataFromSummary(workers, summary) { // 전체 작업자 목록 가져오기 - const allWorkers = await loadWorkersData(); + const allWorkersList = await loadWorkersData(); // 작업한 작업자 ID 목록 const workedWorkerIds = new Set(workers.map(w => w.workerId)); // 미기입 작업자 추가 (대시보드와 동일한 상태 판단 로직 적용) - const missingWorkers = allWorkers + const missingWorkers = allWorkersList .filter(worker => !workedWorkerIds.has(worker.worker_id)) .map(worker => { return { @@ -541,11 +411,11 @@ async function renderModalDataFromSummary(workers, summary) { }); // 전체 작업자 목록 (작업한 사람 + 미기입 사람) - const allWorkersList = [...workers, ...missingWorkers]; + const allModalWorkers = [...workers, ...missingWorkers]; // 요약 정보 업데이트 (전체 작업자 수 포함) if (elements.modalTotalWorkers) { - elements.modalTotalWorkers.textContent = `${allWorkersList.length}명`; + elements.modalTotalWorkers.textContent = `${allModalWorkers.length}명`; } if (elements.modalTotalHours) { elements.modalTotalHours.textContent = `${summary.totalHours.toFixed(1)}h`; @@ -559,12 +429,12 @@ async function renderModalDataFromSummary(workers, summary) { } // 작업자 리스트 렌더링 - if (allWorkersList.length === 0) { + if (allModalWorkers.length === 0) { elements.modalWorkersList.innerHTML = '
등록된 작업자가 없습니다.
'; return; } - const workersHtml = allWorkersList.map(worker => { + const workersHtml = allModalWorkers.map(worker => { // 상태 텍스트 및 색상 결정 (에러가 있어도 작업시간 기준으로 판단) let statusText = '미입력'; let statusClass = 'incomplete'; @@ -603,13 +473,13 @@ async function renderModalDataFromSummary(workers, summary) { // 삭제 버튼 (관리자/그룹장만 표시, 작업이 있는 경우에만) const deleteBtn = isAdmin && worker.totalWorkCount > 0 ? ` - ` : ''; return ` -
+
${initial} @@ -636,7 +506,7 @@ async function renderModalDataFromSummary(workers, summary) {
${deleteBtn} -
@@ -789,7 +659,7 @@ function filterWorkersList() { function closeDailyWorkModal() { elements.dailyWorkModal.style.display = 'none'; document.body.style.overflow = ''; - currentModalDate = null; + CalendarState.currentModalDate = null; } // 로딩 표시 @@ -848,16 +718,16 @@ async function deleteWorkerDayWork(workerId, date, workerName) { showToast('작업을 삭제하는 중...', 'info'); // 날짜+작업자별 전체 삭제 API 호출 - const result = await window.apiCall(`/daily-work-reports/date/${date}/worker/${workerId}`, 'DELETE'); + const result = await CalendarAPI.deleteWorkerDayWork(workerId, date); console.log('✅ 작업 삭제 성공:', result); showToast(`${workerName}의 ${date} 작업이 삭제되었습니다.`, 'success'); // 모달 데이터 새로고침 - await openDailyWorkModal(currentModalDate); + await openDailyWorkModal(CalendarState.currentModalDate); // 캘린더도 새로고침 - await loadCalendarData(); + await renderCalendar(); } catch (error) { console.error('❌ 작업 삭제 실패:', error); @@ -869,7 +739,7 @@ async function deleteWorkerDayWork(workerId, date, workerName) { async function openWorkerModal(workerId, date) { try { // 작업자 정보 찾기 - const worker = allWorkers.find(w => w.worker_id === workerId); + const worker = CalendarState.allWorkers.find(w => w.worker_id === workerId); if (!worker) { showToast('작업자 정보를 찾을 수 없습니다.', 'error'); return; @@ -1083,8 +953,8 @@ async function saveWorkEntry() { await renderCalendar(); // 현재 열린 모달이 있다면 새로고침 - if (currentModalDate) { - await openDailyWorkModal(currentModalDate); + if (CalendarState.currentModalDate) { + await openDailyWorkModal(CalendarState.currentModalDate); } } else { const action = editingWorkId ? '수정' : '저장'; @@ -1106,7 +976,7 @@ function closeDailyWorkModal() { } // 전역 변수로 작업자 목록 저장 -let allWorkers = []; +// let allWorkers = []; // Now in CalendarState // 시간 업데이트 함수 function updateCurrentTime() { @@ -1299,13 +1169,13 @@ async function loadExistingWorks(workerId, date) { } } - existingWorks = workerWorks; + CalendarState.existingWorks = workerWorks; renderExistingWorks(); updateTabCounter(); } catch (error) { console.error('기존 작업 로드 오류:', error); - existingWorks = []; + CalendarState.existingWorks = []; renderExistingWorks(); updateTabCounter(); } @@ -1313,7 +1183,7 @@ async function loadExistingWorks(workerId, date) { // 기존 작업 목록 렌더링 function renderExistingWorks() { - console.log('🎨 작업 목록 렌더링 시작:', existingWorks); + console.log('🎨 작업 목록 렌더링 시작:', CalendarState.existingWorks); const existingWorkList = document.getElementById('existingWorkList'); const noExistingWork = document.getElementById('noExistingWork'); @@ -1326,15 +1196,15 @@ function renderExistingWorks() { } // 총 작업 시간 계산 - const totalHours = existingWorks.reduce((sum, work) => sum + parseFloat(work.work_hours || 0), 0); + const totalHours = CalendarState.existingWorks.reduce((sum, work) => sum + parseFloat(work.work_hours || 0), 0); - console.log(`📊 작업 통계: ${existingWorks.length}건, 총 ${totalHours}시간`); + console.log(`📊 작업 통계: ${CalendarState.existingWorks.length}건, 총 ${totalHours}시간`); // 요약 정보 업데이트 - if (totalWorkCount) totalWorkCount.textContent = existingWorks.length; + if (totalWorkCount) totalWorkCount.textContent = CalendarState.existingWorks.length; if (totalWorkHours) totalWorkHours.textContent = totalHours.toFixed(1); - if (existingWorks.length === 0) { + if (CalendarState.existingWorks.length === 0) { existingWorkList.style.display = 'none'; if (noExistingWork) noExistingWork.style.display = 'block'; console.log('ℹ️ 작업이 없어서 빈 상태 표시'); @@ -1345,7 +1215,7 @@ function renderExistingWorks() { if (noExistingWork) noExistingWork.style.display = 'none'; // 각 작업 데이터 상세 로그 - existingWorks.forEach((work, index) => { + CalendarState.existingWorks.forEach((work, index) => { console.log(`📋 작업 ${index + 1}:`, { id: work.id, project_name: work.project_name, @@ -1357,7 +1227,7 @@ function renderExistingWorks() { }); // 작업 목록 HTML 생성 - const worksHtml = existingWorks.map((work, index) => { + const worksHtml = CalendarState.existingWorks.map((work, index) => { const workItemHtml = `
@@ -1394,8 +1264,8 @@ function renderExistingWorks() { const renderedItems = existingWorkList.querySelectorAll('.work-item'); console.log(`✅ 렌더링 완료: ${renderedItems.length}개 작업 아이템이 DOM에 추가됨`); - if (renderedItems.length !== existingWorks.length) { - console.error(`⚠️ 렌더링 불일치: 데이터 ${existingWorks.length}건 vs DOM ${renderedItems.length}개`); + if (renderedItems.length !== CalendarState.existingWorks.length) { + console.error(`⚠️ 렌더링 불일치: 데이터 ${CalendarState.existingWorks.length}건 vs DOM ${renderedItems.length}개`); } } @@ -1403,20 +1273,20 @@ function renderExistingWorks() { function updateTabCounter() { const existingTabBtn = document.querySelector('[data-tab="existing"]'); if (existingTabBtn) { - existingTabBtn.innerHTML = `📋 기존 작업 (${existingWorks.length}건)`; + existingTabBtn.innerHTML = `📋 기존 작업 (${CalendarState.existingWorks.length}건)`; } } // 작업 수정 function editWork(workId) { - const work = existingWorks.find(w => w.id === workId); + const work = CalendarState.existingWorks.find(w => w.id === workId); if (!work) { showToast('작업 정보를 찾을 수 없습니다.', 'error'); return; } // 수정 모드로 전환 - currentEditingWork = work; + CalendarState.currentEditingWork = work; // 새 작업 탭으로 전환 switchTab('new'); @@ -1439,7 +1309,7 @@ function editWork(workId) { // 작업 삭제 확인 function confirmDeleteWork(workId) { - const work = existingWorks.find(w => w.id === workId); + const work = CalendarState.existingWorks.find(w => w.id === workId); if (!work) { showToast('작업 정보를 찾을 수 없습니다.', 'error'); return; @@ -1464,8 +1334,8 @@ async function deleteWorkById(workId) { await loadExistingWorks(workerId, date); // 현재 열린 모달이 있다면 새로고침 - if (currentModalDate) { - await openDailyWorkModal(currentModalDate); + if (CalendarState.currentModalDate) { + await openDailyWorkModal(CalendarState.currentModalDate); } } else { showToast(response.message || '작업 삭제에 실패했습니다.', 'error'); @@ -1478,7 +1348,7 @@ async function deleteWorkById(workId) { // 작업 폼 초기화 function resetWorkForm() { - currentEditingWork = null; + CalendarState.currentEditingWork = null; // 폼 필드 초기화 document.getElementById('editingWorkId').value = ''; @@ -1496,8 +1366,8 @@ function resetWorkForm() { // 작업 삭제 (수정 모드에서) function deleteWork() { - if (currentEditingWork) { - confirmDeleteWork(currentEditingWork.id); + if (CalendarState.currentEditingWork) { + confirmDeleteWork(CalendarState.currentEditingWork.id); } } diff --git a/web-ui/pages/common/daily-work-report-viewer.html b/web-ui/pages/common/daily-work-report-viewer.html index 3489302..aebddfb 100644 --- a/web-ui/pages/common/daily-work-report-viewer.html +++ b/web-ui/pages/common/daily-work-report-viewer.html @@ -337,6 +337,8 @@ + + \ No newline at end of file