Files
TK-FB-Project/web-ui/pages/admin/workers.html
Hyungi Ahn 4d0c4c0801 feat: TBM 시스템 구축 및 페이지 권한 관리 기능 추가
## 주요 변경사항

### 1. TBM (Tool Box Meeting) 시스템 구축
- **데이터베이스 스키마** (5개 테이블 생성)
  - tbm_sessions: TBM 세션 관리
  - tbm_team_assignments: 팀 구성 관리
  - tbm_safety_checks: 안전 체크리스트 마스터 (17개 항목)
  - tbm_safety_records: 안전 체크 기록
  - team_handovers: 작업 인계 관리

- **API 엔드포인트** (17개)
  - TBM 세션 CRUD
  - 팀 구성 관리
  - 안전 체크리스트
  - 작업 인계
  - 통계 및 리포트

- **프론트엔드**
  - TBM 관리 페이지 (/pages/work/tbm.html)
  - 모달 기반 UI (세션 생성, 팀 구성, 안전 체크)

### 2. 페이지 권한 관리 시스템
- 페이지별 접근 권한 설정 기능
- 관리자 페이지 (/pages/admin/page-access.html)
- 사용자별 페이지 권한 부여/회수
- TBM 페이지 등록 및 권한 연동

### 3. 네비게이션 role 표시 버그 수정
- load-navbar.js: case-insensitive role 매칭 적용
- JWT의 "Admin" role이 "관리자"로 정상 표시
- admin-only 메뉴 항목 정상 표시

### 4. 대시보드 개선
- 작업 현황 테이블 가독성 향상
- 고대비 색상 및 명확한 구분선 적용
- 이모지 제거 및 SVG 아이콘 적용

### 5. 문서화
- TBM 배포 가이드 작성 (docs/TBM_DEPLOYMENT_GUIDE.md)
- 데이터베이스 스키마 상세 기록
- 배포 절차 및 체크리스트 제공

## 기술 스택
- Backend: Node.js, Express, MySQL
- Frontend: Vanilla JavaScript, HTML5, CSS3
- Database: MySQL (InnoDB)

## 파일 변경사항

### 신규 파일
- api.hyungi.net/db/migrations/20260120000000_create_tbm_system.js
- api.hyungi.net/db/migrations/20260120000001_add_tbm_page.js
- api.hyungi.net/models/tbmModel.js
- api.hyungi.net/models/pageAccessModel.js
- api.hyungi.net/controllers/tbmController.js
- api.hyungi.net/controllers/pageAccessController.js
- api.hyungi.net/routes/tbmRoutes.js
- web-ui/pages/work/tbm.html
- web-ui/pages/admin/page-access.html
- web-ui/js/page-access-management.js
- docs/TBM_DEPLOYMENT_GUIDE.md

### 수정 파일
- api.hyungi.net/config/routes.js (TBM 라우트 추가)
- web-ui/js/load-navbar.js (role 매칭 버그 수정)
- web-ui/pages/admin/workers.html (HTML 구조 수정)
- web-ui/pages/dashboard.html (이모지 제거)
- web-ui/css/design-system.css (색상 팔레트 추가)
- web-ui/css/modern-dashboard.css (가독성 개선)
- web-ui/js/modern-dashboard.js (SVG 아이콘 적용)

## 배포 시 주의사항
⚠️ 본 서버 배포 시 반드시 마이그레이션 실행 필요:
```bash
npm run db:migrate
```

상세한 배포 절차는 docs/TBM_DEPLOYMENT_GUIDE.md 참조

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-20 15:38:17 +09:00

