// daily-work-report.js - 브라우저 호환 버전 // ================================================================= // 🌐 API 설정 (window 객체에서 가져오기) // ================================================================= // API 설정은 api-config.js에서 window 객체에 설정됨 // 전역 변수 let workTypes = []; let workStatusTypes = []; let errorTypes = []; let workers = []; let projects = []; let selectedWorkers = new Set(); let workEntryCounter = 0; let currentStep = 1; let editingWorkId = null; // 수정 중인 작업 ID // 한국 시간 기준 오늘 날짜 가져오기 function getKoreaToday() { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, '0'); const day = String(today.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } // 현재 로그인한 사용자 정보 가져오기 function getCurrentUser() { try { const token = localStorage.getItem('token'); if (!token) return null; const payloadBase64 = token.split('.')[1]; if (payloadBase64) { const payload = JSON.parse(atob(payloadBase64)); console.log('토큰에서 추출한 사용자 정보:', payload); return payload; } } catch (error) { console.log('토큰에서 사용자 정보 추출 실패:', error); } try { const userInfo = localStorage.getItem('user') || localStorage.getItem('userInfo') || localStorage.getItem('currentUser'); if (userInfo) { const parsed = JSON.parse(userInfo); console.log('localStorage에서 가져온 사용자 정보:', parsed); return parsed; } } catch (error) { console.log('localStorage에서 사용자 정보 가져오기 실패:', error); } return null; } // 메시지 표시 function showMessage(message, type = 'info') { const container = document.getElementById('message-container'); container.innerHTML = `
${message}
`; if (type === 'success') { setTimeout(() => { hideMessage(); }, 5000); } } function hideMessage() { document.getElementById('message-container').innerHTML = ''; } // 저장 결과 모달 표시 function showSaveResultModal(type, title, message, details = null) { const modal = document.getElementById('saveResultModal'); const titleElement = document.getElementById('resultModalTitle'); const contentElement = document.getElementById('resultModalContent'); // 아이콘 설정 let icon = ''; switch (type) { case 'success': icon = '✅'; break; case 'error': icon = '❌'; break; case 'warning': icon = '⚠️'; break; default: icon = 'ℹ️'; } // 모달 내용 구성 let content = `
${icon}

${title}

${message}

`; // 상세 정보가 있으면 추가 if (details && details.length > 0) { content += `

상세 정보:

