// worker-individual-report.js - 작업자별 개별 보고서 관리 // 전역 변수 let currentWorkerId = null; let currentWorkerName = ''; let selectedDate = ''; let currentUser = null; let workTypes = []; let workStatusTypes = []; let errorTypes = []; let projects = []; let existingWork = []; // URL 파라미터에서 정보 추출 function getUrlParams() { const urlParams = new URLSearchParams(window.location.search); return { user_id: urlParams.get('user_id'), worker_name: decodeURIComponent(urlParams.get('worker_name') || ''), date: urlParams.get('date') || new Date().toISOString().split('T')[0] }; } // 현재 로그인한 사용자 정보 가져오기 function getCurrentUser() { try { const token = localStorage.getItem('sso_token'); if (!token) return null; const payloadBase64 = token.split('.')[1]; if (payloadBase64) { const payload = JSON.parse(atob(payloadBase64)); return payload; } } catch (error) { console.log('토큰에서 사용자 정보 추출 실패:', error); } try { const userInfo = localStorage.getItem('sso_user'); if (userInfo) { return JSON.parse(userInfo); } } catch (error) { console.log('localStorage에서 사용자 정보 파싱 실패:', error); } return null; } // 메시지 표시 함수 function showMessage(msg, type = 'info') { const container = document.getElementById('message-container'); if (container) { container.innerHTML = `
${msg}
`; setTimeout(() => { container.innerHTML = ''; }, 5000); } } // 페이지 초기화 document.addEventListener('DOMContentLoaded', async () => { // API 함수가 로드될 때까지 기다림 let retryCount = 0; const maxRetries = 50; while (!window.apiCall && retryCount < maxRetries) { await new Promise(resolve => setTimeout(resolve, 100)); retryCount++; } if (!window.apiCall) { console.error('❌ API 함수를 로드할 수 없습니다.'); showMessage('시스템을 초기화할 수 없습니다. 페이지를 새로고침해주세요.', 'error'); return; } try { await initializePage(); } catch (error) { console.error('페이지 초기화 오류:', error); showMessage('페이지를 불러오는 중 오류가 발생했습니다.', 'error'); } }); async function initializePage() { console.log('🚀 개별 작업 보고서 페이지 초기화 시작'); // URL 파라미터 추출 const params = getUrlParams(); currentWorkerId = parseInt(params.user_id); currentWorkerName = params.worker_name; selectedDate = params.date; // 사용자 정보 설정 currentUser = getCurrentUser(); if (!currentWorkerId || !currentWorkerName) { showMessage('잘못된 접근입니다. 작업자 정보가 없습니다.', 'error'); setTimeout(() => { window.history.back(); }, 2000); return; } // 페이지 제목 설정 updatePageHeader(); // 이벤트 리스너 설정 setupEventListeners(); // 초기 데이터 로드 await loadInitialData(); console.log('✅ 개별 작업 보고서 페이지 초기화 완료'); } function updatePageHeader() { document.getElementById('pageTitle').textContent = `👤 ${currentWorkerName} 작업 보고서`; document.getElementById('pageSubtitle').textContent = `${selectedDate} 작업 내용을 관리합니다.`; // 작업자 정보 카드 업데이트 document.getElementById('workerInitial').textContent = currentWorkerName.charAt(0); document.getElementById('workerName').textContent = currentWorkerName; document.getElementById('selectedDate').textContent = selectedDate; } function setupEventListeners() { // 새 작업 추가 버튼 document.getElementById('addNewWorkBtn').addEventListener('click', showNewWorkForm); document.getElementById('cancelNewWorkBtn').addEventListener('click', hideNewWorkForm); document.getElementById('saveNewWorkBtn').addEventListener('click', saveNewWork); // 업무 상태 변경 시 에러 유형 섹션 토글 document.getElementById('newWorkStatusSelect').addEventListener('change', toggleErrorTypeSection); // 빠른 시간 버튼 document.querySelectorAll('.quick-time-btn').forEach(btn => { btn.addEventListener('click', (e) => { document.getElementById('newWorkHours').value = e.target.dataset.hours; }); }); // 휴가 처리 버튼들 document.querySelectorAll('.vacation-process-btn').forEach(btn => { btn.addEventListener('click', (e) => { const vacationType = e.target.dataset.type; handleVacationProcess(vacationType); }); }); } async function loadInitialData() { try { showMessage('데이터를 불러오는 중...', 'loading'); // 병렬로 데이터 로드 await Promise.all([ loadWorkerInfo(), loadExistingWork(), loadProjects(), loadWorkTypes(), loadWorkStatusTypes(), loadErrorTypes() ]); // UI 업데이트 updateWorkerSummary(); renderExistingWork(); populateDropdowns(); showMessage('데이터 로드 완료', 'success'); } catch (error) { console.error('초기 데이터 로드 실패:', error); showMessage('데이터 로드 중 오류가 발생했습니다: ' + error.message, 'error'); } } async function loadWorkerInfo() { try { const response = await window.apiCall(`/workers/${currentWorkerId}`); const worker = response.data || response; document.getElementById('workerJob').textContent = worker.job_type || '작업자'; } catch (error) { console.error('작업자 정보 로드 오류:', error); } } async function loadExistingWork() { try { const response = await window.apiCall(`/daily-work-reports?date=${selectedDate}&user_id=${currentWorkerId}`); existingWork = Array.isArray(response) ? response : (response.data || []); console.log(`✅ 기존 작업 ${existingWork.length}건 로드 완료`); } catch (error) { console.error('기존 작업 로드 오류:', error); existingWork = []; } } async function loadProjects() { try { const response = await window.apiCall(`/projects/active/list`); projects = Array.isArray(response) ? response : (response.data || []); } catch (error) { console.error('프로젝트 로드 오류:', error); projects = []; } } async function loadWorkTypes() { try { const response = await window.apiCall(`/daily-work-reports/work-types`); workTypes = Array.isArray(response) ? response : (response.data || []); } catch (error) { console.error('작업 유형 로드 오류:', error); workTypes = []; } } async function loadWorkStatusTypes() { try { const response = await window.apiCall(`/daily-work-reports/work-status-types`); workStatusTypes = Array.isArray(response) ? response : (response.data || []); } catch (error) { console.error('작업 상태 유형 로드 오류:', error); workStatusTypes = []; } } async function loadErrorTypes() { try { const response = await window.apiCall(`/daily-work-reports/error-types`); errorTypes = Array.isArray(response) ? response : (response.data || []); } catch (error) { console.error('에러 유형 로드 오류:', error); errorTypes = []; } } function updateWorkerSummary() { const totalHours = existingWork.reduce((sum, work) => sum + parseFloat(work.work_hours || 0), 0); const workCount = existingWork.length; document.getElementById('totalHours').textContent = `${totalHours.toFixed(1)}h`; document.getElementById('workCount').textContent = `${workCount}건`; // 12시간 초과 경고 if (totalHours > 12) { document.getElementById('totalHours').classList.add('warning'); showMessage(`⚠️ 총 작업시간이 ${totalHours.toFixed(1)}시간으로 12시간을 초과했습니다.`, 'warning'); } } function renderExistingWork() { const container = document.getElementById('existingWorkList'); if (existingWork.length === 0) { container.innerHTML = `
📭

