Files
TK-FB-Project/web-ui/pages/work/tbm.html
Hyungi Ahn 170adcc149 refactor: 코드 관리 페이지 삭제 및 프론트엔드 모듈화
- codes.html, code-management.js 삭제 (tasks.html에서 동일 기능 제공)
- 사이드바에서 코드 관리 링크 제거
- daily-work-report, tbm, workplace-management JS 모듈 분리
- common/security.js 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 06:42:12 +09:00

673 lines
33 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=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=2" 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: 1000px;">
<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>
<!-- 작업자 및 작업 정보 섹션 -->
<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>
작업자 및 작업 정보
</h3>
<div style="display: flex; gap: 0.5rem;">
<button type="button" class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="openBulkSettingModal()">
일괄 설정
</button>
<button type="button" class="tbm-btn tbm-btn-primary tbm-btn-sm" onclick="openWorkerSelectionModal()">
<span class="tbm-btn-icon">+</span>
작업자 선택
</button>
</div>
</div>
<!-- 작업자 카드 리스트 -->
<div id="workerTaskList" class="tbm-worker-list">
<!-- 작업자 카드들이 여기에 동적으로 추가됩니다 -->
<div class="tbm-empty-state" id="workerListEmpty" style="padding: 2rem; border: 2px dashed #d1d5db; border-radius: 10px;">
<div class="tbm-empty-icon">&#128101;</div>
<p class="tbm-empty-description" style="margin: 0;">작업자를 선택해주세요</p>
</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: 1001;">
<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: 1001;">
<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: 1002;">
<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: 1002;">
<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; margin-bottom: 1rem; 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>
</div>
<!-- 리스트 기반 선택 (오류 대비용) -->
<div>
<div style="font-size: 0.875rem; color: #64748b; margin-bottom: 0.75rem; display: flex; align-items: center; justify-content: space-between;">
<span>리스트에서 선택 (지도 오류 시)</span>
<button type="button" class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="toggleWorkplaceList()" id="toggleListBtn">
<span id="toggleListIcon">&#9660;</span>
리스트 보기
</button>
</div>
<div id="workplaceList" class="tbm-item-list" style="display: none; max-height: 250px;">
<div style="color: #94a3b8; text-align: center; padding: 2rem;">
공장을 먼저 선택해주세요
</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>
<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" 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">
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeDetailModal()">닫기</button>
</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=4"></script>
</body>
</html>