// task-management.js - 작업 관리 페이지 JavaScript // 전역 변수 let workTypes = []; // 공정 목록 let tasks = []; // 작업 목록 let currentWorkTypeId = ''; // 현재 선택된 공정 ID let currentEditingTask = null; // 페이지 초기화 document.addEventListener('DOMContentLoaded', async () => { console.log('📋 작업 관리 페이지 초기화'); // API 함수가 로드될 때까지 대기 let retryCount = 0; while (!window.apiCall && retryCount < 50) { await new Promise(resolve => setTimeout(resolve, 100)); retryCount++; } if (!window.apiCall) { showToast('시스템을 초기화할 수 없습니다. 페이지를 새로고침해주세요.', 'error'); return; } await loadAllData(); }); // 전체 데이터 로드 async function loadAllData() { try { // 공정 목록 로드 (work_types 조회 - 코드 관리 API 사용) await loadWorkTypes(); // 작업 목록 로드 await loadTasks(); } catch (error) { console.error('❌ 데이터 로드 오류:', error); showToast('데이터를 불러오는 중 오류가 발생했습니다.', 'error'); } } window.loadAllData = loadAllData; // 공정 목록 로드 async function loadWorkTypes() { try { // 작업 유형(공정) 목록 조회 const response = await window.apiCall('/daily-work-reports/work-types'); if (response && response.success) { workTypes = response.data || []; } else { workTypes = []; } console.log('✅ 공정 목록 로드:', workTypes.length + '개'); renderWorkTypeTabs(); populateWorkTypeSelect(); } catch (error) { console.error('❌ 공정 목록 조회 오류:', error); // API 오류 시에도 빈 배열로 처리 workTypes = []; renderWorkTypeTabs(); } } // 작업 목록 로드 async function loadTasks() { try { const response = await window.apiCall('/tasks'); if (response && response.success) { tasks = response.data || []; console.log('✅ 작업 목록 로드:', tasks.length + '개'); } else { tasks = []; } renderTasks(); updateStatistics(); } catch (error) { console.error('❌ 작업 목록 조회 오류:', error); showToast('작업 목록을 불러오는 중 오류가 발생했습니다.', 'error'); tasks = []; renderTasks(); } } // 공정 탭 렌더링 function renderWorkTypeTabs() { const tabsContainer = document.getElementById('workTypeTabs'); let tabsHtml = ` `; workTypes.forEach(workType => { const count = tasks.filter(t => t.work_type_id === workType.id).length; const isActive = currentWorkTypeId === workType.id; const safeId = parseInt(workType.id) || 0; tabsHtml += ` `; }); tabsContainer.innerHTML = tabsHtml; } // 공정 전환 function switchWorkType(workTypeId) { currentWorkTypeId = workTypeId === '' ? '' : parseInt(workTypeId); renderWorkTypeTabs(); renderTasks(); updateStatistics(); } window.switchWorkType = switchWorkType; // 작업 목록 렌더링 function renderTasks() { const grid = document.getElementById('taskGrid'); // 현재 선택된 공정으로 필터링 let filteredTasks = tasks; if (currentWorkTypeId !== '') { filteredTasks = tasks.filter(t => t.work_type_id === currentWorkTypeId); } if (filteredTasks.length === 0) { grid.innerHTML = `
📋

등록된 작업이 없습니다

"작업 추가" 버튼을 눌러 새로운 작업을 등록하세요

`; return; } grid.innerHTML = filteredTasks.map(task => createTaskCard(task)).join(''); } // 작업 카드 생성 function createTaskCard(task) { const statusBadge = task.is_active ? '활성' : '비활성'; const safeTaskId = parseInt(task.task_id) || 0; return `

${escapeHtml(task.task_name)}

