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:
Hyungi Ahn
2026-01-26 15:06:43 +09:00
parent 9c636bf6ad
commit 7acb835c39
15 changed files with 1157 additions and 3 deletions

View File

@@ -0,0 +1,36 @@
/**
* 작업 테이블 생성 (공정=work_types에 속함)
*
* @param {import('knex').Knex} knex
*/
exports.up = function(knex) {
return knex.schema.createTable('tasks', function(table) {
table.increments('task_id').primary().comment('작업 ID');
table.integer('work_type_id').nullable().comment('공정 ID (work_types 참조)');
table.string('task_name', 255).notNullable().comment('작업명');
table.text('description').nullable().comment('작업 설명');
table.boolean('is_active').defaultTo(true).comment('활성화 여부');
table.timestamp('created_at').defaultTo(knex.fn.now()).comment('생성일시');
table.timestamp('updated_at').defaultTo(knex.fn.now()).comment('수정일시');
// 외래키 (work_types 테이블 참조)
table.foreign('work_type_id')
.references('id')
.inTable('work_types')
.onDelete('SET NULL')
.onUpdate('CASCADE');
// 인덱스
table.index('work_type_id');
table.index('is_active');
}).then(() => {
console.log('✅ tasks 테이블 생성 완료');
});
};
/**
* @param {import('knex').Knex} knex
*/
exports.down = function(knex) {
return knex.schema.dropTableIfExists('tasks');
};

View File

@@ -0,0 +1,42 @@
/**
* TBM 세션에 공정/작업 컬럼 추가
*
* @param {import('knex').Knex} knex
*/
exports.up = function(knex) {
return knex.schema.table('tbm_sessions', function(table) {
table.integer('work_type_id').nullable().comment('공정 ID (work_types 참조)');
table.integer('task_id').unsigned().nullable().comment('작업 ID (tasks 참조)');
// 외래키 추가
table.foreign('work_type_id')
.references('id')
.inTable('work_types')
.onDelete('SET NULL')
.onUpdate('CASCADE');
table.foreign('task_id')
.references('task_id')
.inTable('tasks')
.onDelete('SET NULL')
.onUpdate('CASCADE');
// 인덱스 추가
table.index('work_type_id');
table.index('task_id');
}).then(() => {
console.log('✅ tbm_sessions 테이블에 work_type_id, task_id 컬럼 추가 완료');
});
};
/**
* @param {import('knex').Knex} knex
*/
exports.down = function(knex) {
return knex.schema.table('tbm_sessions', function(table) {
table.dropForeign('work_type_id');
table.dropForeign('task_id');
table.dropColumn('work_type_id');
table.dropColumn('task_id');
});
};