`; } titleElement.textContent = '저장 결과'; contentElement.innerHTML = content; modal.style.display = 'flex'; // ESC 키로 닫기 document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeSaveResultModal(); } }); // 배경 클릭으로 닫기 modal.addEventListener('click', function(e) { if (e.target === modal) { closeSaveResultModal(); } }); } // 저장 결과 모달 닫기 function closeSaveResultModal() { const modal = document.getElementById('saveResultModal'); modal.style.display = 'none'; // 이벤트 리스너 제거 document.removeEventListener('keydown', closeSaveResultModal); } // 단계 이동 function goToStep(stepNumber) { for (let i = 1; i <= 3; i++) { const step = document.getElementById(`step${i}`); if (step) { step.classList.remove('active', 'completed'); if (i < stepNumber) { step.classList.add('completed'); const stepNum = step.querySelector('.step-number'); if (stepNum) stepNum.classList.add('completed'); } else if (i === stepNumber) { step.classList.add('active'); } } } // 진행 단계 표시 업데이트 updateProgressSteps(stepNumber); currentStep = stepNumber; } // 진행 단계 표시 업데이트 function updateProgressSteps(currentStepNumber) { for (let i = 1; i <= 3; i++) { const progressStep = document.getElementById(`progressStep${i}`); if (progressStep) { progressStep.classList.remove('active', 'completed'); if (i < currentStepNumber) { progressStep.classList.add('completed'); } else if (i === currentStepNumber) { progressStep.classList.add('active'); } } } } // 초기 데이터 로드 (통합 API 사용) async function loadData() { try { showMessage('데이터를 불러오는 중...', 'loading'); console.log('🔗 통합 API 설정을 사용한 기본 데이터 로딩 시작...'); await loadWorkers(); await loadProjects(); await loadWorkTypes(); await loadWorkStatusTypes(); await loadErrorTypes(); console.log('로드된 작업자 수:', workers.length); console.log('로드된 프로젝트 수:', projects.length); console.log('작업 유형 수:', workTypes.length); populateWorkerGrid(); hideMessage(); } catch (error) { console.error('데이터 로드 실패:', error); showMessage('데이터 로드 중 오류가 발생했습니다: ' + error.message, 'error'); } } async function loadWorkers() { try { console.log('Workers API 호출 중... (통합 API 사용)'); const data = await window.apiCall(`${window.API}/workers`); const allWorkers = Array.isArray(data) ? data : (data.data || data.workers || []); // 활성화된 작업자만 필터링 workers = allWorkers.filter(worker => { return worker.status === 'active' || worker.is_active === 1 || worker.is_active === true; }); console.log(`✅ Workers 로드 성공: ${workers.length}명 (전체: ${allWorkers.length}명)`); } catch (error) { console.error('작업자 로딩 오류:', error); throw error; } } async function loadProjects() { try { console.log('Projects API 호출 중... (활성 프로젝트만)'); const data = await window.apiCall(`${window.API}/projects/active/list`); projects = Array.isArray(data) ? data : (data.data || data.projects || []); console.log('✅ 활성 프로젝트 로드 성공:', projects.length); } catch (error) { console.error('프로젝트 로딩 오류:', error); throw error; } } async function loadWorkTypes() { try { const data = await window.apiCall(`${window.API}/daily-work-reports/work-types`); if (Array.isArray(data) && data.length > 0) { workTypes = data; console.log('✅ 작업 유형 API 사용 (통합 설정)'); return; } throw new Error('API 실패'); } catch (error) { console.log('⚠️ 작업 유형 API 사용 불가, 기본값 사용'); workTypes = [ {id: 1, name: 'Base'}, {id: 2, name: 'Vessel'}, {id: 3, name: 'Piping'} ]; } } async function loadWorkStatusTypes() { try { const data = await window.apiCall(`${window.API}/daily-work-reports/work-status-types`); if (Array.isArray(data) && data.length > 0) { workStatusTypes = data; console.log('✅ 업무 상태 유형 API 사용 (통합 설정)'); return; } throw new Error('API 실패'); } catch (error) { console.log('⚠️ 업무 상태 유형 API 사용 불가, 기본값 사용'); workStatusTypes = [ {id: 1, name: '정규'}, {id: 2, name: '에러'} ]; } } async function loadErrorTypes() { try { const data = await window.apiCall(`${window.API}/daily-work-reports/error-types`); if (Array.isArray(data) && data.length > 0) { errorTypes = data; console.log('✅ 에러 유형 API 사용 (통합 설정)'); return; } throw new Error('API 실패'); } catch (error) { console.log('⚠️ 에러 유형 API 사용 불가, 기본값 사용'); errorTypes = [ {id: 1, name: '설계미스'}, {id: 2, name: '외주작업 불량'}, {id: 3, name: '입고지연'}, {id: 4, name: '작업 불량'} ]; } } // 작업자 그리드 생성 function populateWorkerGrid() { const grid = document.getElementById('workerGrid'); grid.innerHTML = ''; workers.forEach(worker => { const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'worker-card'; btn.textContent = worker.worker_name; btn.dataset.id = worker.worker_id; btn.addEventListener('click', () => { toggleWorkerSelection(worker.worker_id, btn); }); grid.appendChild(btn); }); } // 작업자 선택 토글 function toggleWorkerSelection(workerId, btnElement) { if (selectedWorkers.has(workerId)) { selectedWorkers.delete(workerId); btnElement.classList.remove('selected'); } else { selectedWorkers.add(workerId); btnElement.classList.add('selected'); } const nextBtn = document.getElementById('nextStep2'); nextBtn.disabled = selectedWorkers.size === 0; } // 작업 항목 추가 function addWorkEntry() { console.log('🔧 addWorkEntry 함수 호출됨'); const container = document.getElementById('workEntriesList'); console.log('🔧 컨테이너:', container); workEntryCounter++; console.log('🔧 작업 항목 카운터:', workEntryCounter); const entryDiv = document.createElement('div'); entryDiv.className = 'work-entry'; entryDiv.dataset.id = workEntryCounter; console.log('🔧 생성된 작업 항목 div:', entryDiv); entryDiv.innerHTML = `
작업 항목 #${workEntryCounter}
🏗️ 프로젝트
⚙️ 작업 유형
📊 업무 상태
⚠️ 에러 유형
작업 시간 (시간)
`; container.appendChild(entryDiv); console.log('🔧 작업 항목이 컨테이너에 추가됨'); console.log('🔧 현재 컨테이너 내용:', container.innerHTML.length, '문자'); console.log('🔧 현재 .work-entry 개수:', container.querySelectorAll('.work-entry').length); setupWorkEntryEvents(entryDiv); console.log('🔧 이벤트 설정 완료'); } // 작업 항목 이벤트 설정 function setupWorkEntryEvents(entryDiv) { const timeInput = entryDiv.querySelector('.time-input'); const workStatusSelect = entryDiv.querySelector('.work-status-select'); const errorTypeSection = entryDiv.querySelector('.error-type-section'); const errorTypeSelect = entryDiv.querySelector('.error-type-select'); // 시간 입력 이벤트 timeInput.addEventListener('input', updateTotalHours); // 빠른 시간 버튼 이벤트 entryDiv.querySelectorAll('.quick-time-btn').forEach(btn => { btn.addEventListener('click', (e) => { e.preventDefault(); timeInput.value = btn.dataset.hours; updateTotalHours(); // 버튼 클릭 효과 btn.style.transform = 'scale(0.95)'; setTimeout(() => { btn.style.transform = ''; }, 150); }); }); // 업무 상태 변경 시 에러 유형 섹션 토글 workStatusSelect.addEventListener('change', (e) => { const isError = e.target.value === '2'; // 에러 상태 ID가 2라고 가정 if (isError) { errorTypeSection.classList.add('visible'); errorTypeSelect.required = true; // 에러 상태일 때 시각적 피드백 errorTypeSection.style.animation = 'slideDown 0.4s ease-out'; } else { errorTypeSection.classList.remove('visible'); errorTypeSelect.required = false; errorTypeSelect.value = ''; } }); // 폼 필드 포커스 효과 entryDiv.querySelectorAll('.form-field-group').forEach(group => { const input = group.querySelector('select, input'); if (input) { input.addEventListener('focus', () => { group.classList.add('focused'); }); input.addEventListener('blur', () => { group.classList.remove('focused'); }); } }); } // 작업 항목 제거 function removeWorkEntry(id) { console.log('🗑️ removeWorkEntry 호출됨, id:', id); const entry = document.querySelector(`.work-entry[data-id="${id}"]`); console.log('🗑️ 찾은 entry:', entry); if (entry) { entry.remove(); updateTotalHours(); console.log('✅ 작업 항목 삭제 완료'); } else { console.log('❌ 작업 항목을 찾을 수 없음'); } } // 총 시간 업데이트 function updateTotalHours() { const timeInputs = document.querySelectorAll('.time-input'); let total = 0; timeInputs.forEach(input => { const value = parseFloat(input.value) || 0; total += value; }); const display = document.getElementById('totalHoursDisplay'); display.textContent = `총 작업시간: ${total}시간`; if (total > 24) { display.style.background = 'linear-gradient(135deg, #e74c3c 0%, #c0392b 100%)'; display.textContent += ' ⚠️ 24시간 초과'; } else { display.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'; } } // 저장 함수 (통합 API 사용) async function saveWorkReport() { const reportDate = document.getElementById('reportDate').value; if (!reportDate || selectedWorkers.size === 0) { showSaveResultModal( 'error', '입력 오류', '날짜와 작업자를 선택해주세요.' ); return; } const entries = document.querySelectorAll('.work-entry'); console.log('🔍 찾은 작업 항목들:', entries); console.log('🔍 작업 항목 개수:', entries.length); if (entries.length === 0) { showSaveResultModal( 'error', '작업 항목 없음', '최소 하나의 작업을 추가해주세요.' ); return; } const newWorkEntries = []; console.log('🔍 작업 항목 수집 시작...'); for (const entry of entries) { console.log('🔍 작업 항목 처리 중:', entry); const projectSelect = entry.querySelector('.project-select'); const workTypeSelect = entry.querySelector('.work-type-select'); const workStatusSelect = entry.querySelector('.work-status-select'); const errorTypeSelect = entry.querySelector('.error-type-select'); const timeInput = entry.querySelector('.time-input'); console.log('🔍 선택된 요소들:', { projectSelect, workTypeSelect, workStatusSelect, errorTypeSelect, timeInput }); const projectId = projectSelect?.value; const workTypeId = workTypeSelect?.value; const workStatusId = workStatusSelect?.value; const errorTypeId = errorTypeSelect?.value; const workHours = timeInput?.value; console.log('🔍 수집된 값들:', { projectId, workTypeId, workStatusId, errorTypeId, workHours }); if (!projectId || !workTypeId || !workStatusId || !workHours) { showSaveResultModal( 'error', '입력 오류', '모든 작업 항목을 완성해주세요.' ); return; } if (workStatusId === '2' && !errorTypeId) { showSaveResultModal( 'error', '입력 오류', '에러 상태인 경우 에러 유형을 선택해주세요.' ); return; } const workEntry = { project_id: parseInt(projectId), work_type_id: parseInt(workTypeId), work_status_id: parseInt(workStatusId), error_type_id: errorTypeId ? parseInt(errorTypeId) : null, work_hours: parseFloat(workHours) }; console.log('🔍 생성된 작업 항목:', workEntry); console.log('🔍 작업 항목 상세:', { project_id: workEntry.project_id, work_type_id: workEntry.work_type_id, work_status_id: workEntry.work_status_id, error_type_id: workEntry.error_type_id, work_hours: workEntry.work_hours }); newWorkEntries.push(workEntry); } console.log('🔍 최종 수집된 작업 항목들:', newWorkEntries); console.log('🔍 총 작업 항목 개수:', newWorkEntries.length); try { const submitBtn = document.getElementById('submitBtn'); submitBtn.disabled = true; submitBtn.textContent = '💾 저장 중...'; const currentUser = getCurrentUser(); let totalSaved = 0; let totalFailed = 0; const failureDetails = []; for (const workerId of selectedWorkers) { const workerName = workers.find(w => w.worker_id == workerId)?.worker_name || '알 수 없음'; // 서버가 기대하는 work_entries 배열 형태로 전송 const requestData = { report_date: reportDate, worker_id: parseInt(workerId), work_entries: newWorkEntries.map(entry => ({ project_id: entry.project_id, task_id: entry.work_type_id, // 서버에서 task_id로 기대 work_hours: entry.work_hours, work_status_id: entry.work_status_id, error_type_id: entry.error_type_id })), created_by: currentUser?.user_id || currentUser?.id }; console.log('🔄 배열 형태로 전송:', requestData); console.log('🔄 work_entries:', requestData.work_entries); console.log('🔄 work_entries[0] 상세:', requestData.work_entries[0]); console.log('🔄 전송 데이터 JSON:', JSON.stringify(requestData, null, 2)); try { const result = await window.apiCall(`${window.API}/daily-work-reports`, 'POST', requestData); console.log('✅ 저장 성공:', result); totalSaved++; } catch (error) { console.error('❌ 저장 실패:', error); totalFailed++; failureDetails.push(`${workerName}: ${error.message}`); } } // 결과 모달 표시 if (totalSaved > 0 && totalFailed === 0) { showSaveResultModal( 'success', '저장 완료!', `${totalSaved}명의 작업보고서가 성공적으로 저장되었습니다.` ); } else if (totalSaved > 0 && totalFailed > 0) { showSaveResultModal( 'warning', '부분 저장 완료', `${totalSaved}명은 성공했지만 ${totalFailed}명은 실패했습니다.`, failureDetails ); } else { showSaveResultModal( 'error', '저장 실패', '모든 작업보고서 저장이 실패했습니다.', failureDetails ); } if (totalSaved > 0) { setTimeout(() => { refreshTodayWorkers(); resetForm(); }, 2000); } } catch (error) { console.error('저장 오류:', error); showSaveResultModal( 'error', '저장 오류', '저장 중 예기치 못한 오류가 발생했습니다.', [error.message] ); } finally { const submitBtn = document.getElementById('submitBtn'); submitBtn.disabled = false; submitBtn.textContent = '💾 작업보고서 저장'; } } // 폼 초기화 function resetForm() { goToStep(1); selectedWorkers.clear(); document.querySelectorAll('.worker-card.selected').forEach(btn => { btn.classList.remove('selected'); }); const container = document.getElementById('workEntriesList'); container.innerHTML = ''; workEntryCounter = 0; updateTotalHours(); document.getElementById('nextStep2').disabled = true; } // 당일 작업자 현황 로드 (본인 입력분만) - 통합 API 사용 async function loadTodayWorkers() { const section = document.getElementById('dailyWorkersSection'); const content = document.getElementById('dailyWorkersContent'); if (!section || !content) { console.log('당일 현황 섹션이 HTML에 없습니다.'); return; } try { const today = getKoreaToday(); const currentUser = getCurrentUser(); content.innerHTML = '
📊 내가 입력한 오늘의 작업 현황을 불러오는 중... (통합 API)
'; section.style.display = 'block'; // 본인이 입력한 데이터만 조회 (통합 API 사용) let queryParams = `date=${today}`; if (currentUser?.user_id) { queryParams += `&created_by=${currentUser.user_id}`; } else if (currentUser?.id) { queryParams += `&created_by=${currentUser.id}`; } console.log(`🔒 본인 입력분만 조회 (통합 API): ${API}/daily-work-reports?${queryParams}`); const rawData = await window.apiCall(`${window.API}/daily-work-reports?${queryParams}`); console.log('📊 당일 작업 데이터 (통합 API):', rawData); let data = []; if (Array.isArray(rawData)) { data = rawData; } else if (rawData?.data) { data = rawData.data; } displayMyDailyWorkers(data, today); } catch (error) { console.error('당일 작업자 로드 오류:', error); content.innerHTML = `
❌ 오늘의 작업 현황을 불러올 수 없습니다.
${error.message}
`; } } // 본인 입력 작업자 현황 표시 (수정/삭제 기능 포함) function displayMyDailyWorkers(data, date) { const content = document.getElementById('dailyWorkersContent'); if (!Array.isArray(data) || data.length === 0) { content.innerHTML = `
📝 내가 오늘(${date}) 입력한 작업이 없습니다.
새로운 작업을 추가해보세요!
`; return; } // 작업자별로 데이터 그룹화 const workerGroups = {}; data.forEach(work => { const workerName = work.worker_name || '미지정'; if (!workerGroups[workerName]) { workerGroups[workerName] = []; } workerGroups[workerName].push(work); }); const totalWorkers = Object.keys(workerGroups).length; const totalWorks = data.length; const headerHtml = `

