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.
This commit is contained in:
Hyungi Ahn
2025-12-19 10:46:29 +09:00
parent bc5df77595
commit 8a8307edfc
5 changed files with 249 additions and 230 deletions

View File

@@ -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 = '<tr><td colspan="9">불러오는 중...</td></tr>';
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);

View File

@@ -0,0 +1,131 @@
// web-ui/js/modules/calendar/CalendarAPI.js
/**
* 캘린더와 관련된 모든 API 호출을 관리하는 전역 객체입니다.
*/
(function(window) {
'use strict';
const CalendarAPI = {};
/**
* 활성화된 모든 작업자 목록을 가져옵니다.
* @returns {Promise<Array>} 작업자 객체 배열
*/
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<object>}
*/
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<object>}
*/
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<object>}
*/
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<object>}
*/
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);

View File

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

View File

@@ -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 = '<div class="empty-state">등록된 작업자가 없습니다.</div>';
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 ? `
<button class="btn-delete-worker-work" onclick="event.stopPropagation(); deleteWorkerDayWork(${worker.workerId}, '${currentModalDate}', '${worker.workerName}')" title="이 작업자의 해당 날짜 작업 전체 삭제">
<button class="btn-delete-worker-work" onclick="event.stopPropagation(); deleteWorkerDayWork(${worker.workerId}, '${CalendarState.currentModalDate}', '${worker.workerName}')" title="이 작업자의 해당 날짜 작업 전체 삭제">
🗑️
</button>
` : '';
return `
<div class="worker-card ${statusClass}" onclick="openWorkerModal(${worker.workerId}, '${currentModalDate}')">
<div class="worker-card ${statusClass}" onclick="openWorkerModal(${worker.workerId}, '${CalendarState.currentModalDate}')">
<div class="worker-avatar">
<div class="avatar-circle">
<span class="avatar-text">${initial}</span>
@@ -636,7 +506,7 @@ async function renderModalDataFromSummary(workers, summary) {
</div>
<div class="worker-actions">
${deleteBtn}
<button class="btn-work-entry" onclick="event.stopPropagation(); openWorkerModal(${worker.workerId}, '${currentModalDate}')" title="작업입력">
<button class="btn-work-entry" onclick="event.stopPropagation(); openWorkerModal(${worker.workerId}, '${CalendarState.currentModalDate}')" title="작업입력">
작업입력
</button>
</div>
@@ -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 = `
<div class="work-item" data-work-id="${work.id}">
<div class="work-item-header">
@@ -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);
}
}

View File

@@ -337,6 +337,8 @@
<script src="/js/api-config.js?v=13"></script>
<script src="/js/auth-check.js?v=13"></script>
<script src="/js/load-navbar.js?v=4"></script>
<script src="/js/modules/calendar/CalendarState.js?v=1"></script>
<script src="/js/modules/calendar/CalendarAPI.js?v=1"></script>
<script src="/js/work-report-calendar.js?v=41"></script>
</body>
</html>