feat: 다수 기능 개선 - 순찰, 출근, 작업분석, 모바일 UI 등
- 순찰/점검 기능 개선 (zone-detail 페이지 추가) - 출근/근태 시스템 개선 (연차 조회, 근무현황) - 작업분석 대분류 그룹화 및 마이그레이션 스크립트 - 모바일 네비게이션 UI 추가 - NAS 배포 도구 및 문서 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1
deploy/tkfb-package/web-ui/pages/work/.gitkeep
Normal file
1
deploy/tkfb-package/web-ui/pages/work/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# Placeholder file to create work directory
|
||||
2859
deploy/tkfb-package/web-ui/pages/work/analysis.html
Normal file
2859
deploy/tkfb-package/web-ui/pages/work/analysis.html
Normal file
File diff suppressed because it is too large
Load Diff
306
deploy/tkfb-package/web-ui/pages/work/nonconformity.html
Normal file
306
deploy/tkfb-package/web-ui/pages/work/nonconformity.html
Normal file
@@ -0,0 +1,306 @@
|
||||
<!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/api-base.js"></script>
|
||||
<script src="/js/app-init.js?v=2" defer></script>
|
||||
<script src="https://instant.page/5.2.0" type="module"></script>
|
||||
<style>
|
||||
/* 통계 카드 */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
padding: 1.25rem;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.stat-card.reported .stat-number { color: #3b82f6; }
|
||||
.stat-card.received .stat-number { color: #f97316; }
|
||||
.stat-card.in_progress .stat-number { color: #8b5cf6; }
|
||||
.stat-card.completed .stat-number { color: #10b981; }
|
||||
|
||||
/* 필터 바 */
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem 1.25rem;
|
||||
background: white;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.filter-bar select,
|
||||
.filter-bar input {
|
||||
padding: 0.625rem 0.875rem;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.filter-bar select:focus,
|
||||
.filter-bar input:focus {
|
||||
outline: none;
|
||||
border-color: #f97316;
|
||||
box-shadow: 0 0 0 3px rgba(249, 115, 22, 0.1);
|
||||
}
|
||||
|
||||
.btn-new-report {
|
||||
margin-left: auto;
|
||||
padding: 0.625rem 1.25rem;
|
||||
background: #f97316;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.btn-new-report:hover {
|
||||
background: #ea580c;
|
||||
}
|
||||
|
||||
/* 신고 목록 */
|
||||
.issue-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.issue-card {
|
||||
background: white;
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.25rem;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.issue-card:hover {
|
||||
border-color: #fed7aa;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.issue-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.issue-id {
|
||||
font-size: 0.875rem;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.issue-status {
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.issue-status.reported {
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.issue-status.received {
|
||||
background: #fed7aa;
|
||||
color: #c2410c;
|
||||
}
|
||||
|
||||
.issue-status.in_progress {
|
||||
background: #e9d5ff;
|
||||
color: #7c3aed;
|
||||
}
|
||||
|
||||
.issue-status.completed {
|
||||
background: #d1fae5;
|
||||
color: #047857;
|
||||
}
|
||||
|
||||
.issue-status.closed {
|
||||
background: #f3f4f6;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.issue-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.issue-category-badge {
|
||||
display: inline-block;
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
margin-right: 0.5rem;
|
||||
background: #fff7ed;
|
||||
color: #c2410c;
|
||||
}
|
||||
|
||||
.issue-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.issue-meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.issue-photos {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.issue-photos img {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
object-fit: cover;
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
/* 빈 상태 */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 4rem 1.5rem;
|
||||
color: #6b7280;
|
||||
background: white;
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
.empty-state-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.filter-bar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.btn-new-report {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="work-report-container">
|
||||
<div id="navbar-container"></div>
|
||||
|
||||
<main class="work-report-main">
|
||||
<div class="dashboard-main">
|
||||
<div class="page-header">
|
||||
<div class="page-title-section">
|
||||
<h1 class="page-title">부적합 현황</h1>
|
||||
<p class="page-description">자재, 설계, 검사 등 작업 관련 부적합 신고 현황입니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 통계 카드 -->
|
||||
<div class="stats-grid" id="statsGrid">
|
||||
<div class="stat-card reported">
|
||||
<div class="stat-number" id="statReported">-</div>
|
||||
<div class="stat-label">신고</div>
|
||||
</div>
|
||||
<div class="stat-card received">
|
||||
<div class="stat-number" id="statReceived">-</div>
|
||||
<div class="stat-label">접수</div>
|
||||
</div>
|
||||
<div class="stat-card in_progress">
|
||||
<div class="stat-number" id="statProgress">-</div>
|
||||
<div class="stat-label">처리중</div>
|
||||
</div>
|
||||
<div class="stat-card completed">
|
||||
<div class="stat-number" id="statCompleted">-</div>
|
||||
<div class="stat-label">완료</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 필터 바 -->
|
||||
<div class="filter-bar">
|
||||
<select id="filterStatus">
|
||||
<option value="">전체 상태</option>
|
||||
<option value="reported">신고</option>
|
||||
<option value="received">접수</option>
|
||||
<option value="in_progress">처리중</option>
|
||||
<option value="completed">완료</option>
|
||||
<option value="closed">종료</option>
|
||||
</select>
|
||||
|
||||
<input type="date" id="filterStartDate" title="시작일">
|
||||
<input type="date" id="filterEndDate" title="종료일">
|
||||
|
||||
<a href="/pages/safety/report.html?type=nonconformity" class="btn-new-report">
|
||||
+ 부적합 신고
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 신고 목록 -->
|
||||
<div class="issue-list" id="issueList">
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-title">로딩 중...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="/js/nonconformity-list.js?v=2"></script>
|
||||
</body>
|
||||
</html>
|
||||
182
deploy/tkfb-package/web-ui/pages/work/report-create.html
Normal file
182
deploy/tkfb-package/web-ui/pages/work/report-create.html
Normal file
@@ -0,0 +1,182 @@
|
||||
<!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/daily-work-report.css?v=12">
|
||||
<link rel="icon" type="image/png" href="/img/favicon.png">
|
||||
<!-- 최적화된 로딩 -->
|
||||
<script src="/js/api-base.js"></script>
|
||||
<script src="/js/app-init.js?v=2" defer></script>
|
||||
<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="tab-menu" style="margin-bottom: var(--space-6);">
|
||||
<button class="tab-btn active" id="tbmReportTab" onclick="switchTab('tbm')">
|
||||
작업보고서 작성
|
||||
</button>
|
||||
<button class="tab-btn" id="completedReportTab" onclick="switchTab('completed')">
|
||||
작성 완료 보고서
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 메시지 영역 -->
|
||||
<div id="message-container"></div>
|
||||
|
||||
<!-- TBM 작업보고 섹션 -->
|
||||
<div id="tbmReportSection" class="step-section active">
|
||||
<!-- TBM 작업 목록 -->
|
||||
<div id="tbmWorkList">
|
||||
<!-- TBM 작업 항목들이 여기에 동적으로 추가됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 작성 완료 보고서 섹션 -->
|
||||
<div id="completedReportSection" class="step-section" style="display: none;">
|
||||
<!-- 날짜 선택 필터 -->
|
||||
<div class="form-group" style="max-width: 300px; margin-bottom: var(--space-5);">
|
||||
<label for="completedReportDate" class="form-label">조회 날짜</label>
|
||||
<input type="date" id="completedReportDate" class="form-input" onchange="loadCompletedReports()">
|
||||
</div>
|
||||
|
||||
<!-- 완료된 보고서 목록 -->
|
||||
<div id="completedReportsList">
|
||||
<!-- 완료된 보고서들이 여기에 동적으로 추가됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- 저장 결과 모달 -->
|
||||
<div id="saveResultModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-container result-modal">
|
||||
<div class="modal-header">
|
||||
<h2 id="resultModalTitle">저장 결과</h2>
|
||||
<button class="modal-close-btn" onclick="closeSaveResultModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="resultModalContent" class="result-content">
|
||||
<!-- 결과 내용이 여기에 동적으로 추가됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" onclick="closeSaveResultModal()">
|
||||
확인
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 작업장소 선택 모달 (지도 기반) -->
|
||||
<div id="workplaceModal" class="modal-overlay" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 1002; align-items: center; justify-content: center; overflow-y: auto; padding: 2rem 0;">
|
||||
<div class="modal-container" style="background: white; border-radius: 8px; max-width: 1000px; width: 90%; max-height: none; margin: auto; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column;">
|
||||
<div class="modal-header" style="padding: 1.5rem; border-bottom: 1px solid #e5e7eb; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h2 style="font-size: 1.25rem; font-weight: 600; color: #111827; margin: 0;">작업장소 선택</h2>
|
||||
<button class="modal-close" onclick="closeWorkplaceModal()" style="background: none; border: none; font-size: 1.5rem; cursor: pointer; color: #6b7280; padding: 0; width: 2rem; height: 2rem; display: flex; align-items: center; justify-content: center; border-radius: 4px;">×</button>
|
||||
</div>
|
||||
<div class="modal-body" style="padding: 1.5rem; flex: 1; overflow-y: visible;">
|
||||
<!-- 1단계: 카테고리 선택 -->
|
||||
<div id="categorySelectionArea">
|
||||
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 0.75rem; color: #374151;">공장 선택</h3>
|
||||
<div id="workplaceCategoryList" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.75rem;">
|
||||
<!-- 카테고리 버튼들 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2단계: 작업장 선택 (지도 + 리스트) -->
|
||||
<div id="workplaceSelectionArea" style="display: none; margin-top: 1.5rem;">
|
||||
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 0.75rem; color: #374151;">
|
||||
<span id="selectedCategoryTitle">작업장 선택</span>
|
||||
</h3>
|
||||
|
||||
<!-- 지도 기반 선택 영역 -->
|
||||
<div id="layoutMapArea" style="display: none; margin-bottom: 1.5rem; padding: 1rem; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 0.5rem;">
|
||||
<div style="font-size: 0.875rem; color: #6b7280; margin-bottom: 0.75rem;">
|
||||
지도에서 작업장을 클릭하여 선택하세요
|
||||
</div>
|
||||
<div style="text-align: center; position: relative; display: inline-block; max-width: 100%;">
|
||||
<canvas id="workplaceMapCanvas" style="max-width: 100%; border-radius: 0.5rem; cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 리스트 선택 영역 -->
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem;">
|
||||
<span style="font-size: 0.875rem; color: #6b7280;">리스트에서 선택</span>
|
||||
</div>
|
||||
<div id="workplaceListArea" style="display: flex; flex-direction: column; gap: 0.5rem; max-height: 200px; overflow-y: auto; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.5rem; background: white;">
|
||||
<!-- 작업장소 목록 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 0.75rem; justify-content: flex-end; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #e5e7eb;">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeWorkplaceModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" id="confirmWorkplaceBtn" onclick="confirmWorkplaceSelection()" disabled>선택 완료</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 시간 선택 팝오버 -->
|
||||
<div id="timePickerOverlay" class="time-picker-overlay" style="display: none;" onclick="closeTimePicker()">
|
||||
<div class="time-picker-popup" onclick="event.stopPropagation()">
|
||||
<div class="time-picker-header">
|
||||
<h3 id="timePickerTitle">작업시간 선택</h3>
|
||||
<button class="time-picker-close" onclick="closeTimePicker()">×</button>
|
||||
</div>
|
||||
|
||||
<div class="quick-time-grid">
|
||||
<button type="button" class="time-btn" onclick="setTimeValue(0.5)">
|
||||
<span class="time-value">30분</span>
|
||||
</button>
|
||||
<button type="button" class="time-btn" onclick="setTimeValue(1)">
|
||||
<span class="time-value">1시간</span>
|
||||
</button>
|
||||
<button type="button" class="time-btn" onclick="setTimeValue(2)">
|
||||
<span class="time-value">2시간</span>
|
||||
</button>
|
||||
<button type="button" class="time-btn" onclick="setTimeValue(4)">
|
||||
<span class="time-value">4시간</span>
|
||||
</button>
|
||||
<button type="button" class="time-btn" onclick="setTimeValue(8)">
|
||||
<span class="time-value">8시간</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="time-adjust-area">
|
||||
<span class="current-time-label">현재:</span>
|
||||
<strong id="currentTimeDisplay" class="current-time-value">0시간</strong>
|
||||
<div class="adjust-buttons">
|
||||
<button type="button" class="adjust-btn" onclick="adjustTime(-0.5)">-30분</button>
|
||||
<button type="button" class="adjust-btn" onclick="adjustTime(0.5)">+30분</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="confirm-btn" onclick="confirmTimeSelection()">확인</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 스크립트 -->
|
||||
<script src="/js/api-base.js"></script>
|
||||
<script src="/js/app-init.js?v=2" defer></script>
|
||||
<script src="https://instant.page/5.2.0" type="module"></script>
|
||||
|
||||
<!-- 작업보고서 모듈 (리팩토링된 구조) -->
|
||||
<script src="/js/daily-work-report/state.js?v=1"></script>
|
||||
<script src="/js/daily-work-report/utils.js?v=1"></script>
|
||||
<script src="/js/daily-work-report/api.js?v=1"></script>
|
||||
|
||||
<!-- 기존 UI 로직 (점진적 마이그레이션) -->
|
||||
<script type="module" src="/js/daily-work-report.js?v=29"></script>
|
||||
</body>
|
||||
</html>
|
||||
672
deploy/tkfb-package/web-ui/pages/work/tbm.html
Normal file
672
deploy/tkfb-package/web-ui/pages/work/tbm.html
Normal file
@@ -0,0 +1,672 @@
|
||||
<!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">🛠</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">📝</span>
|
||||
TBM 입력
|
||||
</button>
|
||||
<button class="tbm-tab-btn" data-tab="tbm-manage" onclick="switchTbmTab('tbm-manage')">
|
||||
<span class="tbm-tab-icon">📊</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>📅</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">📋</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>📚</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">📚</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>📝</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>📅</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>👥</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">👥</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">✓</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>⚙</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">💡</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>🛠</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">▼</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">▼</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">▼</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">▼</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">✓</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>👥</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">✓</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>🏭</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">▼</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">✓</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>👥</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">✓</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>🛡</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">✓</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>✓</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">⚠</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">✓</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>👉</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">👉</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>📋</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>📅</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>👥</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>🛡</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>
|
||||
Reference in New Issue
Block a user