/** * 신고 상세 페이지 JavaScript */ const API_BASE = window.API_BASE_URL || 'http://localhost:20005/api'; let reportId = null; let reportData = null; let currentUser = null; // 상태 한글명 const statusNames = { reported: '신고', received: '접수', in_progress: '처리중', completed: '완료', closed: '종료' }; // 유형 한글명 const typeNames = { nonconformity: '부적합', safety: '안전' }; // 심각도 한글명 const severityNames = { critical: '심각', high: '높음', medium: '보통', low: '낮음' }; // 초기화 document.addEventListener('DOMContentLoaded', async () => { // URL에서 ID 가져오기 const urlParams = new URLSearchParams(window.location.search); reportId = urlParams.get('id'); if (!reportId) { alert('신고 ID가 없습니다.'); goBackToList(); return; } // 현재 사용자 정보 로드 await loadCurrentUser(); // 상세 데이터 로드 await loadReportDetail(); }); /** * 현재 사용자 정보 로드 */ async function loadCurrentUser() { try { const response = await fetch(`${API_BASE}/users/me`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); if (response.ok) { const data = await response.json(); currentUser = data.data; } } catch (error) { console.error('사용자 정보 로드 실패:', error); } } /** * 신고 상세 로드 */ async function loadReportDetail() { try { const response = await fetch(`${API_BASE}/work-issues/${reportId}`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); if (!response.ok) { throw new Error('신고를 찾을 수 없습니다.'); } const data = await response.json(); if (!data.success) { throw new Error(data.error || '데이터 조회 실패'); } reportData = data.data; renderDetail(); await loadStatusLogs(); } catch (error) { console.error('상세 로드 실패:', error); alert(error.message); goBackToList(); } } /** * 상세 정보 렌더링 */ function renderDetail() { const d = reportData; // 헤더 document.getElementById('reportId').textContent = `#${d.report_id}`; document.getElementById('reportTitle').textContent = d.issue_item_name || d.issue_category_name || '신고'; // 상태 배지 const statusBadge = document.getElementById('statusBadge'); statusBadge.className = `status-badge ${d.status}`; statusBadge.textContent = statusNames[d.status] || d.status; // 기본 정보 renderBasicInfo(d); // 신고 내용 renderIssueContent(d); // 사진 renderPhotos(d); // 처리 정보 renderProcessInfo(d); // 액션 버튼 renderActionButtons(d); } /** * 기본 정보 렌더링 */ function renderBasicInfo(d) { const container = document.getElementById('basicInfo'); const formatDate = (dateStr) => { if (!dateStr) return '-'; const date = new Date(dateStr); return date.toLocaleDateString('ko-KR', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); }; container.innerHTML = `
신고 유형
${typeNames[d.category_type] || d.category_type}
신고일시
${formatDate(d.report_date)}
신고자
${d.reporter_full_name || d.reporter_name || '-'}
위치
${d.custom_location || d.workplace_name || '-'}${d.factory_name ? ` (${d.factory_name})` : ''}
`; } /** * 신고 내용 렌더링 */ function renderIssueContent(d) { const container = document.getElementById('issueContent'); let html = `
카테고리
${d.issue_category_name || '-'}
항목
${d.issue_item_name || '-'} ${d.severity ? `${severityNames[d.severity]}` : ''}
`; if (d.additional_description) { html += `
${escapeHtml(d.additional_description)}
`; } container.innerHTML = html; } /** * 사진 렌더링 */ function renderPhotos(d) { const section = document.getElementById('photoSection'); const gallery = document.getElementById('photoGallery'); const photos = [d.photo_path1, d.photo_path2, d.photo_path3, d.photo_path4, d.photo_path5].filter(Boolean); if (photos.length === 0) { section.style.display = 'none'; return; } section.style.display = 'block'; const baseUrl = (API_BASE).replace('/api', ''); gallery.innerHTML = photos.map(photo => { const fullUrl = photo.startsWith('http') ? photo : `${baseUrl}${photo}`; return `
첨부 사진
`; }).join(''); } /** * 처리 정보 렌더링 */ function renderProcessInfo(d) { const section = document.getElementById('processSection'); const container = document.getElementById('processInfo'); // 담당자 배정 또는 처리 정보가 있는 경우만 표시 if (!d.assigned_user_id && !d.resolution_notes) { section.style.display = 'none'; return; } section.style.display = 'block'; const formatDate = (dateStr) => { if (!dateStr) return '-'; const date = new Date(dateStr); return date.toLocaleDateString('ko-KR', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); }; let html = '
'; if (d.assigned_user_id) { html += `
담당자
${d.assigned_full_name || d.assigned_user_name || '-'}
담당 부서
${d.assigned_department || '-'}
`; } if (d.resolved_at) { html += `
처리 완료일
${formatDate(d.resolved_at)}
처리자
${d.resolved_by_name || '-'}
`; } html += '
'; if (d.resolution_notes) { html += `
처리 내용
${escapeHtml(d.resolution_notes)}
`; } container.innerHTML = html; } /** * 액션 버튼 렌더링 */ function renderActionButtons(d) { const container = document.getElementById('actionButtons'); if (!currentUser) { container.innerHTML = ''; return; } const isAdmin = ['admin', 'system', 'support_team'].includes(currentUser.access_level); const isOwner = d.reporter_id === currentUser.user_id; const isAssignee = d.assigned_user_id === currentUser.user_id; let buttons = []; // 관리자 권한 버튼 if (isAdmin) { if (d.status === 'reported') { buttons.push(``); } if (d.status === 'received' || d.status === 'in_progress') { buttons.push(``); } if (d.status === 'received') { buttons.push(``); } if (d.status === 'in_progress') { buttons.push(``); } if (d.status === 'completed') { buttons.push(``); } } // 담당자 버튼 if (isAssignee && !isAdmin) { if (d.status === 'received') { buttons.push(``); } if (d.status === 'in_progress') { buttons.push(``); } } // 신고자 버튼 (수정/삭제는 reported 상태에서만) if (isOwner && d.status === 'reported') { buttons.push(``); } container.innerHTML = buttons.join(''); } /** * 상태 변경 이력 로드 */ async function loadStatusLogs() { try { const response = await fetch(`${API_BASE}/work-issues/${reportId}/status-logs`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); if (!response.ok) return; const data = await response.json(); if (data.success && data.data) { renderStatusTimeline(data.data); } } catch (error) { console.error('상태 이력 로드 실패:', error); } } /** * 상태 타임라인 렌더링 */ function renderStatusTimeline(logs) { const container = document.getElementById('statusTimeline'); if (!logs || logs.length === 0) { container.innerHTML = '

상태 변경 이력이 없습니다.

'; return; } const formatDate = (dateStr) => { const date = new Date(dateStr); return date.toLocaleDateString('ko-KR', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); }; container.innerHTML = logs.map(log => `
${log.previous_status ? `${statusNames[log.previous_status]} → ` : ''}${statusNames[log.new_status]}
${log.changed_by_full_name || log.changed_by_name} | ${formatDate(log.changed_at)} ${log.change_reason ? `
${escapeHtml(log.change_reason)}` : ''}
`).join(''); } // ==================== 액션 함수 ==================== /** * 신고 접수 */ async function receiveReport() { if (!confirm('이 신고를 접수하시겠습니까?')) return; try { const response = await fetch(`${API_BASE}/work-issues/${reportId}/receive`, { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); const data = await response.json(); if (data.success) { alert('신고가 접수되었습니다.'); location.reload(); } else { throw new Error(data.error || '접수 실패'); } } catch (error) { alert('접수 실패: ' + error.message); } } /** * 처리 시작 */ async function startProcessing() { if (!confirm('처리를 시작하시겠습니까?')) return; try { const response = await fetch(`${API_BASE}/work-issues/${reportId}/start`, { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); const data = await response.json(); if (data.success) { alert('처리가 시작되었습니다.'); location.reload(); } else { throw new Error(data.error || '처리 시작 실패'); } } catch (error) { alert('처리 시작 실패: ' + error.message); } } /** * 신고 종료 */ async function closeReport() { if (!confirm('이 신고를 종료하시겠습니까?')) return; try { const response = await fetch(`${API_BASE}/work-issues/${reportId}/close`, { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); const data = await response.json(); if (data.success) { alert('신고가 종료되었습니다.'); location.reload(); } else { throw new Error(data.error || '종료 실패'); } } catch (error) { alert('종료 실패: ' + error.message); } } /** * 신고 삭제 */ async function deleteReport() { if (!confirm('정말 이 신고를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.')) return; try { const response = await fetch(`${API_BASE}/work-issues/${reportId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); const data = await response.json(); if (data.success) { alert('신고가 삭제되었습니다.'); goBackToList(); } else { throw new Error(data.error || '삭제 실패'); } } catch (error) { alert('삭제 실패: ' + error.message); } } // ==================== 담당자 배정 모달 ==================== async function openAssignModal() { // 사용자 목록 로드 try { const response = await fetch(`${API_BASE}/users`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); if (response.ok) { const data = await response.json(); const select = document.getElementById('assignUser'); select.innerHTML = ''; if (data.success && data.data) { data.data.forEach(user => { select.innerHTML += ``; }); } } } catch (error) { console.error('사용자 목록 로드 실패:', error); } document.getElementById('assignModal').classList.add('visible'); } function closeAssignModal() { document.getElementById('assignModal').classList.remove('visible'); } async function submitAssign() { const department = document.getElementById('assignDepartment').value; const userId = document.getElementById('assignUser').value; if (!userId) { alert('담당자를 선택해주세요.'); return; } try { const response = await fetch(`${API_BASE}/work-issues/${reportId}/assign`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: JSON.stringify({ assigned_department: department, assigned_user_id: parseInt(userId) }) }); const data = await response.json(); if (data.success) { alert('담당자가 배정되었습니다.'); closeAssignModal(); location.reload(); } else { throw new Error(data.error || '배정 실패'); } } catch (error) { alert('담당자 배정 실패: ' + error.message); } } // ==================== 처리 완료 모달 ==================== function openCompleteModal() { document.getElementById('completeModal').classList.add('visible'); } function closeCompleteModal() { document.getElementById('completeModal').classList.remove('visible'); } async function submitComplete() { const notes = document.getElementById('resolutionNotes').value; if (!notes.trim()) { alert('처리 내용을 입력해주세요.'); return; } try { const response = await fetch(`${API_BASE}/work-issues/${reportId}/complete`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: JSON.stringify({ resolution_notes: notes }) }); const data = await response.json(); if (data.success) { alert('처리가 완료되었습니다.'); closeCompleteModal(); location.reload(); } else { throw new Error(data.error || '완료 처리 실패'); } } catch (error) { alert('처리 완료 실패: ' + error.message); } } // ==================== 사진 모달 ==================== function openPhotoModal(src) { document.getElementById('photoModalImg').src = src; document.getElementById('photoModal').classList.add('visible'); } function closePhotoModal() { document.getElementById('photoModal').classList.remove('visible'); } // ==================== 유틸리티 ==================== function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * 목록으로 돌아가기 */ function goBackToList() { const urlParams = new URLSearchParams(window.location.search); const from = urlParams.get('from'); if (from === 'nonconformity') { window.location.href = '/pages/work/nonconformity.html'; } else if (from === 'safety') { window.location.href = '/pages/safety/report-status.html'; } else { if (window.history.length > 1) { window.history.back(); } else { window.location.href = '/pages/safety/report-status.html'; } } } // 전역 함수 노출 window.goBackToList = goBackToList; window.receiveReport = receiveReport; window.startProcessing = startProcessing; window.closeReport = closeReport; window.deleteReport = deleteReport; window.openAssignModal = openAssignModal; window.closeAssignModal = closeAssignModal; window.submitAssign = submitAssign; window.openCompleteModal = openCompleteModal; window.closeCompleteModal = closeCompleteModal; window.submitComplete = submitComplete; window.openPhotoModal = openPhotoModal; window.closePhotoModal = closePhotoModal;