222 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>작업자 관리 | (주)테크니컬코리아</title>
<link rel="stylesheet" href="/css/design-system.css">
<link rel="stylesheet" href="/css/common.css?v=2">
<link rel="stylesheet" href="/css/project-management.css?v=3">
<link rel="icon" type="image/png" href="/img/favicon.png">
<script src="/js/auth-check.js?v=1" defer></script>
<script type="module" src="/js/api-config.js?v=3"></script>
</head>
<body>
<div class="work-report-container">
<!-- 네비게이션 바 -->
<div id="navbar-container"></div>
<!-- 메인 콘텐츠 -->
<main class="work-report-main">
<!-- 뒤로가기 버튼 -->
<a href="/pages/admin/index.html" class="back-button">
← 작업관리로 돌아가기
</a>
<div class="dashboard-main">
<div class="page-header">
<div class="page-title-section">
<h1 class="page-title">
<span class="title-icon">👥</span>
작업자 관리
</h1>
<p class="page-description">작업자 등록, 수정, 삭제 및 기본 정보를 관리합니다</p>
</div>
<div class="page-actions">
<button class="btn btn-primary" onclick="openWorkerModal()">
<span class="btn-icon"></span>
새 작업자 등록
</button>
<button class="btn btn-secondary" onclick="refreshWorkerList()">
<span class="btn-icon">🔄</span>
새로고침
</button>
</div>
<!-- 검색 및 필터 -->
<div class="search-section">
<div class="search-bar">
<input type="text" id="searchInput" class="search-input" placeholder="작업자명, 직책, 전화번호로 검색...">
<button class="search-btn" onclick="searchWorkers()">
<span>🔍</span>
</button>
</div>
<div class="filter-options">
<select id="jobTypeFilter" class="filter-select" onchange="filterWorkers()">
<option value="">모든 직책</option>
<option value="leader">그룹장</option>
<option value="worker">작업자</option>
<option value="admin">관리자</option>
</select>
<select id="statusFilter" class="filter-select" onchange="filterWorkers()">
<option value="">모든 상태</option>
<option value="active">활성</option>
<option value="inactive">비활성</option>
</select>
<select id="sortBy" class="filter-select" onchange="sortWorkers()">
<option value="created_at">등록일순</option>
<option value="worker_name">이름순</option>
<option value="job_type">직책순</option>
</select>
</div>
</div>
<!-- 작업자 목록 -->
<div class="projects-section">
<div class="section-header">
<h2 class="section-title">등록된 작업자</h2>
<div class="project-stats">
<span class="stat-item active-stat" onclick="filterByStatus('active')" title="활성 작업자만 보기">
<span class="stat-icon">🟢</span>
활성 <span id="activeWorkers">0</span>
</span>
<span class="stat-item inactive-stat" onclick="filterByStatus('inactive')" title="비활성 작업자만 보기">
<span class="stat-icon">🔴</span>
비활성 <span id="inactiveWorkers">0</span>
</span>
<span class="stat-item total-stat" onclick="filterByStatus('all')" title="전체 작업자 보기">
<span class="stat-icon">📊</span>
<span id="totalWorkers">0</span>
</span>
</div>
</div>
<div class="projects-grid" id="workersGrid">
<!-- 작업자 카드들이 여기에 동적으로 생성됩니다 -->
</div>
<!-- Empty State -->
<div class="empty-state" id="emptyState" style="display: none;">
<div class="empty-icon">👥</div>
<h3>등록된 작업자가 없습니다.</h3>
<p>"새 작업자 등록" 버튼을 눌러 작업자를 등록해보세요.</p>
<button class="btn btn-primary" onclick="openWorkerModal()">
첫 작업자 등록하기
</button>
</div>
</div>
</div>
</main>
<!-- 작업자 추가/수정 모달 -->
<div id="workerModal" class="modal-overlay" style="display: none;">
<div class="modal-container">
<div class="modal-header">
<h2 id="modalTitle">새 작업자 등록</h2>
<button class="modal-close-btn" onclick="closeWorkerModal()">×</button>
</div>
<div class="modal-body">
<form id="workerForm" onsubmit="event.preventDefault(); saveWorker();">
<input type="hidden" id="workerId">
<div class="form-row">
<div class="form-group">
<label class="form-label">작업자명 *</label>
<input type="text" id="workerName" class="form-control" placeholder="작업자 이름을 입력하세요" required>
</div>
<div class="form-group">
<label class="form-label">직책</label>
<select id="jobType" class="form-control">
<option value="worker">👷 작업자</option>
<option value="leader">👨‍💼 그룹장</option>
<option value="admin">👨‍💻 관리자</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">전화번호</label>
<input type="tel" id="phoneNumber" class="form-control" placeholder="010-0000-0000">
</div>
<div class="form-group">
<label class="form-label">이메일</label>
<input type="email" id="email" class="form-control" placeholder="example@company.com">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">입사일</label>
<input type="date" id="hireDate" class="form-control">
</div>
<div class="form-group">
<label class="form-label">부서</label>
<input type="text" id="department" class="form-control" placeholder="소속 부서">
</div>
</div>
<div class="form-group">
<label class="form-label">비고</label>
<textarea id="notes" class="form-control" rows="3" placeholder="추가 정보나 특이사항을 입력하세요"></textarea>
</div>
<!-- 상태 관리 섹션 -->
<div class="form-group">
<label class="form-label" style="font-weight: 600; margin-bottom: 0.75rem; display: block;">상태 관리</label>
<div style="display: flex; flex-direction: column; gap: 0.75rem;">
<!-- 계정 생성/연동 -->
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" id="hasAccount" style="margin: 0; cursor: pointer;">
<span>🔐 계정 생성/연동</span>
</label>
<small style="color: #6b7280; font-size: 0.75rem; margin-top: -0.5rem; margin-left: 1.5rem;">
체크 시 로그인 계정이 자동 생성됩니다 (나의 대시보드, 연차/출퇴근 관리 가능)
</small>
<!-- 현장직/사무직 구분 -->
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" id="isActive" checked style="margin: 0; cursor: pointer;">
<span>🏭 현장직 (활성화)</span>
</label>
<small style="color: #6b7280; font-size: 0.75rem; margin-top: -0.5rem; margin-left: 1.5rem;">
체크: 현장직 (출퇴근 관리 필요) / 체크 해제: 사무직 (출퇴근 관리 불필요)
</small>
<!-- 퇴사 처리 -->
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" id="isResigned" style="margin: 0; cursor: pointer;">
<span style="color: #ef4444;">🚪 퇴사 처리</span>
</label>
<small style="color: #ef4444; font-size: 0.75rem; margin-top: -0.5rem; margin-left: 1.5rem;">
퇴사한 작업자로 표시됩니다. 작업 보고서에서 제외됩니다
</small>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="closeWorkerModal()">취소</button>
<button type="button" class="btn btn-danger" id="deleteWorkerBtn" onclick="deleteWorker()" style="display: none;">
🗑️ 삭제
</button>
<button type="button" class="btn btn-primary" onclick="saveWorker()">
💾 저장
</button>
</div>
</div>
</div>
</div>
<script type="module" src="/js/load-navbar.js?v=5"></script>
<script type="module" src="/js/worker-management.js?v=7"></script>
</body>
</html>