${statusBadge}
소속 공정 ${escapeHtml(task.work_type_name || '-')}
${task.category ? `
카테고리 ${escapeHtml(task.category)}
` : ''}
${task.description ? `
${escapeHtml(task.description)}
` : ''}
등록: ${escapeHtml(formatDate(task.created_at))}
`; } // 통계 업데이트 function updateStatistics() { let filteredTasks = tasks; if (currentWorkTypeId !== '') { filteredTasks = tasks.filter(t => t.work_type_id === currentWorkTypeId); } const activeCount = filteredTasks.filter(t => t.is_active).length; document.getElementById('totalCount').textContent = filteredTasks.length; document.getElementById('activeCount').textContent = activeCount; } // 새로고침 function refreshTasks() { loadAllData(); showToast('데이터를 새로고침했습니다.', 'success'); } window.refreshTasks = refreshTasks; // ==================== 작업 모달 ==================== // 작업 모달 열기 (신규) function openTaskModal() { currentEditingTask = null; document.getElementById('taskModalTitle').textContent = '작업 추가'; document.getElementById('taskForm').reset(); document.getElementById('taskId').value = ''; document.getElementById('taskIsActive').checked = true; // 공정 선택 드롭다운 채우기 populateWorkTypeSelect(); // 현재 선택된 공정이 있으면 자동 선택 if (currentWorkTypeId !== '') { document.getElementById('taskWorkTypeId').value = currentWorkTypeId; } document.getElementById('deleteTaskBtn').style.display = 'none'; document.getElementById('taskModal').style.display = 'flex'; document.body.style.overflow = 'hidden'; } window.openTaskModal = openTaskModal; // 작업 편집 async function editTask(taskId) { try { const response = await window.apiCall(`/tasks/${taskId}`); if (response && response.success) { currentEditingTask = response.data; document.getElementById('taskModalTitle').textContent = '작업 수정'; document.getElementById('taskId').value = currentEditingTask.task_id; document.getElementById('taskWorkTypeId').value = currentEditingTask.work_type_id || ''; document.getElementById('taskName').value = currentEditingTask.task_name; document.getElementById('taskDescription').value = currentEditingTask.description || ''; document.getElementById('taskIsActive').checked = currentEditingTask.is_active; document.getElementById('deleteTaskBtn').style.display = 'block'; document.getElementById('taskModal').style.display = 'flex'; document.body.style.overflow = 'hidden'; } } catch (error) { console.error('❌ 작업 조회 오류:', error); showToast('작업 정보를 불러올 수 없습니다.', 'error'); } } window.editTask = editTask; // 작업 모달 닫기 function closeTaskModal() { document.getElementById('taskModal').style.display = 'none'; document.body.style.overflow = 'auto'; currentEditingTask = null; } window.closeTaskModal = closeTaskModal; // 공정 선택 드롭다운 채우기 function populateWorkTypeSelect() { const select = document.getElementById('taskWorkTypeId'); select.innerHTML = '' + workTypes.map(wt => ` `).join(''); } // 작업 저장 async function saveTask() { const taskId = document.getElementById('taskId').value; const taskData = { work_type_id: parseInt(document.getElementById('taskWorkTypeId').value) || null, task_name: document.getElementById('taskName').value.trim(), description: document.getElementById('taskDescription').value.trim() || null, is_active: document.getElementById('taskIsActive').checked ? 1 : 0 }; if (!taskData.task_name) { showToast('작업명을 입력해주세요.', 'error'); return; } try { let response; if (taskId) { // 수정 response = await window.apiCall(`/tasks/${taskId}`, 'PUT', taskData); } else { // 신규 response = await window.apiCall('/tasks', 'POST', taskData); } if (response && response.success) { showToast(taskId ? '작업이 수정되었습니다.' : '작업이 추가되었습니다.', 'success'); closeTaskModal(); await loadAllData(); } else { throw new Error(response.message || '저장에 실패했습니다.'); } } catch (error) { console.error('❌ 작업 저장 오류:', error); showToast('작업 저장 중 오류가 발생했습니다.', 'error'); } } window.saveTask = saveTask; // 작업 삭제 async function deleteTask() { if (!currentEditingTask) return; if (!confirm(`"${currentEditingTask.task_name}" 작업을 삭제하시겠습니까?`)) { return; } try { const response = await window.apiCall(`/tasks/${currentEditingTask.task_id}`, 'DELETE'); if (response && response.success) { showToast('작업이 삭제되었습니다.', 'success'); closeTaskModal(); await loadAllData(); } else { throw new Error(response.message || '삭제에 실패했습니다.'); } } catch (error) { console.error('❌ 작업 삭제 오류:', error); showToast('작업 삭제 중 오류가 발생했습니다.', 'error'); } } window.deleteTask = deleteTask; // ==================== 유틸리티 ==================== // 날짜 포맷 function formatDate(dateString) { if (!dateString) return '-'; const date = new Date(dateString); return date.toLocaleDateString('ko-KR', { year: 'numeric', month: '2-digit', day: '2-digit' }); } // 토스트 알림 function showToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.textContent = message; toast.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 1rem 1.5rem; background: ${type === 'success' ? '#10b981' : type === 'error' ? '#ef4444' : '#3b82f6'}; color: white; border-radius: 0.5rem; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); z-index: 10000; animation: slideIn 0.3s ease-out; `; document.body.appendChild(toast); setTimeout(() => { toast.style.animation = 'slideOut 0.3s ease-out'; setTimeout(() => toast.remove(), 300); }, 3000); } // ==================== 공정 관리 ==================== let currentEditingWorkType = null; // 공정 모달 열기 (신규) function openWorkTypeModal() { currentEditingWorkType = null; document.getElementById('workTypeModalTitle').textContent = '공정 추가'; document.getElementById('workTypeId').value = ''; document.getElementById('workTypeName').value = ''; document.getElementById('workTypeCategory').value = ''; document.getElementById('workTypeDescription').value = ''; document.getElementById('deleteWorkTypeBtn').style.display = 'none'; document.getElementById('workTypeModal').style.display = 'flex'; document.body.style.overflow = 'hidden'; } window.openWorkTypeModal = openWorkTypeModal; // 공정 수정 모달 열기 async function editWorkType(workTypeId) { try { const workType = workTypes.find(wt => wt.id === workTypeId); if (!workType) { showToast('공정 정보를 찾을 수 없습니다.', 'error'); return; } currentEditingWorkType = workType; document.getElementById('workTypeModalTitle').textContent = '공정 수정'; document.getElementById('workTypeId').value = workType.id; document.getElementById('workTypeName').value = workType.name || ''; document.getElementById('workTypeCategory').value = workType.category || ''; document.getElementById('workTypeDescription').value = workType.description || ''; document.getElementById('deleteWorkTypeBtn').style.display = 'block'; document.getElementById('workTypeModal').style.display = 'flex'; document.body.style.overflow = 'hidden'; } catch (error) { console.error('❌ 공정 조회 오류:', error); showToast('공정 정보를 불러올 수 없습니다.', 'error'); } } window.editWorkType = editWorkType; // 공정 모달 닫기 function closeWorkTypeModal() { document.getElementById('workTypeModal').style.display = 'none'; document.body.style.overflow = 'auto'; currentEditingWorkType = null; } window.closeWorkTypeModal = closeWorkTypeModal; // 공정 저장 async function saveWorkType() { const workTypeId = document.getElementById('workTypeId').value; const workTypeData = { name: document.getElementById('workTypeName').value.trim(), category: document.getElementById('workTypeCategory').value.trim() || null, description: document.getElementById('workTypeDescription').value.trim() || null }; if (!workTypeData.name) { showToast('공정명을 입력해주세요.', 'error'); return; } try { let response; if (workTypeId) { // 수정 response = await window.apiCall(`/daily-work-reports/work-types/${workTypeId}`, 'PUT', workTypeData); } else { // 신규 response = await window.apiCall('/daily-work-reports/work-types', 'POST', workTypeData); } if (response && response.success) { showToast(workTypeId ? '공정이 수정되었습니다.' : '공정이 추가되었습니다.', 'success'); closeWorkTypeModal(); await loadAllData(); } else { throw new Error(response.message || '저장에 실패했습니다.'); } } catch (error) { console.error('❌ 공정 저장 오류:', error); showToast('공정 저장 중 오류가 발생했습니다.', 'error'); } } window.saveWorkType = saveWorkType; // 공정 삭제 async function deleteWorkType() { if (!currentEditingWorkType) return; // 이 공정에 속한 작업이 있는지 확인 const relatedTasks = tasks.filter(t => t.work_type_id === currentEditingWorkType.id); if (relatedTasks.length > 0) { showToast(`이 공정에 ${relatedTasks.length}개의 작업이 연결되어 있어 삭제할 수 없습니다.`, 'error'); return; } if (!confirm(`"${currentEditingWorkType.name}" 공정을 삭제하시겠습니까?`)) { return; } try { const response = await window.apiCall(`/daily-work-reports/work-types/${currentEditingWorkType.id}`, 'DELETE'); if (response && response.success) { showToast('공정이 삭제되었습니다.', 'success'); closeWorkTypeModal(); await loadAllData(); } else { throw new Error(response.message || '삭제에 실패했습니다.'); } } catch (error) { console.error('❌ 공정 삭제 오류:', error); showToast('공정 삭제 중 오류가 발생했습니다.', 'error'); } } window.deleteWorkType = deleteWorkType;