📊 내가 입력한 오늘(${date}) 작업 현황 - 총 ${totalWorkers}명, ${totalWorks}개 작업

`; const workersHtml = Object.entries(workerGroups).map(([workerName, works]) => { const totalHours = works.reduce((sum, work) => { return sum + parseFloat(work.work_hours || 0); }, 0); // 개별 작업 항목들 (수정/삭제 버튼 포함) const individualWorksHtml = works.map((work) => { const projectName = work.project_name || '미지정'; const workTypeName = work.work_type_name || '미지정'; const workStatusName = work.work_status_name || '미지정'; const workHours = work.work_hours || 0; const errorTypeName = work.error_type_name || null; const workId = work.id; return `
🏗️ 프로젝트
${projectName}
⚙️ 작업종류
${workTypeName}
📊 작업상태
${workStatusName}
⏰ 작업시간
${workHours}시간
${errorTypeName ? `
❌ 에러유형
${errorTypeName}
` : ''}
`; }).join(''); return `
👤 ${workerName}
총 ${totalHours}시간
${individualWorksHtml}
`; }).join(''); content.innerHTML = headerHtml + '
' + workersHtml + '
'; } // 작업 항목 수정 함수 (통합 API 사용) async function editWorkItem(workId) { try { console.log('수정할 작업 ID:', workId); // 1. 기존 데이터 조회 (통합 API 사용) showMessage('작업 정보를 불러오는 중... (통합 API)', 'loading'); const workData = await window.apiCall(`${window.API}/daily-work-reports/${workId}`); console.log('수정할 작업 데이터 (통합 API):', workData); // 2. 수정 모달 표시 showEditModal(workData); hideMessage(); } catch (error) { console.error('작업 정보 조회 오류:', error); showMessage('작업 정보를 불러올 수 없습니다: ' + error.message, 'error'); } } // 수정 모달 표시 function showEditModal(workData) { editingWorkId = workData.id; const modalHtml = `