등록된 작업이 없습니다

${selectedDate}에 ${currentWorkerName}님의 작업이 등록되지 않았습니다.

`; return; } container.innerHTML = existingWork.map(work => `

${work.project_name || '미지정 프로젝트'}

${work.work_type_name || '미지정 작업'}

${work.work_status_name || '정상'} ${work.work_hours}h
${work.work_status_id === 2 && work.error_type_name ? `
오류: ${work.error_type_name}
` : ''}
`).join(''); } function populateDropdowns() { // 프로젝트 드롭다운 const projectSelect = document.getElementById('newProjectSelect'); projectSelect.innerHTML = ''; projects.forEach(project => { const option = document.createElement('option'); option.value = project.project_id; option.textContent = project.project_name; projectSelect.appendChild(option); }); // 작업 유형 드롭다운 const workTypeSelect = document.getElementById('newWorkTypeSelect'); workTypeSelect.innerHTML = ''; workTypes.forEach(type => { const option = document.createElement('option'); option.value = type.id; option.textContent = type.name; workTypeSelect.appendChild(option); }); // 작업 상태 드롭다운 const workStatusSelect = document.getElementById('newWorkStatusSelect'); workStatusSelect.innerHTML = ''; workStatusTypes.forEach(status => { const option = document.createElement('option'); option.value = status.id; option.textContent = status.name; workStatusSelect.appendChild(option); }); // 에러 유형 드롭다운 const errorTypeSelect = document.getElementById('newErrorTypeSelect'); errorTypeSelect.innerHTML = ''; errorTypes.forEach(error => { const option = document.createElement('option'); option.value = error.id; option.textContent = error.name; errorTypeSelect.appendChild(option); }); } function showNewWorkForm() { document.getElementById('newWorkSection').style.display = 'block'; document.getElementById('addNewWorkBtn').style.display = 'none'; } function hideNewWorkForm() { document.getElementById('newWorkSection').style.display = 'none'; document.getElementById('addNewWorkBtn').style.display = 'block'; resetNewWorkForm(); } function resetNewWorkForm() { document.getElementById('newProjectSelect').value = ''; document.getElementById('newWorkTypeSelect').value = ''; document.getElementById('newWorkStatusSelect').value = ''; document.getElementById('newErrorTypeSelect').value = ''; document.getElementById('newWorkHours').value = '1.00'; document.getElementById('newErrorTypeSection').classList.remove('visible'); } function toggleErrorTypeSection() { const workStatusSelect = document.getElementById('newWorkStatusSelect'); const errorSection = document.getElementById('newErrorTypeSection'); const errorTypeSelect = document.getElementById('newErrorTypeSelect'); if (workStatusSelect.value === '2') { // 에러 상태 errorSection.classList.add('visible'); errorTypeSelect.setAttribute('required', 'true'); } else { errorSection.classList.remove('visible'); errorTypeSelect.removeAttribute('required'); errorTypeSelect.value = ''; } } async function saveNewWork() { try { const projectId = document.getElementById('newProjectSelect').value; const workTypeId = document.getElementById('newWorkTypeSelect').value; const workStatusId = document.getElementById('newWorkStatusSelect').value; const errorTypeId = document.getElementById('newErrorTypeSelect').value; const workHours = document.getElementById('newWorkHours').value; // 유효성 검사 if (!projectId || !workTypeId || !workStatusId || !workHours) { showMessage('모든 필수 필드를 입력해주세요.', 'error'); return; } if (workStatusId === '2' && !errorTypeId) { showMessage('에러 상태일 때는 에러 유형을 선택해야 합니다.', 'error'); return; } showMessage('작업을 저장하는 중...', 'loading'); const workData = { report_date: selectedDate, user_id: currentWorkerId, project_id: parseInt(projectId), work_type_id: parseInt(workTypeId), work_status_id: parseInt(workStatusId), error_type_id: workStatusId === '2' ? parseInt(errorTypeId) : null, work_hours: parseFloat(workHours), created_by: currentUser?.user_id || 1 }; const response = await window.apiCall(`/daily-work-reports`, 'POST', workData); showMessage('작업이 성공적으로 저장되었습니다.', 'success'); // 데이터 새로고침 await loadExistingWork(); updateWorkerSummary(); renderExistingWork(); hideNewWorkForm(); } catch (error) { console.error('작업 저장 오류:', error); showMessage(`작업 저장 중 오류가 발생했습니다: ${error.message}`, 'error'); } } async function editWork(workId) { // TODO: 작업 수정 모달 또는 인라인 편집 구현 console.log(`작업 ${workId} 수정`); showMessage('작업 수정 기능은 곧 구현될 예정입니다.', 'info'); } async function deleteWork(workId) { if (!confirm('이 작업을 삭제하시겠습니까?')) { return; } try { showMessage('작업을 삭제하는 중...', 'loading'); await window.apiCall(`/daily-work-reports/${workId}`, { method: 'DELETE' }); showMessage('작업이 성공적으로 삭제되었습니다.', 'success'); // 데이터 새로고침 await loadExistingWork(); updateWorkerSummary(); renderExistingWork(); } catch (error) { console.error('작업 삭제 오류:', error); showMessage(`작업 삭제 중 오류가 발생했습니다: ${error.message}`, 'error'); } } async function handleVacationProcess(vacationType) { const vacationNames = { 'full': '연차', 'half-half': '반반차', 'half': '반차' }; const vacationHours = { 'full': 8, 'half-half': 6, 'half': 4 }; if (!confirm(`${vacationNames[vacationType]} 처리하시겠습니까?\n(${vacationHours[vacationType]}시간으로 자동 입력됩니다)`)) { return; } try { showMessage(`${vacationNames[vacationType]} 처리 중...`, 'loading'); // 휴가용 작업 보고서 생성 const vacationWork = { report_date: selectedDate, user_id: currentWorkerId, project_id: 1, // 기본 프로젝트 (휴가용) work_type_id: 999, // 휴가 전용 작업 유형 (DB에 추가 필요) work_status_id: 1, // 정상 상태 error_type_id: null, work_hours: vacationHours[vacationType], created_by: currentUser?.user_id || 1 }; const response = await window.apiCall(`/daily-work-reports`, { method: 'POST', body: JSON.stringify(vacationWork) }); showMessage(`${vacationNames[vacationType]} 처리가 완료되었습니다.`, 'success'); // 데이터 새로고침 await loadExistingWork(); updateWorkerSummary(); renderExistingWork(); } catch (error) { console.error('휴가 처리 오류:', error); showMessage(`휴가 처리 중 오류가 발생했습니다: ${error.message}`, 'error'); } } // 전역 함수로 등록 window.editWork = editWork; window.deleteWork = deleteWork;