Files
tk-factory-services/system1-factory/web/pages/work/tbm.html
Hyungi Ahn 7637be33f3 feat: TBM 모바일 시스템 + 작업 분할/이동 + 권한 통합
TBM 시스템:
- 4단계 워크플로우 (draft→세부편집→완료→작업보고)
- 모바일 전용 TBM 페이지 (tbm-mobile.html) + 3단계 생성 위자드
- 작업자 작업 분할 (work_hours + split_seq)
- 작업자 이동 보내기/빼오기 (tbm_transfers 테이블)
- 생성 시 중복 배정 방지 (당일 배정 현황 조회)
- 데스크탑 TBM 페이지 세부편집 기능 추가

작업보고서:
- 모바일 전용 작업보고서 페이지 (report-create-mobile.html)
- TBM에서 사전 등록된 work_hours 자동 반영

권한 시스템:
- tkuser user_page_permissions 테이블과 system1 페이지 접근 연동
- pageAccessRoutes를 userRoutes보다 먼저 등록 (라우트 우선순위 수정)
- TKUSER_DEFAULT_ACCESS 폴백 추가 (개인→부서→기본값 3단계)
- 권한 캐시키 갱신 (userPageAccess_v2)

기타:
- app-init.js 캐시 버스팅 (v=5)
- iOS Safari touch-action: manipulation 적용
- KST 타임존 날짜 버그 수정 (toISOString UTC 이슈)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 07:46:21 +09:00

