feat: 작업 관리 시스템 및 TBM 공정/작업 통합
## Backend Changes - Create tasks table with work_type_id FK to work_types - Add taskModel, taskController, taskRoutes for task CRUD - Update tbmModel to support work_type_id and task_id - Add migrations for tasks table and TBM integration ## Frontend Changes - Create task management admin page (tasks.html, task-management.js) - Update TBM modal to include work type (공정) and task (작업) selection - Add cascading dropdown: work type → task selection - Display work type and task info in TBM session cards - Update sidebar navigation in all admin pages ## Database Schema - tasks: task_id, work_type_id, task_name, description, is_active - tbm_sessions: add work_type_id, task_id columns with FKs - Foreign keys maintain referential integrity with work_types and tasks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@ let allSessions = [];
|
||||
let todaySessions = [];
|
||||
let allWorkers = [];
|
||||
let allProjects = [];
|
||||
let allWorkTypes = [];
|
||||
let allTasks = [];
|
||||
let allSafetyChecks = [];
|
||||
let currentSessionId = null;
|
||||
let selectedWorkers = new Set();
|
||||
@@ -77,6 +79,20 @@ async function loadInitialData() {
|
||||
console.log('✅ 안전 체크리스트 로드:', allSafetyChecks.length + '개');
|
||||
}
|
||||
|
||||
// 공정(Work Types) 목록 로드
|
||||
const workTypesResponse = await window.apiCall('/tools/work-types');
|
||||
if (workTypesResponse && workTypesResponse.success) {
|
||||
allWorkTypes = workTypesResponse.data || [];
|
||||
console.log('✅ 공정 목록 로드:', allWorkTypes.length + '개');
|
||||
}
|
||||
|
||||
// 작업(Tasks) 목록 로드
|
||||
const tasksResponse = await window.apiCall('/tasks/active/list');
|
||||
if (tasksResponse && tasksResponse.success) {
|
||||
allTasks = tasksResponse.data || [];
|
||||
console.log('✅ 작업 목록 로드:', allTasks.length + '개');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 초기 데이터 로드 오류:', error);
|
||||
showToast('데이터를 불러오는 중 오류가 발생했습니다.', 'error');
|
||||
@@ -275,6 +291,14 @@ function createSessionCard(session) {
|
||||
<span class="info-label">프로젝트</span>
|
||||
<span class="info-value">${session.project_name || '-'}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">공정</span>
|
||||
<span class="info-value">${session.work_type_name || '-'}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">작업</span>
|
||||
<span class="info-value">${session.task_name || '-'}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">작업 장소</span>
|
||||
<span class="info-value">${session.work_location || '-'}</span>
|
||||
@@ -328,6 +352,14 @@ function openNewTbmModal() {
|
||||
// 팀장 목록 로드
|
||||
populateLeaderSelect();
|
||||
populateProjectSelect();
|
||||
populateWorkTypeSelect();
|
||||
|
||||
// 작업 드롭다운 초기화
|
||||
const taskSelect = document.getElementById('taskId');
|
||||
if (taskSelect) {
|
||||
taskSelect.innerHTML = '<option value="">작업 선택...</option>';
|
||||
taskSelect.disabled = true;
|
||||
}
|
||||
|
||||
document.getElementById('tbmModal').style.display = 'flex';
|
||||
document.body.style.overflow = 'hidden';
|
||||
@@ -360,6 +392,48 @@ function populateProjectSelect() {
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 공정(Work Type) 선택 드롭다운 채우기
|
||||
function populateWorkTypeSelect() {
|
||||
const workTypeSelect = document.getElementById('workTypeId');
|
||||
if (!workTypeSelect) return;
|
||||
|
||||
workTypeSelect.innerHTML = '<option value="">공정 선택...</option>' +
|
||||
allWorkTypes.map(wt => `
|
||||
<option value="${wt.id}">${wt.name}${wt.category ? ' (' + wt.category + ')' : ''}</option>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 작업(Task) 선택 드롭다운 채우기 (공정 선택 시 호출)
|
||||
function loadTasksByWorkType() {
|
||||
const workTypeId = document.getElementById('workTypeId').value;
|
||||
const taskSelect = document.getElementById('taskId');
|
||||
|
||||
if (!taskSelect) return;
|
||||
|
||||
if (!workTypeId) {
|
||||
taskSelect.innerHTML = '<option value="">작업 선택...</option>';
|
||||
taskSelect.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 선택한 공정에 해당하는 작업만 필터링
|
||||
const filteredTasks = allTasks.filter(task =>
|
||||
task.work_type_id === parseInt(workTypeId)
|
||||
);
|
||||
|
||||
taskSelect.disabled = false;
|
||||
taskSelect.innerHTML = '<option value="">작업 선택...</option>' +
|
||||
filteredTasks.map(task => `
|
||||
<option value="${task.task_id}">${task.task_name}</option>
|
||||
`).join('');
|
||||
|
||||
if (filteredTasks.length === 0) {
|
||||
taskSelect.innerHTML = '<option value="">등록된 작업이 없습니다</option>';
|
||||
taskSelect.disabled = true;
|
||||
}
|
||||
}
|
||||
window.loadTasksByWorkType = loadTasksByWorkType;
|
||||
|
||||
// TBM 모달 닫기
|
||||
function closeTbmModal() {
|
||||
document.getElementById('tbmModal').style.display = 'none';
|
||||
@@ -369,10 +443,15 @@ window.closeTbmModal = closeTbmModal;
|
||||
|
||||
// TBM 세션 저장
|
||||
async function saveTbmSession() {
|
||||
const workTypeId = document.getElementById('workTypeId').value;
|
||||
const taskId = document.getElementById('taskId').value;
|
||||
|
||||
const sessionData = {
|
||||
session_date: document.getElementById('sessionDate').value,
|
||||
leader_id: parseInt(document.getElementById('leaderId').value),
|
||||
project_id: document.getElementById('projectId').value || null,
|
||||
work_type_id: workTypeId ? parseInt(workTypeId) : null,
|
||||
task_id: taskId ? parseInt(taskId) : null,
|
||||
work_location: document.getElementById('workLocation').value || null,
|
||||
work_description: document.getElementById('workDescription').value || null,
|
||||
safety_notes: document.getElementById('safetyNotes').value || null,
|
||||
@@ -384,6 +463,11 @@ async function saveTbmSession() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sessionData.work_type_id || !sessionData.task_id) {
|
||||
showToast('공정과 작업을 선택해주세요.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await window.apiCall('/tbm/sessions', 'POST', sessionData);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user