From 25cca1482ebbf50c6e34953bf8833c873602eb64 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Mon, 19 Jan 2026 10:02:32 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9E=91=EC=97=85=EC=9E=90=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EA=B0=9C=EC=84=A0=20-=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EB=B3=B4=EA=B3=A0=EC=84=9C=20=ED=91=9C=EC=8B=9C/=ED=98=84?= =?UTF-8?q?=EC=9E=A5=EC=A7=81=20=EA=B5=AC=EB=B6=84/=ED=87=B4=EC=82=AC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 작업자 관리 페이지에 3가지 상태 관리 기능을 추가했습니다: 1. 작업 보고서 표시 여부 (관리자 등은 작업보고서에 표시 안함) 2. 현장직/사무직 구분 (사무직은 출퇴근 관리 불필요) 3. 퇴사 처리 (퇴사자 별도 표시) ## 주요 변경사항 ### 데이터베이스 - **마이그레이션**: 20260119095549_add_worker_display_fields.js - workers 테이블에 show_in_work_reports (BOOLEAN) 추가 - workers 테이블에 employment_status (ENUM: employed, resigned) 추가 ### 백엔드 - **workerModel.js**: create, update 함수에 새로운 필드 처리 로직 추가 ### 프론트엔드 - **worker-management.html**: 작업자 모달에 3가지 체크박스 추가 - 작업 보고서에 표시 - 현장직 (활성화) - 사무직과 구분 - 퇴사 처리 - **worker-management.js**: - 퇴사자 카드 렌더링 시 별도 스타일 적용 - 새 필드 값 로드 및 저장 처리 - **daily-work-report.js**: - 작업 보고서 작성 시 show_in_work_reports=true이고 퇴사하지 않은 작업자만 표시 ## 배포 절차 ```bash cd api.hyungi.net npm run db:migrate ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- ...0260119095549_add_worker_display_fields.js | 34 ++++++++++++ api.hyungi.net/models/workerModel.js | 22 ++++++-- web-ui/js/daily-work-report.js | 13 +++-- web-ui/js/worker-management.js | 52 ++++++++++++++----- .../pages/management/worker-management.html | 36 +++++++++++-- 5 files changed, 129 insertions(+), 28 deletions(-) create mode 100644 api.hyungi.net/db/migrations/20260119095549_add_worker_display_fields.js diff --git a/api.hyungi.net/db/migrations/20260119095549_add_worker_display_fields.js b/api.hyungi.net/db/migrations/20260119095549_add_worker_display_fields.js new file mode 100644 index 0000000..3a52b9e --- /dev/null +++ b/api.hyungi.net/db/migrations/20260119095549_add_worker_display_fields.js @@ -0,0 +1,34 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = async function(knex) { + await knex.schema.alterTable('workers', (table) => { + // 작업 보고서 표시 여부 (기본값: true, 작업자는 표시, 관리자는 선택 가능) + table.boolean('show_in_work_reports') + .defaultTo(true) + .notNullable() + .comment('작업 보고서에 표시 여부'); + + // 재직 상태 (employed: 재직, resigned: 퇴사) + table.enum('employment_status', ['employed', 'resigned']) + .defaultTo('employed') + .notNullable() + .comment('재직 상태'); + }); + + console.log('✅ workers 테이블에 show_in_work_reports, employment_status 컬럼 추가 완료'); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = async function(knex) { + await knex.schema.alterTable('workers', (table) => { + table.dropColumn('show_in_work_reports'); + table.dropColumn('employment_status'); + }); + + console.log('✅ workers 테이블에서 show_in_work_reports, employment_status 컬럼 삭제 완료'); +}; diff --git a/api.hyungi.net/models/workerModel.js b/api.hyungi.net/models/workerModel.js index ca519a0..f2ef1ab 100644 --- a/api.hyungi.net/models/workerModel.js +++ b/api.hyungi.net/models/workerModel.js @@ -19,14 +19,16 @@ const create = async (worker, callback) => { join_date = null, salary = null, annual_leave = null, - status = 'active' + status = 'active', + show_in_work_reports = true, + employment_status = 'employed' } = worker; const [result] = await db.query( `INSERT INTO workers - (worker_name, job_type, join_date, salary, annual_leave, status) - VALUES (?, ?, ?, ?, ?, ?)`, - [worker_name, job_type, formatDate(join_date), salary, annual_leave, status] + (worker_name, job_type, join_date, salary, annual_leave, status, show_in_work_reports, employment_status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [worker_name, job_type, formatDate(join_date), salary, annual_leave, status, show_in_work_reports, employment_status] ); callback(null, result.insertId); @@ -81,7 +83,9 @@ const update = async (worker, callback) => { status, join_date, salary, - annual_leave + annual_leave, + show_in_work_reports, + employment_status } = worker; // 업데이트할 필드만 동적으로 구성 @@ -112,6 +116,14 @@ const update = async (worker, callback) => { updates.push('annual_leave = ?'); values.push(annual_leave); } + if (show_in_work_reports !== undefined) { + updates.push('show_in_work_reports = ?'); + values.push(show_in_work_reports); + } + if (employment_status !== undefined) { + updates.push('employment_status = ?'); + values.push(employment_status); + } if (updates.length === 0) { callback(new Error('업데이트할 필드가 없습니다')); diff --git a/web-ui/js/daily-work-report.js b/web-ui/js/daily-work-report.js index aeb8d53..a91223f 100644 --- a/web-ui/js/daily-work-report.js +++ b/web-ui/js/daily-work-report.js @@ -206,16 +206,21 @@ async function loadData() { async function loadWorkers() { try { console.log('Workers API 호출 중... (통합 API 사용)'); - // 활성 작업자 1000명까지 조회 (서버 사이드 필터링 적용) - const data = await window.apiCall(`${window.API}/workers?status=active&limit=1000`); + // 모든 작업자 1000명까지 조회 + const data = await window.apiCall(`${window.API}/workers?limit=1000`); const allWorkers = Array.isArray(data) ? data : (data.data || data.workers || []); - // 활성화된 작업자만 필터링 + // 작업 보고서에 표시할 작업자만 필터링 + // 1. show_in_work_reports가 true + // 2. employment_status가 resigned가 아님 workers = allWorkers.filter(worker => { - return worker.status === 'active' || worker.is_active === 1 || worker.is_active === true; + const showInReports = worker.show_in_work_reports !== 0 && worker.show_in_work_reports !== false; + const notResigned = worker.employment_status !== 'resigned'; + return showInReports && notResigned; }); console.log(`✅ Workers 로드 성공: ${workers.length}명 (전체: ${allWorkers.length}명)`); + console.log(`📊 필터링 조건: show_in_work_reports=true, employment_status≠resigned`); } catch (error) { console.error('작업자 로딩 오류:', error); throw error; diff --git a/web-ui/js/worker-management.js b/web-ui/js/worker-management.js index 46d187f..781c0de 100644 --- a/web-ui/js/worker-management.js +++ b/web-ui/js/worker-management.js @@ -197,18 +197,23 @@ function renderWorkers() { const jobType = jobTypeMap[worker.job_type] || jobTypeMap['worker']; const isInactive = worker.status === 'inactive' || worker.is_active === 0 || worker.is_active === false; - + const isResigned = worker.employment_status === 'resigned'; + const showInWorkReports = worker.show_in_work_reports !== 0 && worker.show_in_work_reports !== false; + console.log('🎨 카드 렌더링:', { worker_id: worker.worker_id, worker_name: worker.worker_name, status: worker.status, is_active: worker.is_active, - isInactive: isInactive + isInactive: isInactive, + isResigned: isResigned, + showInWorkReports: showInWorkReports }); - + return ` -
- ${isInactive ? '
🚫 비활성화됨
' : ''} +
+ ${isResigned ? '
🚪 퇴사
' : + isInactive ? '
🏢 사무직
' : ''}
@@ -216,7 +221,8 @@ function renderWorkers() {

${worker.worker_name} - ${isInactive ? '(비활성)' : ''} + ${isResigned ? '(퇴사)' : + isInactive ? '(사무직)' : ''}

${jobType.icon} ${jobType.text} @@ -224,14 +230,15 @@ function renderWorkers() { ${worker.email ? `📧 ${worker.email}` : ''} ${worker.department ? `🏢 ${worker.department}` : ''} ${worker.hire_date ? `📅 입사: ${formatDate(worker.hire_date)}` : ''} - ${isInactive ? '⚠️ 작업보고서에서 숨김' : ''} + ${isResigned ? '⚠️ 퇴사 처리됨' : + !showInWorkReports ? '⚠️ 작업보고서에서 숨김' : ''}
-
+
- - 체크 해제 시 작업보고서 입력에서 숨겨집니다 + + +
+ + + + 체크 해제 시 일일 작업보고서 작성 시 이 작업자가 목록에 나타나지 않습니다 + + + + + + 체크: 현장직 (출퇴근 관리 필요) / 체크 해제: 사무직 (출퇴근 관리 불필요) + + + + + + 퇴사한 작업자로 표시됩니다. 작업자 목록에서 별도로 표시됩니다 + +