/* ===== Training Management (안전교육 실시 - 관리자) ===== */ let pendingRequests = []; let completedTrainings = []; let trainingRequestId = null; /* Signature canvas state */ let sigCanvas, sigCtx; let isDrawing = false; let hasSignature = false; /* ===== Load approved requests needing training ===== */ async function loadPendingTraining() { try { const res = await api('/visit-requests/requests?status=approved'); pendingRequests = res.data || []; renderPendingTraining(); } catch (e) { showToast('대기 목록 로드 실패: ' + e.message, 'error'); } } function renderPendingTraining() { const tbody = document.getElementById('pendingTrainingBody'); if (!pendingRequests.length) { tbody.innerHTML = '교육 대기 중인 신청이 없습니다'; return; } tbody.innerHTML = pendingRequests.map(r => ` ${escapeHtml(r.visitor_company)} ${r.visitor_count} ${escapeHtml(r.workplace_name || '-')} ${formatDate(r.visit_date)} ${r.visit_time ? String(r.visit_time).substring(0, 5) : '-'} ${escapeHtml(r.purpose_name || '-')} `).join(''); } /* ===== Load completed training records ===== */ async function loadCompletedTraining() { try { const res = await api('/visit-requests/training'); completedTrainings = res.data || []; renderCompletedTraining(); } catch (e) { showToast('이력 로드 실패: ' + e.message, 'error'); } } function renderCompletedTraining() { const tbody = document.getElementById('completedTrainingBody'); if (!completedTrainings.length) { tbody.innerHTML = '교육 완료 이력이 없습니다'; return; } tbody.innerHTML = completedTrainings.map(t => { const timeRange = t.training_start_time ? String(t.training_start_time).substring(0, 5) + (t.training_end_time ? ' ~ ' + String(t.training_end_time).substring(0, 5) : '') : '-'; return ` ${formatDate(t.training_date)} ${escapeHtml(t.visitor_company || '-')} ${t.visitor_count || '-'} ${timeRange} ${escapeHtml(t.training_topics || '-')} ${escapeHtml(t.trainer_full_name || t.trainer_name || '-')} ${t.completed_at ? '완료' : '진행중'} `; }).join(''); } /* ===== Training Modal ===== */ function openTrainingModal(requestId) { const r = pendingRequests.find(x => x.request_id === requestId); if (!r) return; trainingRequestId = requestId; document.getElementById('trainingRequestInfo').innerHTML = `
업체: ${escapeHtml(r.visitor_company)}
인원: ${r.visitor_count}명
작업장: ${escapeHtml(r.workplace_name || '-')}
방문일: ${formatDate(r.visit_date)}
`; // Set defaults const today = new Date().toISOString().substring(0, 10); const now = new Date().toTimeString().substring(0, 5); document.getElementById('trainingDate').value = today; document.getElementById('trainingStartTime').value = now; document.getElementById('trainingEndTime').value = ''; document.getElementById('trainingTopics').value = ''; // Reset signature clearSignature(); document.getElementById('trainingModal').classList.remove('hidden'); } function closeTrainingModal() { document.getElementById('trainingModal').classList.add('hidden'); trainingRequestId = null; } /* ===== Submit Training ===== */ async function submitTraining(e) { e.preventDefault(); if (!trainingRequestId) return; const data = { request_id: trainingRequestId, training_date: document.getElementById('trainingDate').value, training_start_time: document.getElementById('trainingStartTime').value, training_end_time: document.getElementById('trainingEndTime').value || null, training_topics: document.getElementById('trainingTopics').value.trim() || null }; if (!data.training_date) { showToast('교육일을 선택해주세요', 'error'); return; } if (!data.training_start_time) { showToast('시작시간을 입력해주세요', 'error'); return; } try { // 1. Create training record const createRes = await api('/visit-requests/training', { method: 'POST', body: JSON.stringify(data) }); const trainingId = createRes.data?.training_id || createRes.data?.insertId; // 2. Complete with signature if exists if (trainingId && hasSignature) { const signatureData = sigCanvas.toDataURL('image/png'); await api('/visit-requests/training/' + trainingId + '/complete', { method: 'PUT', body: JSON.stringify({ signature_data: signatureData }) }); } showToast('안전교육이 완료되었습니다'); closeTrainingModal(); await Promise.all([loadPendingTraining(), loadCompletedTraining()]); } catch (e) { showToast(e.message, 'error'); } } /* ===== Signature Pad ===== */ function initSignaturePad() { sigCanvas = document.getElementById('signatureCanvas'); if (!sigCanvas) return; sigCtx = sigCanvas.getContext('2d'); // Adjust canvas resolution for retina displays const rect = sigCanvas.getBoundingClientRect(); const dpr = window.devicePixelRatio || 1; sigCanvas.width = rect.width * dpr; sigCanvas.height = rect.height * dpr; sigCtx.scale(dpr, dpr); sigCtx.lineCap = 'round'; sigCtx.lineJoin = 'round'; sigCtx.lineWidth = 2; sigCtx.strokeStyle = '#1f2937'; // Mouse events sigCanvas.addEventListener('mousedown', startDraw); sigCanvas.addEventListener('mousemove', draw); sigCanvas.addEventListener('mouseup', stopDraw); sigCanvas.addEventListener('mouseleave', stopDraw); // Touch events sigCanvas.addEventListener('touchstart', function(e) { e.preventDefault(); const touch = e.touches[0]; startDraw(touchToMouse(touch)); }); sigCanvas.addEventListener('touchmove', function(e) { e.preventDefault(); const touch = e.touches[0]; draw(touchToMouse(touch)); }); sigCanvas.addEventListener('touchend', function(e) { e.preventDefault(); stopDraw(); }); } function touchToMouse(touch) { const rect = sigCanvas.getBoundingClientRect(); return { offsetX: touch.clientX - rect.left, offsetY: touch.clientY - rect.top }; } function startDraw(e) { isDrawing = true; sigCtx.beginPath(); sigCtx.moveTo(e.offsetX, e.offsetY); } function draw(e) { if (!isDrawing) return; hasSignature = true; sigCtx.lineTo(e.offsetX, e.offsetY); sigCtx.stroke(); } function stopDraw() { isDrawing = false; } function clearSignature() { if (!sigCanvas || !sigCtx) return; const rect = sigCanvas.getBoundingClientRect(); sigCtx.clearRect(0, 0, rect.width, rect.height); hasSignature = false; } /* ===== Init ===== */ function initTrainingPage() { if (!initAuth()) return; // Check admin const isAdmin = currentUser && ['admin', 'system'].includes(currentUser.role); if (!isAdmin) { document.querySelector('.flex-1.min-w-0').innerHTML = `

관리자 권한이 필요합니다

`; return; } document.getElementById('trainingForm').addEventListener('submit', submitTraining); initSignaturePad(); loadPendingTraining(); loadCompletedTraining(); }