- 실시간 작업장 현황을 지도로 시각화 - 작업장 관리 페이지에서 정의한 구역 정보 활용 - TBM 작업자 및 방문자 현황 표시 주요 변경사항: - dashboard.html: 작업장 현황 섹션 추가 (기존 작업 현황 테이블 제거) - workplace-status.js: 지도 렌더링 및 데이터 통합 로직 구현 - modern-dashboard.js: 삭제된 DOM 요소 조건부 체크 추가 시각화 방식: - 인원 없음: 회색 테두리 + 작업장 이름 - 내부 작업자: 파란색 영역 + 인원 수 - 외부 방문자: 보라색 영역 + 인원 수 - 둘 다: 초록색 영역 + 총 인원 수 기술 구현: - Canvas API 기반 사각형 영역 렌더링 - map-regions API를 통한 데이터 일관성 보장 - 클릭 이벤트로 상세 정보 모달 표시 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
328 lines
12 KiB
HTML
328 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>안전교육 진행 | (주)테크니컬코리아</title>
|
|
<link rel="stylesheet" href="/css/design-system.css">
|
|
<link rel="stylesheet" href="/css/common.css?v=2">
|
|
<link rel="stylesheet" href="/css/project-management.css?v=3">
|
|
<link rel="icon" type="image/png" href="/img/favicon.png">
|
|
<script src="/js/auth-check.js?v=1" defer></script>
|
|
<script type="module" src="/js/api-config.js?v=3"></script>
|
|
<style>
|
|
.training-container {
|
|
max-width: 1000px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.request-info-card {
|
|
background: white;
|
|
border-radius: var(--radius-lg);
|
|
padding: 24px;
|
|
margin-bottom: 24px;
|
|
box-shadow: var(--shadow-sm);
|
|
border-left: 4px solid var(--primary-500);
|
|
}
|
|
|
|
.info-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 16px;
|
|
}
|
|
|
|
.info-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.info-label {
|
|
font-size: var(--text-sm);
|
|
color: var(--gray-600);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.info-value {
|
|
font-size: var(--text-base);
|
|
font-weight: 600;
|
|
color: var(--gray-900);
|
|
}
|
|
|
|
.checklist-section {
|
|
background: white;
|
|
border-radius: var(--radius-lg);
|
|
padding: 24px;
|
|
margin-bottom: 24px;
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
.checklist-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 16px;
|
|
margin-bottom: 12px;
|
|
background: var(--gray-50);
|
|
border-radius: var(--radius-md);
|
|
border: 2px solid transparent;
|
|
transition: all var(--transition-fast);
|
|
}
|
|
|
|
.checklist-item:hover {
|
|
border-color: var(--primary-300);
|
|
background: var(--primary-50);
|
|
}
|
|
|
|
.checklist-item input[type="checkbox"] {
|
|
width: 24px;
|
|
height: 24px;
|
|
margin-right: 16px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.checklist-item label {
|
|
flex: 1;
|
|
font-size: var(--text-base);
|
|
cursor: pointer;
|
|
user-select: none;
|
|
}
|
|
|
|
.checklist-item input[type="checkbox"]:checked + label {
|
|
color: var(--gray-500);
|
|
text-decoration: line-through;
|
|
}
|
|
|
|
.signature-section {
|
|
background: white;
|
|
border-radius: var(--radius-lg);
|
|
padding: 24px;
|
|
margin-bottom: 24px;
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
.signature-canvas-container {
|
|
border: 2px solid var(--gray-300);
|
|
border-radius: var(--radius-md);
|
|
background: white;
|
|
margin-top: 16px;
|
|
position: relative;
|
|
touch-action: none;
|
|
}
|
|
|
|
.signature-canvas {
|
|
display: block;
|
|
cursor: crosshair;
|
|
touch-action: none;
|
|
}
|
|
|
|
.signature-actions {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-top: 12px;
|
|
}
|
|
|
|
.signature-placeholder {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
color: var(--gray-400);
|
|
font-size: var(--text-base);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.form-actions {
|
|
display: flex;
|
|
gap: 12px;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.warning-box {
|
|
background: var(--yellow-50);
|
|
border: 2px solid var(--yellow-300);
|
|
border-radius: var(--radius-md);
|
|
padding: 16px;
|
|
margin-bottom: 24px;
|
|
display: flex;
|
|
align-items: start;
|
|
gap: 12px;
|
|
}
|
|
|
|
.warning-icon {
|
|
font-size: 24px;
|
|
}
|
|
|
|
.warning-text {
|
|
flex: 1;
|
|
color: var(--yellow-800);
|
|
font-size: var(--text-sm);
|
|
}
|
|
|
|
.saved-signature-card {
|
|
background: var(--gray-50);
|
|
border: 2px solid var(--gray-300);
|
|
border-radius: var(--radius-md);
|
|
padding: 16px;
|
|
margin-bottom: 12px;
|
|
display: flex;
|
|
gap: 16px;
|
|
align-items: center;
|
|
}
|
|
|
|
.saved-signature-card img {
|
|
max-width: 300px;
|
|
height: auto;
|
|
border: 1px solid var(--gray-300);
|
|
border-radius: var(--radius-sm);
|
|
background: white;
|
|
}
|
|
|
|
.saved-signature-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.saved-signature-number {
|
|
font-size: var(--text-lg);
|
|
font-weight: 700;
|
|
color: var(--primary-600);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.saved-signature-date {
|
|
font-size: var(--text-sm);
|
|
color: var(--gray-600);
|
|
}
|
|
|
|
.saved-signature-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
</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="training-container">
|
|
<!-- 출입 신청 정보 -->
|
|
<div class="request-info-card">
|
|
<h2 class="section-title" style="margin-bottom: 16px;">출입 신청 정보</h2>
|
|
<div id="requestInfo" class="info-grid">
|
|
<!-- 동적으로 로드됨 -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 안전교육 체크리스트 -->
|
|
<div class="checklist-section">
|
|
<h2 class="section-title" style="margin-bottom: 16px;">안전교육 체크리스트</h2>
|
|
<p style="color: var(--gray-600); margin-bottom: 20px;">
|
|
방문자에게 다음 안전 사항을 교육하고 체크해주세요.
|
|
</p>
|
|
|
|
<div id="checklistContainer">
|
|
<div class="checklist-item">
|
|
<input type="checkbox" id="check1" name="safety-check" value="개인보호구 착용" onchange="updateCompleteButton()">
|
|
<label for="check1">개인보호구(안전모, 안전화, 안전복) 착용 방법 교육</label>
|
|
</div>
|
|
<div class="checklist-item">
|
|
<input type="checkbox" id="check2" name="safety-check" value="작업장 위험요소" onchange="updateCompleteButton()">
|
|
<label for="check2">작업장 내 위험요소 및 주의사항 안내</label>
|
|
</div>
|
|
<div class="checklist-item">
|
|
<input type="checkbox" id="check3" name="safety-check" value="비상대피로" onchange="updateCompleteButton()">
|
|
<label for="check3">비상대피로 및 비상연락망 안내</label>
|
|
</div>
|
|
<div class="checklist-item">
|
|
<input type="checkbox" id="check4" name="safety-check" value="출입통제구역" onchange="updateCompleteButton()">
|
|
<label for="check4">출입통제구역 및 금지사항 안내</label>
|
|
</div>
|
|
<div class="checklist-item">
|
|
<input type="checkbox" id="check5" name="safety-check" value="사고발생시 대응" onchange="updateCompleteButton()">
|
|
<label for="check5">사고 발생 시 대응 절차 교육</label>
|
|
</div>
|
|
<div class="checklist-item">
|
|
<input type="checkbox" id="check6" name="safety-check" value="안전수칙 준수" onchange="updateCompleteButton()">
|
|
<label for="check6">현장 안전수칙 준수 서약</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 경고 -->
|
|
<div class="warning-box">
|
|
<div class="warning-icon">⚠️</div>
|
|
<div class="warning-text">
|
|
<strong>중요:</strong> 모든 체크리스트 항목을 완료하고 방문자의 서명을 받은 후 교육 완료 처리를 해주세요.
|
|
교육 완료 후에는 수정할 수 없습니다.
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 서명 섹션 -->
|
|
<div class="signature-section">
|
|
<h2 class="section-title" style="margin-bottom: 16px;">방문자 서명 (<span id="signatureCount">0</span>명)</h2>
|
|
<p style="color: var(--gray-600); margin-bottom: 20px;">
|
|
각 방문자가 왼쪽에 이름을 쓰고 오른쪽에 서명한 후 "저장" 버튼을 눌러주세요.
|
|
</p>
|
|
|
|
<div class="signature-canvas-container" style="position: relative;">
|
|
<!-- 이름과 서명 구분선 및 라벨 -->
|
|
<div style="position: absolute; top: 10px; left: 10px; right: 10px; display: flex; justify-content: space-between; z-index: 1; pointer-events: none;">
|
|
<span style="font-size: var(--text-sm); color: var(--gray-500); font-weight: 600;">이름</span>
|
|
<span style="position: absolute; left: 250px; top: 0; bottom: 0; width: 2px; background: var(--gray-300);"></span>
|
|
<span style="font-size: var(--text-sm); color: var(--gray-500); font-weight: 600); margin-left: auto;">서명</span>
|
|
</div>
|
|
<canvas id="signatureCanvas" class="signature-canvas" width="800" height="300"></canvas>
|
|
<div id="signaturePlaceholder" class="signature-placeholder" style="display: flex; flex-direction: column; align-items: center; gap: 8px;">
|
|
<div>왼쪽에 이름을 쓰고, 오른쪽에 서명해주세요</div>
|
|
<div style="font-size: var(--text-sm); color: var(--gray-400);">(마우스, 터치, 또는 Apple Pencil 사용)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="signature-actions">
|
|
<button type="button" class="btn btn-secondary" onclick="clearSignature()">
|
|
서명 지우기
|
|
</button>
|
|
<button type="button" class="btn btn-primary" onclick="saveSignature()">
|
|
서명 저장
|
|
</button>
|
|
</div>
|
|
|
|
<div style="font-size: var(--text-sm); color: var(--gray-600); margin-top: 12px;">
|
|
서명 날짜: <span id="signatureDate"></span>
|
|
</div>
|
|
|
|
<!-- 저장된 서명 목록 -->
|
|
<div id="savedSignatures" style="margin-top: 24px;">
|
|
<!-- 동적으로 추가됨 -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 제출 버튼 -->
|
|
<div class="form-actions">
|
|
<button type="button" class="btn btn-secondary" onclick="goBack()">
|
|
취소
|
|
</button>
|
|
<button type="button" class="btn btn-primary" onclick="completeTraining()" id="completeBtn" disabled>
|
|
교육 완료 처리
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<!-- Scripts -->
|
|
<script type="module" src="/js/load-navbar.js?v=5"></script>
|
|
<script src="/js/safety-training-conduct.js"></script>
|
|
</body>
|
|
</html>
|