feat: 일일순회점검 시스템 구축 및 관리 기능 개선
- 일일순회점검 시스템 신규 구현 - DB 테이블: patrol_checklist_items, daily_patrol_sessions, patrol_check_records, workplace_items, item_types - API: /api/patrol/* 엔드포인트 - 프론트엔드: 지도 기반 작업장 점검 UI - 설비 관리 기능 개선 - 구매 관련 필드 추가 (구매일, 가격, 공급업체 등) - 설비 코드 자동 생성 (TKP-XXX 형식) - 작업장 관리 개선 - 레이아웃 이미지 업로드 기능 - 마커 위치 저장 기능 - 부서 관리 기능 추가 - 사이드바 네비게이션 카테고리 재구성 - 이미지 401 오류 수정 (정적 파일 경로 처리) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,10 +10,9 @@
|
||||
<link rel="stylesheet" href="/css/design-system.css">
|
||||
<link rel="stylesheet" href="/css/work-analysis.css?v=41">
|
||||
<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>
|
||||
<script type="module" src="/js/load-navbar.js"></script>
|
||||
<script type="module" src="/js/load-sidebar.js"></script>
|
||||
<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="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -2853,6 +2852,5 @@
|
||||
// 초기 모드 설정
|
||||
window.currentAnalysisMode = 'period';
|
||||
</script>
|
||||
<script type="module" src="/js/load-navbar.js?v=5"></script>
|
||||
</body>
|
||||
</html>
|
||||
210
web-ui/pages/work/daily-patrol.html
Normal file
210
web-ui/pages/work/daily-patrol.html
Normal file
@@ -0,0 +1,210 @@
|
||||
<!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/admin-pages.css?v=8">
|
||||
<link rel="stylesheet" href="/css/daily-patrol.css?v=1">
|
||||
<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>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 네비게이션 바 -->
|
||||
<div id="navbar-container"></div>
|
||||
|
||||
<!-- 메인 레이아웃 -->
|
||||
<div class="page-container">
|
||||
<main class="main-content">
|
||||
<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="patrol-session-selector">
|
||||
<div class="patrol-date-time">
|
||||
<div class="form-group">
|
||||
<label for="patrolDate">점검 일자</label>
|
||||
<input type="date" id="patrolDate" class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>점검 시간대</label>
|
||||
<div class="patrol-time-buttons">
|
||||
<button type="button" class="patrol-time-btn active" data-time="morning">오전</button>
|
||||
<button type="button" class="patrol-time-btn" data-time="afternoon">오후</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="categorySelect">공장 선택</label>
|
||||
<select id="categorySelect" class="form-control">
|
||||
<option value="">공장 선택...</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" id="startPatrolBtn" onclick="startPatrol()">
|
||||
순회점검 시작
|
||||
</button>
|
||||
</div>
|
||||
<!-- 오늘 점검 현황 요약 -->
|
||||
<div id="todayStatusSummary" class="today-status-summary">
|
||||
<!-- JS에서 렌더링 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 점검 진행 영역 (세션 시작 후 표시) -->
|
||||
<div id="patrolArea" class="patrol-area" style="display: none;">
|
||||
<!-- 세션 정보 -->
|
||||
<div id="sessionInfo" class="session-info-bar">
|
||||
<!-- JS에서 렌더링 -->
|
||||
</div>
|
||||
|
||||
<!-- 지도 및 체크리스트 영역 -->
|
||||
<div class="patrol-content">
|
||||
<!-- 작업장 지도 (좌측) -->
|
||||
<div class="patrol-map-section">
|
||||
<div class="map-header">
|
||||
<h3>작업장 지도</h3>
|
||||
<div class="map-legend">
|
||||
<span class="legend-item completed"><span class="dot"></span> 점검완료</span>
|
||||
<span class="legend-item in-progress"><span class="dot"></span> 점검중</span>
|
||||
<span class="legend-item pending"><span class="dot"></span> 미점검</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="patrolMapContainer" class="patrol-map-container">
|
||||
<!-- 지도 이미지 및 작업장 마커 -->
|
||||
</div>
|
||||
<!-- 작업장 목록 (지도 대신 사용 가능) -->
|
||||
<div id="workplaceListContainer" class="workplace-list-container">
|
||||
<!-- 작업장 목록 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 체크리스트 영역 (우측) -->
|
||||
<div class="patrol-checklist-section">
|
||||
<div id="checklistHeader" class="checklist-header">
|
||||
<h3>체크리스트</h3>
|
||||
<p class="checklist-subtitle">작업장을 선택하면 체크리스트가 표시됩니다</p>
|
||||
</div>
|
||||
<div id="checklistContent" class="checklist-content">
|
||||
<!-- 체크리스트 항목들 -->
|
||||
<div class="checklist-placeholder">
|
||||
<p>좌측 지도에서 점검할 작업장을 선택해주세요</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="checklistActions" class="checklist-actions" style="display: none;">
|
||||
<button type="button" class="btn btn-secondary" onclick="saveChecklistDraft()">임시저장</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveChecklist()">저장 후 다음</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 물품 현황 영역 -->
|
||||
<div id="itemsSection" class="items-section" style="display: none;">
|
||||
<div class="items-header">
|
||||
<h3><span id="selectedWorkplaceName">작업장</span> 물품 현황</h3>
|
||||
<button type="button" class="btn btn-sm btn-outline" onclick="toggleItemEditMode()">
|
||||
<span id="itemEditModeText">편집모드</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="itemsMapContainer" class="items-map-container">
|
||||
<!-- 작업장 상세 지도 및 물품 마커 -->
|
||||
</div>
|
||||
<div id="itemsLegend" class="items-legend">
|
||||
<!-- 물품 유형 범례 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 순회점검 완료 버튼 -->
|
||||
<div class="patrol-complete-section">
|
||||
<div class="form-group">
|
||||
<label for="patrolNotes">특이사항</label>
|
||||
<textarea id="patrolNotes" class="form-control" rows="2" placeholder="순회 중 발견한 특이사항을 기록하세요..."></textarea>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-lg" onclick="completePatrol()">
|
||||
순회점검 완료
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- 물품 추가/수정 모달 -->
|
||||
<div id="itemModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-container" style="max-width: 500px;">
|
||||
<div class="modal-header">
|
||||
<h2 id="itemModalTitle">물품 추가</h2>
|
||||
<button class="btn-close" onclick="closeItemModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="itemForm">
|
||||
<input type="hidden" id="itemId">
|
||||
<div class="form-group">
|
||||
<label for="itemType">물품 유형 *</label>
|
||||
<select id="itemType" class="form-control" required>
|
||||
<!-- JS에서 옵션 추가 -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="itemName">물품명/설명</label>
|
||||
<input type="text" id="itemName" class="form-control" placeholder="예: A사 용기 10개">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="itemQuantity">수량</label>
|
||||
<input type="number" id="itemQuantity" class="form-control" value="1" min="1">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeItemModal()">취소</button>
|
||||
<button type="button" class="btn btn-danger" id="deleteItemBtn" onclick="deleteItem()" style="display: none;">삭제</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveItem()">저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
<script type="module">
|
||||
import '/js/api-config.js?v=3';
|
||||
</script>
|
||||
<script>
|
||||
(function() {
|
||||
const checkApiConfig = setInterval(() => {
|
||||
if (window.API_BASE_URL) {
|
||||
clearInterval(checkApiConfig);
|
||||
axios.defaults.baseURL = window.API_BASE_URL;
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
axios.interceptors.request.use(
|
||||
config => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) config.headers.Authorization = `Bearer ${token}`;
|
||||
return config;
|
||||
},
|
||||
error => Promise.reject(error)
|
||||
);
|
||||
axios.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
if (error.response?.status === 401) {
|
||||
alert('세션이 만료되었습니다. 다시 로그인해주세요.');
|
||||
window.location.href = '/pages/login.html';
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
}, 50);
|
||||
})();
|
||||
</script>
|
||||
<script src="/js/daily-patrol.js?v=1"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -8,8 +8,9 @@
|
||||
<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>
|
||||
<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 {
|
||||
@@ -300,8 +301,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script type="module" src="/js/load-navbar.js"></script>
|
||||
<script type="module" src="/js/load-sidebar.js"></script>
|
||||
<script src="/js/nonconformity-list.js?v=2"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
<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/auth-check.js" defer></script>
|
||||
<script type="module" src="/js/api-config.js"></script>
|
||||
<script type="module" src="/js/load-navbar.js"></script>
|
||||
<script type="module" src="/js/load-sidebar.js"></script>
|
||||
<!-- 최적화된 로딩 -->
|
||||
<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">
|
||||
@@ -167,8 +167,9 @@
|
||||
</div>
|
||||
|
||||
<!-- 스크립트 -->
|
||||
<script type="module" src="/js/api-config.js?v=3"></script>
|
||||
<script type="module" src="/js/load-navbar.js?v=5"></script>
|
||||
<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 type="module" src="/js/daily-work-report.js?v=28"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -8,10 +8,10 @@
|
||||
<link rel="stylesheet" href="/css/common.css?v=13">
|
||||
<link rel="stylesheet" href="/css/modern-dashboard.css?v=14">
|
||||
<link rel="stylesheet" href="/css/work-report-calendar.css?v=29">
|
||||
<script src="/js/auth-check.js" defer></script>
|
||||
<script type="module" src="/js/api-config.js"></script>
|
||||
<script type="module" src="/js/load-navbar.js"></script>
|
||||
<script type="module" src="/js/load-sidebar.js"></script>
|
||||
<!-- 최적화된 로딩 -->
|
||||
<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>
|
||||
<!-- 네비게이션 헤더 -->
|
||||
@@ -283,9 +283,9 @@
|
||||
</div>
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script type="module" src="/js/api-config.js?v=13"></script>
|
||||
<script type="module" src="/js/auth-check.js?v=13"></script>
|
||||
<script type="module" src="/js/load-navbar.js"></script>
|
||||
<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/modules/calendar/CalendarState.js?v=1"></script>
|
||||
<script src="/js/modules/calendar/CalendarAPI.js?v=1"></script>
|
||||
<script src="/js/modules/calendar/CalendarView.js?v=1"></script>
|
||||
|
||||
@@ -6,58 +6,13 @@
|
||||
<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/project-management.css?v=3">
|
||||
<link rel="stylesheet" href="/css/tbm.css?v=1">
|
||||
<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>
|
||||
<script type="module" src="/js/load-navbar.js"></script>
|
||||
<script type="module" src="/js/load-sidebar.js"></script>
|
||||
<style>
|
||||
.date-group {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.date-group-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||||
border-radius: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
border-left: 4px solid var(--primary-500);
|
||||
}
|
||||
.date-group-header.today {
|
||||
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
|
||||
border-left-color: #3b82f6;
|
||||
}
|
||||
.date-group-date {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
}
|
||||
.date-group-day {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: white;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.date-group-count {
|
||||
margin-left: auto;
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
.date-group-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.date-group-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- 최적화된 로딩: 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">
|
||||
@@ -66,60 +21,75 @@
|
||||
|
||||
<!-- 메인 콘텐츠 -->
|
||||
<main class="work-report-main">
|
||||
<div class="dashboard-main">
|
||||
<div class="page-header">
|
||||
<div class="page-title-section">
|
||||
<h1 class="page-title">TBM (Tool Box Meeting)</h1>
|
||||
<p class="page-description">아침 안전 회의 및 팀 구성 관리</p>
|
||||
</div>
|
||||
|
||||
<div class="page-actions" id="headerActions">
|
||||
<!-- 탭에 따라 동적으로 변경됩니다 -->
|
||||
<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="code-tabs">
|
||||
<button class="tab-btn active" data-tab="tbm-input" onclick="switchTbmTab('tbm-input')">
|
||||
<!-- 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="tab-btn" data-tab="tbm-manage" onclick="switchTbmTab('tbm-manage')">
|
||||
<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="code-tab-content active">
|
||||
<div class="code-section">
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">오늘의 TBM</h2>
|
||||
<div class="section-actions">
|
||||
<button class="btn btn-primary" onclick="openNewTbmModal()">새 TBM 시작</button>
|
||||
<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="code-stats">
|
||||
<span class="stat-item">
|
||||
오늘 등록 <span id="todayTotalSessions">0</span>개
|
||||
<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="stat-item">
|
||||
완료 <span id="todayCompletedSessions">0</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="stat-item">
|
||||
진행중 <span id="todayActiveSessions">0</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="code-grid" id="todayTbmGrid">
|
||||
<div class="tbm-card-grid" id="todayTbmGrid">
|
||||
<!-- 오늘의 TBM 세션 카드들이 여기에 동적으로 생성됩니다 -->
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div class="empty-state" id="todayEmptyState" style="display: none;">
|
||||
<div class="empty-icon"></div>
|
||||
<h3>오늘 등록된 TBM이 없습니다</h3>
|
||||
<p>"새 TBM 시작" 버튼을 눌러 오늘의 TBM을 시작해보세요.</p>
|
||||
<button class="btn btn-primary" onclick="openNewTbmModal()">
|
||||
<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>
|
||||
@@ -127,37 +97,46 @@
|
||||
</div>
|
||||
|
||||
<!-- TBM 관리 탭 -->
|
||||
<div id="tbm-manage-tab" class="code-tab-content">
|
||||
<div class="code-section">
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">TBM 기록</h2>
|
||||
<div class="section-actions">
|
||||
<button class="btn btn-secondary" onclick="loadMoreTbmDays()" id="loadMoreBtn">더 보기</button>
|
||||
<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="code-stats">
|
||||
<span class="stat-item">
|
||||
총 <span id="totalSessions">0</span>개
|
||||
<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="stat-item">
|
||||
완료 <span id="completedSessions">0</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="stat-item" id="viewModeIndicator" style="display: none;">
|
||||
<span id="viewModeText">내 TBM만</span>
|
||||
<span class="tbm-stat-item" id="viewModeIndicator" style="display: none;">
|
||||
<span class="tbm-stat-value" id="viewModeText">내 TBM만</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 날짜별 그룹 컨테이너 -->
|
||||
<div id="tbmDateGroupsContainer">
|
||||
<div class="tbm-section-body" id="tbmDateGroupsContainer">
|
||||
<!-- 날짜별 TBM 그룹이 여기에 동적으로 생성됩니다 -->
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div class="empty-state" id="emptyState" style="display: none;">
|
||||
<div class="empty-icon"></div>
|
||||
<h3>등록된 TBM 세션이 없습니다</h3>
|
||||
<p>TBM 입력 탭에서 새로운 TBM을 시작해보세요.</p>
|
||||
<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>
|
||||
@@ -165,131 +144,153 @@
|
||||
</main>
|
||||
|
||||
<!-- TBM 생성/수정 모달 -->
|
||||
<div id="tbmModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-container" style="max-width: 1000px;">
|
||||
<div class="modal-header">
|
||||
<h2 id="modalTitle">새 TBM 시작</h2>
|
||||
<button class="modal-close-btn" onclick="closeTbmModal()">×</button>
|
||||
<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="modal-body">
|
||||
<div class="tbm-modal-body">
|
||||
<form id="tbmForm" onsubmit="event.preventDefault(); saveTbmSession();">
|
||||
<input type="hidden" id="sessionId">
|
||||
|
||||
<!-- 고정 정보 섹션 -->
|
||||
<div class="form-section" style="background: #f9fafb; padding: 1rem; border-radius: 0.5rem; margin-bottom: 1.5rem;">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">TBM 날짜 *</label>
|
||||
<input type="date" id="sessionDate" class="form-control" required readonly style="background: #e5e7eb;">
|
||||
<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="form-group">
|
||||
<label class="form-label">입력자 *</label>
|
||||
<input type="text" id="leaderName" class="form-control" readonly style="background: #e5e7eb;">
|
||||
<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="form-section">
|
||||
<div class="section-header" style="margin-bottom: 1rem;">
|
||||
<h3 style="font-size: 1.1rem; font-weight: 600; color: #1f2937;">
|
||||
<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="btn btn-sm btn-secondary" onclick="openBulkSettingModal()" style="display: flex; align-items: center; gap: 0.25rem;">
|
||||
<button type="button" class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="openBulkSettingModal()">
|
||||
일괄 설정
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="openWorkerSelectionModal()">
|
||||
<span style="font-size: 1.2rem; margin-right: 0.25rem;">+</span>
|
||||
<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" style="display: flex; flex-direction: column; gap: 0.75rem; min-height: 100px;">
|
||||
<div id="workerTaskList" class="tbm-worker-list">
|
||||
<!-- 작업자 카드들이 여기에 동적으로 추가됩니다 -->
|
||||
<div class="empty-state-small" id="workerListEmpty" style="display: flex; align-items: center; justify-content: center; padding: 2rem; border: 2px dashed #d1d5db; border-radius: 0.5rem; color: #6b7280;">
|
||||
<div style="text-align: center;">
|
||||
<p>작업자를 선택해주세요</p>
|
||||
</div>
|
||||
<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="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeTbmModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveTbmSession()">저장하기</button>
|
||||
<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="modal-overlay" style="display: none; z-index: 1001;">
|
||||
<div class="modal-container" style="max-width: 700px;">
|
||||
<div class="modal-header">
|
||||
<h2>일괄 설정</h2>
|
||||
<button class="modal-close-btn" onclick="closeBulkSettingModal()">×</button>
|
||||
<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="modal-body">
|
||||
<div style="background: #dbeafe; border: 1px solid #3b82f6; padding: 1rem; border-radius: 0.5rem; margin-bottom: 1.5rem;">
|
||||
<div style="font-weight: 600; color: #1e40af; margin-bottom: 0.25rem;">일괄 설정</div>
|
||||
<div style="color: #1e40af; font-size: 0.9rem;">
|
||||
선택한 작업자들의 <strong>첫 번째 작업 라인</strong>에 동일한 정보가 적용됩니다.
|
||||
<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="form-group">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem;">
|
||||
<label class="form-label" style="margin-bottom: 0;">적용할 작업자 선택 *</label>
|
||||
<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="btn btn-sm btn-secondary" onclick="selectAllForBulk()" style="font-size: 0.75rem; padding: 0.25rem 0.5rem;">전체</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" onclick="deselectAllForBulk()" style="font-size: 0.75rem; padding: 0.25rem 0.5rem;">해제</button>
|
||||
<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" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 0.5rem; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.5rem; max-height: 200px; overflow-y: auto; background: #f9fafb;">
|
||||
<div id="bulkWorkerSelection" class="tbm-worker-select-grid" style="max-height: 180px;">
|
||||
<!-- 작업자 체크박스들이 여기에 생성됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="border-top: 1px solid #e5e7eb; margin: 1.5rem 0; padding-top: 1.5rem;">
|
||||
<h4 style="font-size: 0.95rem; font-weight: 600; margin-bottom: 1rem; color: #374151;">적용할 작업 정보</h4>
|
||||
<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="form-group">
|
||||
<label class="form-label">프로젝트</label>
|
||||
<button type="button" id="bulkProjectBtn" onclick="openBulkItemSelect('project')" class="btn btn-secondary" style="width: 100%; text-align: left; justify-content: flex-start;">
|
||||
<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="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">공정 *</label>
|
||||
<button type="button" id="bulkWorkTypeBtn" onclick="openBulkItemSelect('workType')" class="btn btn-secondary" style="width: 100%; text-align: left; justify-content: flex-start;">
|
||||
<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="form-group">
|
||||
<label class="form-label">작업 *</label>
|
||||
<button type="button" id="bulkTaskBtn" onclick="openBulkItemSelect('task')" class="btn btn-secondary" style="width: 100%; text-align: left; justify-content: flex-start;" disabled>
|
||||
<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="form-group">
|
||||
<label class="form-label">작업장 *</label>
|
||||
<button type="button" id="bulkWorkplaceBtn" onclick="openBulkWorkplaceSelect()" class="btn btn-secondary" style="width: 100%; text-align: left; justify-content: flex-start;">
|
||||
<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">
|
||||
@@ -297,9 +298,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeBulkSettingModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" onclick="applyBulkSettings()">
|
||||
<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>
|
||||
@@ -307,27 +309,31 @@
|
||||
</div>
|
||||
|
||||
<!-- 작업자 선택 모달 -->
|
||||
<div id="workerSelectionModal" class="modal-overlay" style="display: none; z-index: 1001;">
|
||||
<div class="modal-container" style="max-width: 800px;">
|
||||
<div class="modal-header">
|
||||
<h2>작업자 선택</h2>
|
||||
<button class="modal-close-btn" onclick="closeWorkerSelectionModal()">×</button>
|
||||
<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="modal-body">
|
||||
<div class="tbm-modal-body">
|
||||
<div style="margin-bottom: 1rem; display: flex; gap: 0.5rem;">
|
||||
<button type="button" class="btn btn-sm btn-secondary" onclick="selectAllWorkersInModal()">전체 선택</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" onclick="deselectAllWorkersInModal()">전체 해제</button>
|
||||
<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" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.75rem; max-height: 500px; overflow-y: auto; padding: 0.5rem;">
|
||||
<div id="workerCardGrid" class="tbm-worker-select-grid">
|
||||
<!-- 작업자 카드들이 여기에 생성됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeWorkerSelectionModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" onclick="confirmWorkerSelection()">
|
||||
<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>
|
||||
@@ -335,81 +341,89 @@
|
||||
</div>
|
||||
|
||||
<!-- 항목 선택 모달 (프로젝트/공정/작업 선택용) -->
|
||||
<div id="itemSelectModal" class="modal-overlay" style="display: none; z-index: 1002;">
|
||||
<div class="modal-container" style="max-width: 600px;">
|
||||
<div class="modal-header">
|
||||
<h2 id="itemSelectModalTitle">항목 선택</h2>
|
||||
<button class="modal-close-btn" onclick="closeItemSelectModal()">×</button>
|
||||
<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="modal-body">
|
||||
<div id="itemSelectList" style="display: flex; flex-direction: column; gap: 0.5rem; max-height: 400px; overflow-y: auto; padding: 0.5rem;">
|
||||
<div class="tbm-modal-body">
|
||||
<div id="itemSelectList" class="tbm-item-list">
|
||||
<!-- 선택 항목들이 여기에 생성됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeItemSelectModal()">취소</button>
|
||||
<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="modal-overlay" style="display: none; z-index: 1002;">
|
||||
<div class="modal-container" style="max-width: 1000px; max-height: 90vh;">
|
||||
<div class="modal-header">
|
||||
<h2>작업장 선택</h2>
|
||||
<button class="modal-close-btn" onclick="closeWorkplaceSelectModal()">×</button>
|
||||
<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="modal-body" style="overflow-y: auto;">
|
||||
<div class="tbm-modal-body">
|
||||
<!-- 1단계: 공장 선택 -->
|
||||
<div style="margin-bottom: 1.5rem;">
|
||||
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 0.75rem; color: #374151;">
|
||||
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; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.5rem; background: #f9fafb;">
|
||||
<div id="categoryList" style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
|
||||
<!-- 공장 카테고리 버튼들이 여기에 생성됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2단계: 작업장 선택 (지도 + 리스트) -->
|
||||
<div id="workplaceSelectionArea" style="display: none;">
|
||||
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 0.75rem; color: #374151;">
|
||||
2. 작업장 선택
|
||||
</h3>
|
||||
<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: 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 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 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>
|
||||
<div style="font-size: 0.875rem; color: #6b7280; margin-bottom: 0.75rem; display: flex; align-items: center; justify-content: space-between;">
|
||||
<span>리스트에서 선택 (지도 오류 시)</span>
|
||||
<button type="button" class="btn btn-sm btn-secondary" onclick="toggleWorkplaceList()" id="toggleListBtn">
|
||||
<span id="toggleListIcon">▼</span>
|
||||
리스트 보기
|
||||
</button>
|
||||
</div>
|
||||
<div id="workplaceList" style="display: none; flex-direction: column; gap: 0.5rem; max-height: 300px; overflow-y: auto; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.5rem; background: white;">
|
||||
<div style="color: #9ca3af; text-align: center; padding: 2rem;">
|
||||
공장을 먼저 선택해주세요
|
||||
<!-- 리스트 기반 선택 (오류 대비용) -->
|
||||
<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="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeWorkplaceSelectModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" onclick="confirmWorkplaceSelection()" id="confirmWorkplaceBtn" disabled>
|
||||
<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>
|
||||
@@ -417,37 +431,43 @@
|
||||
</div>
|
||||
|
||||
<!-- 팀 구성 모달 -->
|
||||
<div id="teamModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-container" style="max-width: 900px;">
|
||||
<div class="modal-header">
|
||||
<h2>팀 구성</h2>
|
||||
<button class="modal-close-btn" onclick="closeTeamModal()">×</button>
|
||||
<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="modal-body">
|
||||
<div class="section-header" style="margin-bottom: 1rem;">
|
||||
<h3 style="font-size: 1rem; font-weight: 600;">작업자 선택</h3>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-secondary" onclick="selectAllWorkers()">전체 선택</button>
|
||||
<button class="btn btn-sm btn-secondary" onclick="deselectAllWorkers()">전체 해제</button>
|
||||
<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" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.75rem; max-height: 400px; overflow-y: auto; padding: 0.5rem; border: 1px solid #e5e7eb; border-radius: 0.5rem;">
|
||||
<div id="workerSelectionGrid" class="tbm-worker-select-grid">
|
||||
<!-- 작업자 체크박스 목록이 여기에 생성됩니다 -->
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 1.5rem;">
|
||||
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 0.75rem;">선택된 팀원 <span id="selectedCount">0</span>명</h3>
|
||||
<div id="selectedWorkersList" style="display: flex; flex-wrap: wrap; gap: 0.5rem; min-height: 50px; padding: 0.75rem; background: #f9fafb; border-radius: 0.5rem;">
|
||||
<p style="margin: 0; color: #9ca3af; font-size: 0.875rem;">작업자를 선택해주세요</p>
|
||||
<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="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeTeamModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveTeamComposition()">
|
||||
<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>
|
||||
@@ -455,22 +475,26 @@
|
||||
</div>
|
||||
|
||||
<!-- 안전 체크리스트 모달 -->
|
||||
<div id="safetyModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-container" style="max-width: 700px;">
|
||||
<div class="modal-header">
|
||||
<h2>안전 체크리스트</h2>
|
||||
<button class="modal-close-btn" onclick="closeSafetyModal()">×</button>
|
||||
<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="modal-body">
|
||||
<div id="safetyChecklistContainer" style="max-height: 500px; overflow-y: auto;">
|
||||
<div class="tbm-modal-body">
|
||||
<div id="safetyChecklistContainer" class="tbm-safety-list">
|
||||
<!-- 안전 체크리스트가 여기에 생성됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeSafetyModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveSafetyChecklist()">
|
||||
<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>
|
||||
@@ -478,26 +502,35 @@
|
||||
</div>
|
||||
|
||||
<!-- TBM 완료 모달 -->
|
||||
<div id="completeModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-container" style="max-width: 500px;">
|
||||
<div class="modal-header">
|
||||
<h2>TBM 완료</h2>
|
||||
<button class="modal-close-btn" onclick="closeCompleteModal()">×</button>
|
||||
<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="modal-body">
|
||||
<p style="margin-bottom: 1rem;">이 TBM 세션을 완료 처리하시겠습니까?</p>
|
||||
<p style="color: #6b7280; font-size: 0.875rem;">완료 후에는 수정할 수 없습니다.</p>
|
||||
<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="form-group" style="margin-top: 1.5rem;">
|
||||
<label class="form-label">종료 시간</label>
|
||||
<input type="time" id="endTime" class="form-control">
|
||||
<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="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeCompleteModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" onclick="completeTbmSession()">
|
||||
<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>
|
||||
@@ -505,20 +538,23 @@
|
||||
</div>
|
||||
|
||||
<!-- 작업 인계 모달 -->
|
||||
<div id="handoverModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-container" style="max-width: 600px;">
|
||||
<div class="modal-header">
|
||||
<h2>작업 인계</h2>
|
||||
<button class="modal-close-btn" onclick="closeHandoverModal()">×</button>
|
||||
<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="modal-body">
|
||||
<div class="tbm-modal-body">
|
||||
<form id="handoverForm">
|
||||
<input type="hidden" id="handoverSessionId">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">인계 사유 *</label>
|
||||
<select id="handoverReason" class="form-control" required>
|
||||
<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>
|
||||
@@ -527,41 +563,42 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">인수자 (다음 팀장) *</label>
|
||||
<select id="toLeaderId" class="form-control" required>
|
||||
<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="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">인계 날짜 *</label>
|
||||
<input type="date" id="handoverDate" class="form-control" required>
|
||||
<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="form-group">
|
||||
<label class="form-label">인계 시간</label>
|
||||
<input type="time" id="handoverTime" class="form-control">
|
||||
<div class="tbm-form-group">
|
||||
<label class="tbm-form-label">인계 시간</label>
|
||||
<input type="time" id="handoverTime" class="tbm-form-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">인계 내용</label>
|
||||
<textarea id="handoverNotes" class="form-control" rows="4" placeholder="인수자에게 전달할 내용을 입력하세요"></textarea>
|
||||
<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="form-group">
|
||||
<label class="form-label" style="margin-bottom: 0.75rem; display: block;">인계할 팀원 선택</label>
|
||||
<div id="handoverTeamList" style="max-height: 200px; overflow-y: auto; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 0.5rem;">
|
||||
<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="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeHandoverModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveHandover()">
|
||||
<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>
|
||||
@@ -569,41 +606,53 @@
|
||||
</div>
|
||||
|
||||
<!-- TBM 상세보기 모달 -->
|
||||
<div id="detailModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-container" style="max-width: 900px;">
|
||||
<div class="modal-header">
|
||||
<h2>TBM 상세 정보</h2>
|
||||
<button class="modal-close-btn" onclick="closeDetailModal()">×</button>
|
||||
<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="modal-body" style="max-height: 70vh; overflow-y: auto;">
|
||||
<div class="tbm-modal-body">
|
||||
<!-- 세션 기본 정보 -->
|
||||
<div class="section" style="margin-bottom: 1.5rem;">
|
||||
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 1rem; color: #374151;">기본 정보</h3>
|
||||
<div id="detailBasicInfo" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.75rem;">
|
||||
<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="section" style="margin-bottom: 1.5rem;">
|
||||
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 1rem; color: #374151;">팀 구성</h3>
|
||||
<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="section">
|
||||
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 1rem; color: #374151;">안전 체크리스트</h3>
|
||||
<div class="tbm-form-section">
|
||||
<h3 class="tbm-form-section-title">
|
||||
<span>🛡</span>
|
||||
안전 체크리스트
|
||||
</h3>
|
||||
<div id="detailSafetyChecks">
|
||||
<!-- 동적 생성 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeDetailModal()">닫기</button>
|
||||
<div class="tbm-modal-footer">
|
||||
<button type="button" class="tbm-btn tbm-btn-secondary" onclick="closeDetailModal()">닫기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -612,7 +661,6 @@
|
||||
<div class="toast-container" id="toastContainer"></div>
|
||||
</div>
|
||||
|
||||
<script type="module" src="/js/load-navbar.js?v=5"></script>
|
||||
<script type="module" src="/js/tbm.js?v=3"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user