722 lines
36 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>TBM 관리 | (주)테크니컬코리아</title>
<link rel="stylesheet" href="/css/design-system.css">
<link rel="stylesheet" href="/css/common.css?v=2">
<link rel="stylesheet" href="/css/tbm.css?v=5">
<link rel="stylesheet" href="/css/mobile.css?v=1">
<link rel="icon" type="image/png" href="/img/favicon.png">
<!-- 최적화된 로딩: API 설정 → 앱 초기화 (병렬 컴포넌트 로딩) -->
<script src="/js/api-base.js"></script>
<script src="/js/app-init.js?v=5" defer></script>
<!-- instant.page: 링크 호버 시 페이지 프리로딩 -->
<script src="https://instant.page/5.2.0" type="module"></script>
</head>
<body>
<div class="work-report-container">
<!-- 네비게이션 바 -->
<div id="navbar-container"></div>
<!-- 메인 콘텐츠 -->
<main class="work-report-main">
<div class="tbm-container">
<!-- 페이지 헤더 -->
<div class="tbm-page-header">
<div class="tbm-title-section">
<h1 class="tbm-page-title">
<span class="tbm-page-title-icon">&#128736;</span>
TBM (Tool Box Meeting)
</h1>
<p class="tbm-page-description">아침 안전 회의 및 팀 구성 관리</p>
</div>
</div>
<!-- TBM 탭 메뉴 -->
<div class="tbm-tab-menu">
<button class="tbm-tab-btn active" data-tab="tbm-input" onclick="switchTbmTab('tbm-input')">
<span class="tbm-tab-icon">&#128221;</span>
TBM 입력
</button>
<button class="tbm-tab-btn" data-tab="tbm-manage" onclick="switchTbmTab('tbm-manage')">
<span class="tbm-tab-icon">&#128202;</span>
TBM 관리
</button>
</div>
<!-- TBM 입력 탭 -->
<div id="tbm-input-tab" class="tbm-tab-content active">
<div class="tbm-section">
<div class="tbm-section-header">
<h2 class="tbm-section-title">
<span>&#128197;</span>
오늘의 TBM
</h2>
<div class="tbm-section-actions">
<button class="tbm-btn tbm-btn-primary" onclick="openNewTbmModal()">
<span class="tbm-btn-icon">+</span>
새 TBM 시작
</button>
</div>
</div>
<div class="tbm-stats-bar">
<span class="tbm-stat-item">
<span class="tbm-stat-label">오늘 등록</span>
<span class="tbm-stat-value highlight" id="todayTotalSessions">0</span>
<span class="tbm-stat-label"></span>
</span>
<span class="tbm-stat-item">
<span class="tbm-stat-label">완료</span>
<span class="tbm-stat-value success" id="todayCompletedSessions">0</span>
<span class="tbm-stat-label"></span>
</span>
<span class="tbm-stat-item">
<span class="tbm-stat-label">진행중</span>
<span class="tbm-stat-value warning" id="todayActiveSessions">0</span>
<span class="tbm-stat-label"></span>
</span>
</div>
<div class="tbm-card-grid" id="todayTbmGrid">
<!-- 오늘의 TBM 세션 카드들이 여기에 동적으로 생성됩니다 -->
</div>
<!-- Empty State -->
<div class="tbm-empty-state" id="todayEmptyState" style="display: none;">
<div class="tbm-empty-icon">&#128203;</div>
<h3 class="tbm-empty-title">오늘 등록된 TBM이 없습니다</h3>
<p class="tbm-empty-description">"새 TBM 시작" 버튼을 눌러 오늘의 TBM을 시작해보세요.</p>
<button class="tbm-btn tbm-btn-primary" onclick="openNewTbmModal()">
<span class="tbm-btn-icon">+</span>
첫 TBM 시작하기
</button>
</div>
</div>
</div>
<!-- TBM 관리 탭 -->
<div id="tbm-manage-tab" class="tbm-tab-content">
<div class="tbm-section">
<div class="tbm-section-header">
<h2 class="tbm-section-title">
<span>&#128218;</span>
TBM 기록
</h2>
<div class="tbm-section-actions">
<button class="tbm-btn tbm-btn-secondary" onclick="loadMoreTbmDays()" id="loadMoreBtn">
더 보기
</button>
</div>
</div>
<div class="tbm-stats-bar">
<span class="tbm-stat-item">
<span class="tbm-stat-label"></span>
<span class="tbm-stat-value" id="totalSessions">0</span>
<span class="tbm-stat-label"></span>
</span>
<span class="tbm-stat-item">
<span class="tbm-stat-label">완료</span>
<span class="tbm-stat-value success" id="completedSessions">0</span>
<span class="tbm-stat-label"></span>
</span>
<span class="tbm-stat-item" id="viewModeIndicator" style="display: none;">
<span class="tbm-stat-value" id="viewModeText">내 TBM만</span>
</span>
</div>
<!-- 날짜별 그룹 컨테이너 -->
<div class="tbm-section-body" id="tbmDateGroupsContainer">
<!-- 날짜별 TBM 그룹이 여기에 동적으로 생성됩니다 -->
</div>
<!-- Empty State -->
<div class="tbm-empty-state" id="emptyState" style="display: none;">
<div class="tbm-empty-icon">&#128218;</div>
<h3 class="tbm-empty-title">등록된 TBM 세션이 없습니다</h3>
<p class="tbm-empty-description">TBM 입력 탭에서 새로운 TBM을 시작해보세요.</p>
</div>
</div>
</div>
</div>
</main>
<!-- TBM 생성 모달 (간소화) -->
<div id="tbmModal" class="tbm-modal-overlay" style="display: none;">
<div class="tbm-modal" style="max-width: 800px;">
<div class="tbm-modal-header">
<h2 class="tbm-modal-title" id="modalTitle">
<span>&#128221;</span>
새 TBM 시작
</h2>
<button class="tbm-modal-close" onclick="closeTbmModal()">×</button>
</div>
<div class="tbm-modal-body">
<form id="tbmForm" onsubmit="event.preventDefault(); saveTbmSession();">
<input type="hidden" id="sessionId">
<!-- 기본 정보 섹션 -->
<div class="tbm-form-section">
<h3 class="tbm-form-section-title">
<span>&#128197;</span>
기본 정보
</h3>
<div class="tbm-form-row">
<div class="tbm-form-group">
<label class="tbm-form-label">TBM 날짜<span class="tbm-form-required">*</span></label>
<div class="tbm-form-input-readonly" id="sessionDateDisplay">-</div>
<input type="hidden" id="sessionDate">
</div>
<div class="tbm-form-group">
<label class="tbm-form-label">입력자<span class="tbm-form-required">*</span></label>
<div class="tbm-form-input-readonly" id="leaderName">-</div>
<input type="hidden" id="leaderId">
</div>
</div>
<div class="tbm-form-row">
<div class="tbm-form-group">
<label class="tbm-form-label">프로젝트</label>
<select id="newTbmProjectId" class="tbm-form-input">
<option value="">선택 안함</option>
</select>
</div>
<div class="tbm-form-group">
<label class="tbm-form-label">공정<span class="tbm-form-required">*</span></label>
<select id="newTbmWorkTypeId" class="tbm-form-input" required>
<option value="">공정 선택...</option>
</select>
</div>
</div>
</div>
<!-- 작업자 선택 섹션 -->
<div class="tbm-form-section">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
<h3 class="tbm-form-section-title" style="margin: 0; border: 0; padding: 0;">
<span>&#128101;</span>
작업자 선택
<span id="newTbmWorkerCount" style="color: #3b82f6; font-size: 0.875rem;">(0명)</span>
</h3>
<div style="display: flex; gap: 0.5rem;">
<button type="button" class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="selectAllNewTbmWorkers()">전체 선택</button>
<button type="button" class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="deselectAllNewTbmWorkers()">전체 해제</button>
</div>
</div>
<div id="newTbmWorkerGrid" class="tbm-worker-select-grid">
<!-- 작업자 체크박스 그리드가 여기에 동적으로 생성됩니다 -->
</div>
<div class="tbm-alert tbm-alert-info" style="margin-top: 1rem;">
<span class="tbm-alert-icon">&#128161;</span>
<div class="tbm-alert-content">
<div class="tbm-alert-text">저장 후 카드를 클릭하면 작업자별 <strong>작업/작업장</strong>을 입력할 수 있습니다.</div>
</div>
</div>
</div>
</form>
</div>
<div class="tbm-modal-footer">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeTbmModal()">취소</button>
<button type="button" class="tbm-btn tbm-btn-primary" onclick="saveTbmSession()">
<span class="tbm-btn-icon">&#10003;</span>
저장하기
</button>
</div>
</div>
</div>
<!-- 일괄 설정 모달 -->
<div id="bulkSettingModal" class="tbm-modal-overlay" style="display: none; z-index: 1101;">
<div class="tbm-modal" style="max-width: 700px;">
<div class="tbm-modal-header">
<h2 class="tbm-modal-title">
<span>&#9881;</span>
일괄 설정
</h2>
<button class="tbm-modal-close" onclick="closeBulkSettingModal()">×</button>
</div>
<div class="tbm-modal-body">
<div class="tbm-alert tbm-alert-info">
<span class="tbm-alert-icon">&#128161;</span>
<div class="tbm-alert-content">
<div class="tbm-alert-title">일괄 설정</div>
<div class="tbm-alert-text">선택한 작업자들의 <strong>첫 번째 작업 라인</strong>에 동일한 정보가 적용됩니다.</div>
</div>
</div>
<!-- 작업자 선택 -->
<div class="tbm-form-section">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem;">
<label class="tbm-form-label">적용할 작업자 선택<span class="tbm-form-required">*</span></label>
<div style="display: flex; gap: 0.25rem;">
<button type="button" class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="selectAllForBulk()">전체</button>
<button type="button" class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="deselectAllForBulk()">해제</button>
</div>
</div>
<div id="bulkWorkerSelection" class="tbm-worker-select-grid" style="max-height: 180px;">
<!-- 작업자 체크박스들이 여기에 생성됩니다 -->
</div>
</div>
<div class="tbm-form-section" style="border-top: 1px solid #e2e8f0; padding-top: 1.5rem;">
<h3 class="tbm-form-section-title" style="border: 0; padding: 0; margin-bottom: 1rem;">
<span>&#128736;</span>
적용할 작업 정보
</h3>
<div class="tbm-form-group">
<label class="tbm-form-label">프로젝트</label>
<button type="button" id="bulkProjectBtn" onclick="openBulkItemSelect('project')" class="tbm-select-btn">
프로젝트 선택
<span class="tbm-select-arrow">&#9660;</span>
</button>
<input type="hidden" id="bulkProjectId">
</div>
<div class="tbm-form-row">
<div class="tbm-form-group">
<label class="tbm-form-label">공정<span class="tbm-form-required">*</span></label>
<button type="button" id="bulkWorkTypeBtn" onclick="openBulkItemSelect('workType')" class="tbm-select-btn">
공정 선택
<span class="tbm-select-arrow">&#9660;</span>
</button>
<input type="hidden" id="bulkWorkTypeId">
</div>
<div class="tbm-form-group">
<label class="tbm-form-label">작업<span class="tbm-form-required">*</span></label>
<button type="button" id="bulkTaskBtn" onclick="openBulkItemSelect('task')" class="tbm-select-btn" disabled>
작업 선택
<span class="tbm-select-arrow">&#9660;</span>
</button>
<input type="hidden" id="bulkTaskId">
</div>
</div>
<div class="tbm-form-group">
<label class="tbm-form-label">작업장<span class="tbm-form-required">*</span></label>
<button type="button" id="bulkWorkplaceBtn" onclick="openBulkWorkplaceSelect()" class="tbm-select-btn">
작업장 선택
<span class="tbm-select-arrow">&#9660;</span>
</button>
<input type="hidden" id="bulkWorkplaceCategoryId">
<input type="hidden" id="bulkWorkplaceId">
</div>
</div>
</div>
<div class="tbm-modal-footer">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeBulkSettingModal()">취소</button>
<button type="button" class="tbm-btn tbm-btn-primary" onclick="applyBulkSettings()">
<span class="tbm-btn-icon">&#10003;</span>
선택한 작업자에 적용
</button>
</div>
</div>
</div>
<!-- 작업자 선택 모달 -->
<div id="workerSelectionModal" class="tbm-modal-overlay" style="display: none; z-index: 1101;">
<div class="tbm-modal" style="max-width: 800px;">
<div class="tbm-modal-header">
<h2 class="tbm-modal-title">
<span>&#128101;</span>
작업자 선택
</h2>
<button class="tbm-modal-close" onclick="closeWorkerSelectionModal()">×</button>
</div>
<div class="tbm-modal-body">
<div style="margin-bottom: 1rem; display: flex; gap: 0.5rem;">
<button type="button" class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="selectAllWorkersInModal()">전체 선택</button>
<button type="button" class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="deselectAllWorkersInModal()">전체 해제</button>
</div>
<div id="workerCardGrid" class="tbm-worker-select-grid">
<!-- 작업자 카드들이 여기에 생성됩니다 -->
</div>
</div>
<div class="tbm-modal-footer">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeWorkerSelectionModal()">취소</button>
<button type="button" class="tbm-btn tbm-btn-primary" onclick="confirmWorkerSelection()">
<span class="tbm-btn-icon">&#10003;</span>
선택 완료
</button>
</div>
</div>
</div>
<!-- 항목 선택 모달 (프로젝트/공정/작업 선택용) -->
<div id="itemSelectModal" class="tbm-modal-overlay" style="display: none; z-index: 1102;">
<div class="tbm-modal" style="max-width: 600px;">
<div class="tbm-modal-header">
<h2 class="tbm-modal-title" id="itemSelectModalTitle">항목 선택</h2>
<button class="tbm-modal-close" onclick="closeItemSelectModal()">×</button>
</div>
<div class="tbm-modal-body">
<div id="itemSelectList" class="tbm-item-list">
<!-- 선택 항목들이 여기에 생성됩니다 -->
</div>
</div>
<div class="tbm-modal-footer">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeItemSelectModal()">취소</button>
</div>
</div>
</div>
<!-- 작업장 선택 모달 (2단계: 공장 → 작업장) -->
<div id="workplaceSelectModal" class="tbm-modal-overlay" style="display: none; z-index: 1102;">
<div class="tbm-modal" style="max-width: 1000px;">
<div class="tbm-modal-header">
<h2 class="tbm-modal-title">
<span>&#127981;</span>
작업장 선택
</h2>
<button class="tbm-modal-close" onclick="closeWorkplaceSelectModal()">×</button>
</div>
<div class="tbm-modal-body">
<!-- 1단계: 공장 선택 -->
<div class="tbm-form-section">
<h3 class="tbm-form-section-title">
<span style="background: #3b82f6; color: white; width: 24px; height: 24px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 0.8rem;">1</span>
공장 선택
</h3>
<div id="categoryList" style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
<!-- 공장 카테고리 버튼들이 여기에 생성됩니다 -->
</div>
</div>
<!-- 2단계: 작업장 선택 (지도 기본 + 리스트 토글) -->
<div id="workplaceSelectionArea" style="display: none;">
<div class="tbm-form-section">
<h3 class="tbm-form-section-title">
<span style="background: #3b82f6; color: white; width: 24px; height: 24px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 0.8rem;">2</span>
작업장 선택
</h3>
<!-- 지도 기반 선택 (기본 표시) -->
<div id="layoutMapArea" style="display: none; padding: 1rem; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 10px;">
<div style="font-size: 0.875rem; color: #64748b; margin-bottom: 0.75rem;">
지도에서 작업장을 클릭하여 선택하세요
</div>
<div class="tbm-workplace-map-container">
<canvas id="workplaceMapCanvas"></canvas>
</div>
<button type="button" class="landscape-trigger-btn" id="landscapeTriggerBtn" onclick="openLandscapeMap()" style="display:none;">
&#128250; 전체화면 지도
</button>
</div>
<!-- 리스트 기반 선택 (모바일에서 토글) -->
<div style="margin-top: 0.75rem;">
<button type="button" class="tbm-btn tbm-btn-secondary tbm-btn-sm"
onclick="toggleWorkplaceList()" id="toggleListBtn" style="display: none;">
리스트로 선택
</button>
<div id="workplaceListSection">
<div id="workplaceList" class="tbm-item-list">
<div style="color: #94a3b8; text-align: center; padding: 2rem;">
공장을 먼저 선택해주세요
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tbm-modal-footer">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeWorkplaceSelectModal()">취소</button>
<button type="button" class="tbm-btn tbm-btn-primary" onclick="confirmWorkplaceSelection()" id="confirmWorkplaceBtn" disabled>
<span class="tbm-btn-icon">&#10003;</span>
선택 완료
</button>
</div>
</div>
</div>
<!-- 팀 구성 모달 -->
<div id="teamModal" class="tbm-modal-overlay" style="display: none;">
<div class="tbm-modal" style="max-width: 900px;">
<div class="tbm-modal-header">
<h2 class="tbm-modal-title">
<span>&#128101;</span>
팀 구성
</h2>
<button class="tbm-modal-close" onclick="closeTeamModal()">×</button>
</div>
<div class="tbm-modal-body">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
<h3 class="tbm-form-section-title" style="margin: 0; border: 0; padding: 0;">작업자 선택</h3>
<div style="display: flex; gap: 0.5rem;">
<button class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="selectAllWorkers()">전체 선택</button>
<button class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="deselectAllWorkers()">전체 해제</button>
</div>
</div>
<div id="workerSelectionGrid" class="tbm-worker-select-grid">
<!-- 작업자 체크박스 목록이 여기에 생성됩니다 -->
</div>
<div style="margin-top: 1.5rem;">
<h3 class="tbm-form-section-title">
선택된 팀원 <span id="selectedCount" style="color: #3b82f6;">0</span>
</h3>
<div id="selectedWorkersList" style="display: flex; flex-wrap: wrap; gap: 0.5rem; min-height: 50px; padding: 0.75rem; background: #f8fafc; border-radius: 10px; border: 1px solid #e2e8f0;">
<p style="margin: 0; color: #94a3b8; font-size: 0.875rem;">작업자를 선택해주세요</p>
</div>
</div>
</div>
<div class="tbm-modal-footer">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeTeamModal()">취소</button>
<button type="button" class="tbm-btn tbm-btn-primary" onclick="saveTeamComposition()">
<span class="tbm-btn-icon">&#10003;</span>
팀 구성 완료
</button>
</div>
</div>
</div>
<!-- 안전 체크리스트 모달 -->
<div id="safetyModal" class="tbm-modal-overlay" style="display: none;">
<div class="tbm-modal" style="max-width: 700px;">
<div class="tbm-modal-header">
<h2 class="tbm-modal-title">
<span>&#128737;</span>
안전 체크리스트
</h2>
<button class="tbm-modal-close" onclick="closeSafetyModal()">×</button>
</div>
<div class="tbm-modal-body">
<div id="safetyChecklistContainer" class="tbm-safety-list">
<!-- 안전 체크리스트가 여기에 생성됩니다 -->
</div>
</div>
<div class="tbm-modal-footer">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeSafetyModal()">취소</button>
<button type="button" class="tbm-btn tbm-btn-success" onclick="saveSafetyChecklist()">
<span class="tbm-btn-icon">&#10003;</span>
안전 체크 완료
</button>
</div>
</div>
</div>
<!-- TBM 완료 모달 -->
<div id="completeModal" class="tbm-modal-overlay" style="display: none;">
<div class="tbm-modal" style="max-width: 500px;">
<div class="tbm-modal-header">
<h2 class="tbm-modal-title">
<span>&#10003;</span>
TBM 완료
</h2>
<button class="tbm-modal-close" onclick="closeCompleteModal()">×</button>
</div>
<div class="tbm-modal-body">
<div class="tbm-alert tbm-alert-warning">
<span class="tbm-alert-icon">&#9888;</span>
<div class="tbm-alert-content">
<div class="tbm-alert-title">TBM 완료 확인</div>
<div class="tbm-alert-text">완료 후에는 수정할 수 없습니다.</div>
</div>
</div>
<div class="tbm-form-group" style="margin-top: 1.5rem;">
<label class="tbm-form-label">종료 시간</label>
<input type="time" id="endTime" class="tbm-form-input">
</div>
<div class="tbm-form-group" style="margin-top: 1rem;">
<label class="tbm-form-label">작업자 근태</label>
<div id="completeAttendanceList" style="max-height: 300px; overflow-y: auto; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 0.5rem;">
<div style="text-align:center; color:#9ca3af; padding:1rem;">로딩 중...</div>
</div>
</div>
</div>
<div class="tbm-modal-footer">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeCompleteModal()">취소</button>
<button type="button" class="tbm-btn tbm-btn-success" id="completeModalBtn" onclick="completeTbmSession()">
<span class="tbm-btn-icon">&#10003;</span>
완료
</button>
</div>
</div>
</div>
<!-- 작업 인계 모달 -->
<div id="handoverModal" class="tbm-modal-overlay" style="display: none;">
<div class="tbm-modal" style="max-width: 600px;">
<div class="tbm-modal-header">
<h2 class="tbm-modal-title">
<span>&#128073;</span>
작업 인계
</h2>
<button class="tbm-modal-close" onclick="closeHandoverModal()">×</button>
</div>
<div class="tbm-modal-body">
<form id="handoverForm">
<input type="hidden" id="handoverSessionId">
<div class="tbm-form-group">
<label class="tbm-form-label">인계 사유<span class="tbm-form-required">*</span></label>
<select id="handoverReason" class="tbm-form-input" required>
<option value="">사유 선택...</option>
<option value="half_day">반차</option>
<option value="early_leave">조퇴</option>
<option value="emergency">긴급 상황</option>
<option value="other">기타</option>
</select>
</div>
<div class="tbm-form-group">
<label class="tbm-form-label">인수자 (다음 팀장)<span class="tbm-form-required">*</span></label>
<select id="toLeaderId" class="tbm-form-input" required>
<option value="">인수자 선택...</option>
</select>
</div>
<div class="tbm-form-row">
<div class="tbm-form-group">
<label class="tbm-form-label">인계 날짜<span class="tbm-form-required">*</span></label>
<input type="date" id="handoverDate" class="tbm-form-input" required>
</div>
<div class="tbm-form-group">
<label class="tbm-form-label">인계 시간</label>
<input type="time" id="handoverTime" class="tbm-form-input">
</div>
</div>
<div class="tbm-form-group">
<label class="tbm-form-label">인계 내용</label>
<textarea id="handoverNotes" class="tbm-form-input" rows="4" placeholder="인수자에게 전달할 내용을 입력하세요" style="resize: vertical;"></textarea>
</div>
<div class="tbm-form-group">
<label class="tbm-form-label" style="margin-bottom: 0.75rem;">인계할 팀원 선택</label>
<div id="handoverTeamList" style="max-height: 200px; overflow-y: auto; border: 1px solid #e2e8f0; border-radius: 10px; padding: 0.75rem; background: #f8fafc;">
<!-- 팀원 체크박스 목록 -->
</div>
</div>
</form>
</div>
<div class="tbm-modal-footer">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeHandoverModal()">취소</button>
<button type="button" class="tbm-btn tbm-btn-primary" onclick="saveHandover()">
<span class="tbm-btn-icon">&#128073;</span>
인계 요청
</button>
</div>
</div>
</div>
<!-- TBM 상세보기 모달 -->
<div id="detailModal" class="tbm-modal-overlay" style="display: none;">
<div class="tbm-modal" style="max-width: 900px;">
<div class="tbm-modal-header">
<h2 class="tbm-modal-title">
<span>&#128203;</span>
TBM 상세 정보
</h2>
<button class="tbm-modal-close" onclick="closeDetailModal()">×</button>
</div>
<div class="tbm-modal-body">
<!-- 세션 기본 정보 -->
<div class="tbm-form-section">
<h3 class="tbm-form-section-title">
<span>&#128197;</span>
기본 정보
</h3>
<div id="detailBasicInfo" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem;">
<!-- 동적 생성 -->
</div>
</div>
<!-- 팀 구성 -->
<div class="tbm-form-section">
<h3 class="tbm-form-section-title">
<span>&#128101;</span>
팀 구성
</h3>
<div id="detailTeamMembers" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.75rem;">
<!-- 동적 생성 -->
</div>
</div>
<!-- 안전 체크 -->
<div class="tbm-form-section">
<h3 class="tbm-form-section-title">
<span>&#128737;</span>
안전 체크리스트
</h3>
<div id="detailSafetyChecks">
<!-- 동적 생성 -->
</div>
</div>
</div>
<div class="tbm-modal-footer" id="detailModalFooter">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeDetailModal()">닫기</button>
</div>
</div>
</div>
<!-- 가로모드 전체화면 지도 오버레이 -->
<div id="landscapeOverlay" class="landscape-overlay" style="display:none;">
<div id="landscapeInner" class="landscape-inner">
<div class="landscape-header">
<h3>&#127981; 작업장 선택</h3>
<button type="button" class="landscape-close-btn" onclick="closeLandscapeMap()">×</button>
</div>
<div class="landscape-canvas-wrap">
<canvas id="landscapeCanvas"></canvas>
</div>
</div>
</div>
<!-- 토스트 알림 -->
<div class="toast-container" id="toastContainer"></div>
</div>
<!-- TBM 모듈 (리팩토링된 구조) -->
<script src="/js/tbm/state.js?v=1"></script>
<script src="/js/tbm/utils.js?v=1"></script>
<script src="/js/tbm/api.js?v=1"></script>
<!-- 기존 UI 로직 (점진적 마이그레이션) -->
<script type="module" src="/js/tbm.js?v=10"></script>
<!-- 모바일 하단 네비게이션 -->
<div id="mobile-nav-container"></div>
<script>
if (window.innerWidth <= 768) {
fetch('/components/mobile-nav.html')
.then(r => r.text())
.then(html => {
document.getElementById('mobile-nav-container').innerHTML = html;
const scripts = document.getElementById('mobile-nav-container').querySelectorAll('script');
scripts.forEach(s => eval(s.textContent));
});
}
</script>
</body>
</html>