/** * m-inbox.js — 수신함 모바일 페이지 로직 */ var currentUser = null; var issues = []; var projects = []; var filteredIssues = []; var currentIssueId = null; var statusPhotoBase64 = null; // ===== 초기화 ===== async function initialize() { currentUser = await mCheckAuth(); if (!currentUser) return; await loadProjects(); await loadIssues(); renderBottomNav('inbox'); hideLoading(); } async function loadProjects() { try { var resp = await fetch(API_BASE_URL + '/projects/', { headers: { 'Authorization': 'Bearer ' + TokenManager.getToken() } }); if (resp.ok) { projects = await resp.json(); var sel = document.getElementById('projectFilter'); sel.innerHTML = ''; projects.forEach(function (p) { sel.innerHTML += ''; }); } } catch (e) { console.error('프로젝트 로드 실패:', e); } } async function loadIssues() { try { var pid = document.getElementById('projectFilter').value; var url = API_BASE_URL + '/inbox/' + (pid ? '?project_id=' + pid : ''); var resp = await fetch(url, { headers: { 'Authorization': 'Bearer ' + TokenManager.getToken() } }); if (resp.ok) { issues = await resp.json(); filterIssues(); await loadStatistics(); } } catch (e) { console.error('수신함 로드 실패:', e); } } async function loadStatistics() { try { var todayStart = getKSTToday(); var todayNewCount = issues.filter(function (i) { var d = getKSTDate(new Date(i.report_date)); return new Date(d.getFullYear(), d.getMonth(), d.getDate()) >= todayStart; }).length; var todayProcessedCount = 0; try { var resp = await fetch(API_BASE_URL + '/inbox/statistics', { headers: { 'Authorization': 'Bearer ' + TokenManager.getToken() } }); if (resp.ok) { var s = await resp.json(); todayProcessedCount = s.today_processed || 0; } } catch (e) {} var unresolvedCount = issues.filter(function (i) { var d = getKSTDate(new Date(i.report_date)); return new Date(d.getFullYear(), d.getMonth(), d.getDate()) < todayStart; }).length; document.getElementById('todayNewCount').textContent = todayNewCount; document.getElementById('todayProcessedCount').textContent = todayProcessedCount; document.getElementById('unresolvedCount').textContent = unresolvedCount; } catch (e) { console.error('통계 로드 오류:', e); } } function filterIssues() { var pid = document.getElementById('projectFilter').value; filteredIssues = pid ? issues.filter(function (i) { return i.project_id == pid; }) : issues.slice(); filteredIssues.sort(function (a, b) { return new Date(b.report_date) - new Date(a.report_date); }); renderIssues(); } // ===== 렌더링 ===== function renderIssues() { var container = document.getElementById('issuesList'); var empty = document.getElementById('emptyState'); if (!filteredIssues.length) { container.innerHTML = ''; empty.classList.remove('hidden'); return; } empty.classList.add('hidden'); container.innerHTML = filteredIssues.map(function (issue) { var project = projects.find(function (p) { return p.id === issue.project_id; }); var photos = getPhotoPaths(issue); var photoCount = photos.length; return '
' + '
' + '
검토 대기' + (project ? '' + escapeHtml(project.project_name) + '' : '') + '
' + 'ID: ' + issue.id + '' + '
' + '
' + escapeHtml(issue.final_description || issue.description) + '
' + '
' + '
' + '' + escapeHtml(issue.reporter?.username || '알 수 없음') + '' + '' + getCategoryText(issue.category || issue.final_category) + '' + '' + (photoCount > 0 ? photoCount + '장' : '없음') + '' + '' + getTimeAgo(issue.report_date) + '' + '
' + (photos.length ? renderPhotoThumbs(photos) : '') + (issue.detail_notes ? '
"' + escapeHtml(issue.detail_notes) + '"
' : '') + '
' + '
' + '' + '' + '' + '
' + '
'; }).join(''); } // ===== 폐기 ===== function openDisposeSheet(issueId) { currentIssueId = issueId; document.getElementById('disposalReason').value = 'duplicate'; document.getElementById('customReason').value = ''; document.getElementById('customReasonDiv').classList.add('hidden'); document.getElementById('selectedDuplicateId').value = ''; toggleDisposalFields(); openSheet('dispose'); loadManagementIssues(); } function toggleDisposalFields() { var reason = document.getElementById('disposalReason').value; document.getElementById('customReasonDiv').classList.toggle('hidden', reason !== 'custom'); document.getElementById('duplicateDiv').classList.toggle('hidden', reason !== 'duplicate'); if (reason === 'duplicate') loadManagementIssues(); } async function loadManagementIssues() { var issue = issues.find(function (i) { return i.id === currentIssueId; }); var pid = issue ? issue.project_id : null; try { var resp = await fetch(API_BASE_URL + '/inbox/management-issues' + (pid ? '?project_id=' + pid : ''), { headers: { 'Authorization': 'Bearer ' + TokenManager.getToken() } }); if (!resp.ok) throw new Error('로드 실패'); var list = await resp.json(); var container = document.getElementById('managementIssuesList'); if (!list.length) { container.innerHTML = '
동일 프로젝트의 관리함 이슈가 없습니다.
'; return; } container.innerHTML = list.map(function (mi) { return '
' + '
' + escapeHtml(mi.description || mi.final_description) + '
' + '
' + '' + getCategoryText(mi.category || mi.final_category) + '' + '신고자: ' + escapeHtml(mi.reporter_name) + '' + 'ID: ' + mi.id + '' + '
'; }).join(''); } catch (e) { document.getElementById('managementIssuesList').innerHTML = '
목록 로드 실패
'; } } function selectDuplicate(id, el) { var items = document.getElementById('managementIssuesList').children; for (var i = 0; i < items.length; i++) items[i].style.background = ''; el.style.background = '#eff6ff'; document.getElementById('selectedDuplicateId').value = id; } async function confirmDispose() { if (!currentIssueId) return; var reason = document.getElementById('disposalReason').value; var customReason = document.getElementById('customReason').value; var duplicateId = document.getElementById('selectedDuplicateId').value; if (reason === 'custom' && !customReason.trim()) { showToast('폐기 사유를 입력해주세요.', 'warning'); return; } if (reason === 'duplicate' && !duplicateId) { showToast('중복 대상을 선택해주세요.', 'warning'); return; } try { var body = { disposal_reason: reason, custom_disposal_reason: reason === 'custom' ? customReason : null }; if (reason === 'duplicate' && duplicateId) body.duplicate_of_issue_id = parseInt(duplicateId); var resp = await fetch(API_BASE_URL + '/inbox/' + currentIssueId + '/dispose', { method: 'POST', headers: { 'Authorization': 'Bearer ' + TokenManager.getToken(), 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (resp.ok) { showToast('폐기 처리되었습니다.', 'success'); closeSheet('dispose'); await loadIssues(); } else { var err = await resp.json(); throw new Error(err.detail || '폐기 실패'); } } catch (e) { showToast('오류: ' + e.message, 'error'); } } // ===== 검토 ===== function openReviewSheet(issueId) { currentIssueId = issueId; var issue = issues.find(function (i) { return i.id === issueId; }); if (!issue) return; var project = projects.find(function (p) { return p.id === issue.project_id; }); // 원본 정보 document.getElementById('originalInfo').innerHTML = '
프로젝트: ' + (project ? escapeHtml(project.project_name) : '미지정') + '
' + '
신고자: ' + escapeHtml(issue.reporter?.username || '알 수 없음') + '
' + '
등록일: ' + formatKSTDate(issue.report_date) + '
'; // 프로젝트 select var sel = document.getElementById('reviewProjectId'); sel.innerHTML = ''; projects.forEach(function (p) { sel.innerHTML += ''; }); document.getElementById('reviewCategory').value = issue.category || issue.final_category || 'etc'; var desc = issue.description || issue.final_description || ''; var lines = desc.split('\n'); document.getElementById('reviewTitle').value = lines[0] || ''; document.getElementById('reviewDescription').value = lines.slice(1).join('\n') || desc; openSheet('review'); } async function saveReview() { if (!currentIssueId) return; var projectId = document.getElementById('reviewProjectId').value; var category = document.getElementById('reviewCategory').value; var title = document.getElementById('reviewTitle').value.trim(); var description = document.getElementById('reviewDescription').value.trim(); if (!title) { showToast('부적합명을 입력해주세요.', 'warning'); return; } var combined = title + (description ? '\n' + description : ''); try { var resp = await fetch(API_BASE_URL + '/inbox/' + currentIssueId + '/review', { method: 'POST', headers: { 'Authorization': 'Bearer ' + TokenManager.getToken(), 'Content-Type': 'application/json' }, body: JSON.stringify({ project_id: projectId ? parseInt(projectId) : null, category: category, description: combined }) }); if (resp.ok) { showToast('검토가 완료되었습니다.', 'success'); closeSheet('review'); await loadIssues(); } else { var err = await resp.json(); throw new Error(err.detail || '검토 실패'); } } catch (e) { showToast('오류: ' + e.message, 'error'); } } // ===== 확인 (상태 결정) ===== function openStatusSheet(issueId) { currentIssueId = issueId; document.querySelectorAll('input[name="finalStatus"]').forEach(function (r) { r.checked = false; }); document.querySelectorAll('.m-radio-item').forEach(function (el) { el.classList.remove('selected'); }); document.getElementById('completionSection').classList.add('hidden'); statusPhotoBase64 = null; document.getElementById('statusPhotoInput').value = ''; document.getElementById('statusPhotoPreview').classList.add('hidden'); document.getElementById('solutionInput').value = ''; document.getElementById('responsibleDepartmentInput').value = ''; document.getElementById('responsiblePersonInput').value = ''; openSheet('status'); } function selectStatus(value) { document.querySelectorAll('.m-radio-item').forEach(function (el) { el.classList.remove('selected'); }); var radio = document.querySelector('input[name="finalStatus"][value="' + value + '"]'); if (radio) { radio.checked = true; radio.closest('.m-radio-item').classList.add('selected'); } document.getElementById('completionSection').classList.toggle('hidden', value !== 'completed'); } function handleStatusPhoto(event) { var file = event.target.files[0]; if (!file) return; if (file.size > 5 * 1024 * 1024) { showToast('5MB 이하 파일만 가능합니다.', 'warning'); event.target.value = ''; return; } var reader = new FileReader(); reader.onload = function (e) { statusPhotoBase64 = e.target.result.split(',')[1]; var preview = document.getElementById('statusPhotoPreview'); preview.src = e.target.result; preview.classList.remove('hidden'); }; reader.readAsDataURL(file); } async function confirmStatus() { if (!currentIssueId) return; var selected = document.querySelector('input[name="finalStatus"]:checked'); if (!selected) { showToast('상태를 선택해주세요.', 'warning'); return; } var reviewStatus = selected.value; var body = { review_status: reviewStatus }; if (reviewStatus === 'completed') { var solution = document.getElementById('solutionInput').value.trim(); var dept = document.getElementById('responsibleDepartmentInput').value; var person = document.getElementById('responsiblePersonInput').value.trim(); if (solution) body.solution = solution; if (dept) body.responsible_department = dept; if (person) body.responsible_person = person; if (statusPhotoBase64) body.completion_photo = statusPhotoBase64; } try { var resp = await fetch(API_BASE_URL + '/inbox/' + currentIssueId + '/status', { method: 'POST', headers: { 'Authorization': 'Bearer ' + TokenManager.getToken(), 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (resp.ok) { showToast('상태가 변경되었습니다.', 'success'); closeSheet('status'); await loadIssues(); } else { var err = await resp.json(); throw new Error(err.detail || '상태 변경 실패'); } } catch (e) { showToast('오류: ' + e.message, 'error'); } } // ===== 시작 ===== document.addEventListener('DOMContentLoaded', initialize);