✏️ 작업 수정

`; document.body.insertAdjacentHTML('beforeend', modalHtml); // 업무 상태 변경 이벤트 document.getElementById('editWorkStatus').addEventListener('change', (e) => { const errorTypeGroup = document.getElementById('editErrorTypeGroup'); if (e.target.value === '2') { errorTypeGroup.style.display = 'block'; } else { errorTypeGroup.style.display = 'none'; } }); } // 수정 모달 닫기 function closeEditModal() { const modal = document.getElementById('editModal'); if (modal) { modal.remove(); } editingWorkId = null; } // 수정된 작업 저장 (통합 API 사용) async function saveEditedWork() { try { const projectId = document.getElementById('editProject').value; const workTypeId = document.getElementById('editWorkType').value; const workStatusId = document.getElementById('editWorkStatus').value; const errorTypeId = document.getElementById('editErrorType').value; const workHours = document.getElementById('editWorkHours').value; if (!projectId || !workTypeId || !workStatusId || !workHours) { showMessage('모든 필수 항목을 입력해주세요.', 'error'); return; } if (workStatusId === '2' && !errorTypeId) { showMessage('에러 상태인 경우 에러 유형을 선택해주세요.', 'error'); return; } const updateData = { project_id: parseInt(projectId), work_type_id: parseInt(workTypeId), work_status_id: parseInt(workStatusId), error_type_id: errorTypeId ? parseInt(errorTypeId) : null, work_hours: parseFloat(workHours) }; showMessage('작업을 수정하는 중... (통합 API)', 'loading'); const result = await window.apiCall(`${window.API}/daily-work-reports/${editingWorkId}`, { method: 'PUT', body: JSON.stringify(updateData) }); console.log('✅ 수정 성공 (통합 API):', result); showMessage('✅ 작업이 성공적으로 수정되었습니다!', 'success'); closeEditModal(); refreshTodayWorkers(); } catch (error) { console.error('❌ 수정 실패:', error); showMessage('수정 중 오류가 발생했습니다: ' + error.message, 'error'); } } // 작업 항목 삭제 함수 (통합 API 사용) async function deleteWorkItem(workId) { if (!confirm('정말로 이 작업을 삭제하시겠습니까?\n삭제된 작업은 복구할 수 없습니다.')) { return; } try { console.log('삭제할 작업 ID:', workId); showMessage('작업을 삭제하는 중... (통합 API)', 'loading'); // 개별 항목 삭제 API 호출 (본인 작성분만 삭제 가능) - 통합 API 사용 const result = await window.apiCall(`${window.API}/daily-work-reports/my-entry/${workId}`, { method: 'DELETE' }); console.log('✅ 삭제 성공 (통합 API):', result); showMessage('✅ 작업이 성공적으로 삭제되었습니다!', 'success'); // 화면 새로고침 refreshTodayWorkers(); } catch (error) { console.error('❌ 삭제 실패:', error); showMessage('삭제 중 오류가 발생했습니다: ' + error.message, 'error'); } } // 오늘 현황 새로고침 function refreshTodayWorkers() { loadTodayWorkers(); } // 이벤트 리스너 설정 function setupEventListeners() { document.getElementById('nextStep1').addEventListener('click', () => { const dateInput = document.getElementById('reportDate'); if (dateInput && dateInput.value) { goToStep(2); } else { showMessage('날짜를 선택해주세요.', 'error'); } }); document.getElementById('nextStep2').addEventListener('click', () => { if (selectedWorkers.size > 0) { goToStep(3); addWorkEntry(); } else { showMessage('작업자를 선택해주세요.', 'error'); } }); document.getElementById('addWorkBtn').addEventListener('click', addWorkEntry); document.getElementById('submitBtn').addEventListener('click', saveWorkReport); } // 초기화 async function init() { try { const token = localStorage.getItem('token'); if (!token || token === 'undefined') { showMessage('로그인이 필요합니다.', 'error'); localStorage.removeItem('token'); setTimeout(() => { window.location.href = '/'; }, 2000); return; } document.getElementById('reportDate').value = getKoreaToday(); await loadData(); setupEventListeners(); loadTodayWorkers(); console.log('✅ 시스템 초기화 완료 (통합 API 설정 적용)'); } catch (error) { console.error('초기화 오류:', error); showMessage('초기화 중 오류가 발생했습니다.', 'error'); } } // 페이지 로드 시 초기화 document.addEventListener('DOMContentLoaded', init); // 전역 함수로 노출 window.removeWorkEntry = removeWorkEntry; window.refreshTodayWorkers = refreshTodayWorkers; window.editWorkItem = editWorkItem; window.deleteWorkItem = deleteWorkItem; window.closeEditModal = closeEditModal; window.saveEditedWork = saveEditedWork;