/* ===== Visit Management (출입 관리 - 관리자) ===== */ let allRequests = []; let actionRequestId = null; /* ===== Status badge for visit requests ===== */ function vrStatusBadge(s) { const m = { pending: ['badge-amber', '대기중'], approved: ['badge-green', '승인됨'], rejected: ['badge-red', '반려됨'], training_completed: ['badge-blue', '교육완료'] }; const [cls, label] = m[s] || ['badge-gray', s]; return `${label}`; } /* ===== Load requests ===== */ async function loadRequests() { try { const params = new URLSearchParams(); const status = document.getElementById('filterStatus').value; const dateFrom = document.getElementById('filterDateFrom').value; const dateTo = document.getElementById('filterDateTo').value; if (status) params.set('status', status); if (dateFrom) params.set('start_date', dateFrom); if (dateTo) params.set('end_date', dateTo); const res = await api('/visit-requests/requests?' + params.toString()); allRequests = res.data || []; renderStats(); renderRequestsTable(); } catch (e) { showToast('데이터 로드 실패: ' + e.message, 'error'); } } function renderStats() { const counts = { pending: 0, approved: 0, rejected: 0, training_completed: 0 }; allRequests.forEach(r => { if (counts[r.status] !== undefined) counts[r.status]++; }); document.getElementById('statPending').textContent = counts.pending; document.getElementById('statApproved').textContent = counts.approved; document.getElementById('statRejected').textContent = counts.rejected; document.getElementById('statTrainingDone').textContent = counts.training_completed; } function renderRequestsTable() { const tbody = document.getElementById('requestsTableBody'); if (!allRequests.length) { tbody.innerHTML = '신청 내역이 없습니다'; return; } tbody.innerHTML = allRequests.map(r => { let actions = ''; if (r.status === 'pending') { actions = ` `; } actions += ` `; if (r.status === 'pending') { actions += ` `; } return ` ${formatDate(r.created_at)} ${escapeHtml(r.requester_full_name || r.requester_name || '-')} ${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 || '-')} ${vrStatusBadge(r.status)} ${actions} `; }).join(''); } /* ===== Approve Modal ===== */ function openApproveModal(id) { const r = allRequests.find(x => x.request_id === id); if (!r) return; actionRequestId = id; document.getElementById('approveDetail').innerHTML = `

업체: ${escapeHtml(r.visitor_company)}

방문일: ${formatDate(r.visit_date)} ${r.visit_time ? String(r.visit_time).substring(0, 5) : ''}

작업장: ${escapeHtml(r.workplace_name || '-')}

인원: ${r.visitor_count}명

이 출입 신청을 승인하시겠습니까?

`; document.getElementById('approveModal').classList.remove('hidden'); } function closeApproveModal() { document.getElementById('approveModal').classList.add('hidden'); actionRequestId = null; } async function confirmApprove() { if (!actionRequestId) return; try { await api('/visit-requests/requests/' + actionRequestId + '/approve', { method: 'PUT', body: JSON.stringify({}) }); showToast('승인되었습니다'); closeApproveModal(); await loadRequests(); } catch (e) { showToast(e.message, 'error'); } } /* ===== Reject Modal ===== */ function openRejectModal(id) { const r = allRequests.find(x => x.request_id === id); if (!r) return; actionRequestId = id; document.getElementById('rejectDetail').innerHTML = `

업체: ${escapeHtml(r.visitor_company)}

방문일: ${formatDate(r.visit_date)} ${r.visit_time ? String(r.visit_time).substring(0, 5) : ''}

작업장: ${escapeHtml(r.workplace_name || '-')}

`; document.getElementById('rejectionReason').value = ''; document.getElementById('rejectModal').classList.remove('hidden'); } function closeRejectModal() { document.getElementById('rejectModal').classList.add('hidden'); actionRequestId = null; } async function confirmReject() { if (!actionRequestId) return; const reason = document.getElementById('rejectionReason').value.trim(); if (!reason) { showToast('반려 사유를 입력해주세요', 'error'); return; } try { await api('/visit-requests/requests/' + actionRequestId + '/reject', { method: 'PUT', body: JSON.stringify({ rejection_reason: reason }) }); showToast('반려되었습니다'); closeRejectModal(); await loadRequests(); } catch (e) { showToast(e.message, 'error'); } } /* ===== Detail Modal ===== */ function openDetailModal(id) { const r = allRequests.find(x => x.request_id === id); if (!r) return; document.getElementById('detailContent').innerHTML = `
신청자: ${escapeHtml(r.requester_full_name || r.requester_name || '-')}
업체: ${escapeHtml(r.visitor_company)}
인원: ${r.visitor_count}명
분류: ${escapeHtml(r.category_name || '-')}
작업장: ${escapeHtml(r.workplace_name || '-')}
방문일: ${formatDate(r.visit_date)}
방문시간: ${r.visit_time ? String(r.visit_time).substring(0, 5) : '-'}
목적: ${escapeHtml(r.purpose_name || '-')}
상태: ${vrStatusBadge(r.status)}
신청일: ${formatDateTime(r.created_at)}
${r.approver_name ? `
처리자: ${escapeHtml(r.approver_name)}
` : ''} ${r.approved_at ? `
처리일: ${formatDateTime(r.approved_at)}
` : ''} ${r.rejection_reason ? `
반려사유: ${escapeHtml(r.rejection_reason)}
` : ''} ${r.notes ? `
비고: ${escapeHtml(r.notes)}
` : ''}
`; document.getElementById('detailModal').classList.remove('hidden'); } function closeDetailModal() { document.getElementById('detailModal').classList.add('hidden'); } /* ===== Delete request ===== */ async function doDeleteRequest(id) { if (!confirm('이 신청을 삭제하시겠습니까?')) return; try { await api('/visit-requests/requests/' + id, { method: 'DELETE' }); showToast('삭제되었습니다'); await loadRequests(); } catch (e) { showToast(e.message, 'error'); } } /* ===== Init ===== */ function initVisitManagementPage() { 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('filterStatus').addEventListener('change', loadRequests); document.getElementById('filterDateFrom').addEventListener('change', loadRequests); document.getElementById('filterDateTo').addEventListener('change', loadRequests); loadRequests(); }