/** * m-management.js — 관리함 모바일 페이지 로직 */ var currentUser = null; var issues = []; var projects = []; var filteredIssues = []; var currentTab = 'in_progress'; var currentIssueId = null; var rejectIssueId = null; function cleanManagementComment(text) { if (!text) return ''; return text.replace(/\[완료 반려[^\]]*\][^\n]*\n*/g, '').trim(); } // ===== 초기화 ===== async function initialize() { currentUser = await mCheckAuth(); if (!currentUser) return; await loadProjects(); await loadIssues(); renderBottomNav('management'); 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 resp = await fetch(API_BASE_URL + '/issues/admin/all', { headers: { 'Authorization': 'Bearer ' + TokenManager.getToken() } }); if (resp.ok) { var all = await resp.json(); var filtered = all.filter(function (i) { return i.review_status === 'in_progress' || i.review_status === 'completed'; }); // 프로젝트별 순번 filtered.sort(function (a, b) { return new Date(a.reviewed_at) - new Date(b.reviewed_at); }); var groups = {}; filtered.forEach(function (issue) { if (!groups[issue.project_id]) groups[issue.project_id] = []; groups[issue.project_id].push(issue); }); Object.keys(groups).forEach(function (pid) { groups[pid].forEach(function (issue, idx) { issue.project_sequence_no = idx + 1; }); }); issues = filtered; filterIssues(); } } catch (e) { console.error('이슈 로드 실패:', e); } } // ===== 탭 전환 ===== function switchTab(tab) { currentTab = tab; document.getElementById('tabInProgress').classList.toggle('active', tab === 'in_progress'); document.getElementById('tabCompleted').classList.toggle('active', tab === 'completed'); document.getElementById('additionalInfoBtn').style.display = tab === 'in_progress' ? 'flex' : 'none'; filterIssues(); } // ===== 통계 ===== function updateStatistics() { var pid = document.getElementById('projectFilter').value; var pi = pid ? issues.filter(function (i) { return i.project_id == pid; }) : issues; document.getElementById('totalCount').textContent = pi.length; document.getElementById('inProgressCount').textContent = pi.filter(function (i) { return i.review_status === 'in_progress' && !i.completion_requested_at; }).length; document.getElementById('pendingCompletionCount').textContent = pi.filter(function (i) { return i.review_status === 'in_progress' && i.completion_requested_at; }).length; document.getElementById('completedCount').textContent = pi.filter(function (i) { return i.review_status === 'completed'; }).length; } // ===== 필터 ===== function filterIssues() { var pid = document.getElementById('projectFilter').value; filteredIssues = issues.filter(function (i) { if (i.review_status !== currentTab) return false; if (pid && i.project_id != pid) return false; return true; }); filteredIssues.sort(function (a, b) { return new Date(b.report_date) - new Date(a.report_date); }); renderIssues(); updateStatistics(); } // ===== 이슈 상태 ===== function getIssueStatus(issue) { if (issue.review_status === 'completed') return 'completed'; if (issue.completion_requested_at) return 'pending_completion'; if (issue.expected_completion_date) { var diff = (new Date(issue.expected_completion_date) - new Date()) / 86400000; if (diff < 0) return 'overdue'; if (diff <= 3) return 'urgent'; } return 'in_progress'; } function getStatusBadgeHtml(status) { var map = { 'in_progress': ' 진행 중', 'urgent': ' 긴급', 'overdue': ' 지연됨', 'pending_completion': ' 완료 대기', 'completed': ' 완료됨' }; return map[status] || map['in_progress']; } // ===== 렌더링 ===== 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'); // 날짜별 그룹 var grouped = {}; var dateObjs = {}; filteredIssues.forEach(function (issue) { var dateToUse = currentTab === 'completed' ? (issue.actual_completion_date || issue.report_date) : issue.report_date; var d = new Date(dateToUse); var key = d.toLocaleDateString('ko-KR'); if (!grouped[key]) { grouped[key] = []; dateObjs[key] = d; } grouped[key].push(issue); }); var html = Object.keys(grouped) .sort(function (a, b) { return dateObjs[b] - dateObjs[a]; }) .map(function (dateKey) { var issues = grouped[dateKey]; return '
' + '' + '' + dateKey + '' + '(' + issues.length + '건)' + '' + (currentTab === 'in_progress' ? '업로드일' : '완료일') + '' + '
' + issues.map(function (issue) { return currentTab === 'in_progress' ? renderInProgressCard(issue) : renderCompletedCard(issue); }).join('') + '
'; }).join(''); container.innerHTML = html; } function renderInProgressCard(issue) { var project = projects.find(function (p) { return p.id === issue.project_id; }); var status = getIssueStatus(issue); var isPending = status === 'pending_completion'; var photos = getPhotoPaths(issue); // 관리 필드 표시 var mgmtHtml = '
' + '
해결방안: ' + escapeHtml(cleanManagementComment(issue.management_comment) || '-') + '
' + '
담당부서: ' + getDepartmentText(issue.responsible_department) + '
' + '
담당자: ' + escapeHtml(issue.responsible_person || '-') + '
' + '
조치예상일: ' + (issue.expected_completion_date ? formatKSTDate(issue.expected_completion_date) : '-') + '
' + '
'; // 완료 대기 정보 var completionInfoHtml = ''; if (isPending) { var cPhotos = getCompletionPhotoPaths(issue); completionInfoHtml = '
' + '
완료 신청 정보
' + (cPhotos.length ? renderPhotoThumbs(cPhotos) : '') + '
' + escapeHtml(issue.completion_comment || '코멘트 없음') + '
' + '
신청: ' + formatKSTDateTime(issue.completion_requested_at) + '
' + '
'; } // 액션 버튼 var actionHtml = ''; if (isPending) { actionHtml = '
' + '' + '' + '
'; } else { actionHtml = '
' + '' + '' + '
'; } return '
' + '
' + '
No.' + (issue.project_sequence_no || '-') + '' + '' + escapeHtml(project ? project.project_name : '미지정') + '
' + getStatusBadgeHtml(status) + '
' + '
' + escapeHtml(getIssueTitle(issue)) + '
' + '
' + '
' + escapeHtml(getIssueDetail(issue)) + '
' + '
' + '' + getCategoryText(issue.category || issue.final_category) + '' + '' + escapeHtml(issue.reporter?.full_name || issue.reporter?.username || '-') + '' + '
' + (photos.length ? renderPhotoThumbs(photos) : '') + mgmtHtml + completionInfoHtml + '
' + actionHtml + '' + '
'; } function renderCompletedCard(issue) { var project = projects.find(function (p) { return p.id === issue.project_id; }); var completedDate = issue.completed_at ? formatKSTDate(issue.completed_at) : '-'; return '
' + '
' + '
No.' + (issue.project_sequence_no || '-') + '' + '' + escapeHtml(project ? project.project_name : '미지정') + '
' + ' 완료' + '
' + '
' + escapeHtml(getIssueTitle(issue)) + '
' + '' + '
'; } // ===== 편집 시트 ===== function openEditMgmtSheet(issueId) { currentIssueId = issueId; var issue = issues.find(function (i) { return i.id === issueId; }); if (!issue) return; document.getElementById('editManagementComment').value = cleanManagementComment(issue.management_comment) || ''; document.getElementById('editResponsibleDept').value = issue.responsible_department || ''; document.getElementById('editResponsiblePerson').value = issue.responsible_person || ''; document.getElementById('editExpectedDate').value = issue.expected_completion_date ? issue.expected_completion_date.split('T')[0] : ''; openSheet('editMgmt'); } async function saveManagementEdit() { if (!currentIssueId) return; try { var updates = { management_comment: document.getElementById('editManagementComment').value.trim() || null, responsible_department: document.getElementById('editResponsibleDept').value || null, responsible_person: document.getElementById('editResponsiblePerson').value.trim() || null, expected_completion_date: document.getElementById('editExpectedDate').value ? document.getElementById('editExpectedDate').value + 'T00:00:00' : null }; var resp = await fetch(API_BASE_URL + '/issues/' + currentIssueId + '/management', { method: 'PUT', headers: { 'Authorization': 'Bearer ' + TokenManager.getToken(), 'Content-Type': 'application/json' }, body: JSON.stringify(updates) }); if (resp.ok) { showToast('저장되었습니다.', 'success'); closeSheet('editMgmt'); await loadIssues(); } else { var err = await resp.json(); throw new Error(err.detail || '저장 실패'); } } catch (e) { showToast('오류: ' + e.message, 'error'); } } // ===== 완료 처리 ===== async function confirmCompletion(issueId) { if (!confirm('완료 처리하시겠습니까?')) return; try { var resp = await fetch(API_BASE_URL + '/inbox/' + issueId + '/status', { method: 'POST', headers: { 'Authorization': 'Bearer ' + TokenManager.getToken(), 'Content-Type': 'application/json' }, body: JSON.stringify({ review_status: 'completed' }) }); if (resp.ok) { showToast('완료 처리되었습니다.', 'success'); await loadIssues(); } else { var err = await resp.json(); throw new Error(err.detail || '완료 처리 실패'); } } catch (e) { showToast('오류: ' + e.message, 'error'); } } // ===== 반려 ===== function openRejectSheet(issueId) { rejectIssueId = issueId; document.getElementById('rejectReason').value = ''; openSheet('reject'); } async function submitReject() { if (!rejectIssueId) return; var reason = document.getElementById('rejectReason').value.trim(); if (!reason) { showToast('반려 사유를 입력해주세요.', 'warning'); return; } try { var resp = await fetch(API_BASE_URL + '/issues/' + rejectIssueId + '/reject-completion', { method: 'POST', headers: { 'Authorization': 'Bearer ' + TokenManager.getToken(), 'Content-Type': 'application/json' }, body: JSON.stringify({ rejection_reason: reason }) }); if (resp.ok) { showToast('반려 처리되었습니다.', 'success'); closeSheet('reject'); await loadIssues(); } else { var err = await resp.json(); throw new Error(err.detail || '반려 실패'); } } catch (e) { showToast('오류: ' + e.message, 'error'); } } // ===== 추가 정보 ===== function openAdditionalInfoSheet() { var inProgressIssues = issues.filter(function (i) { return i.review_status === 'in_progress'; }); var sel = document.getElementById('additionalIssueSelect'); sel.innerHTML = ''; inProgressIssues.forEach(function (i) { var p = projects.find(function (pr) { return pr.id === i.project_id; }); sel.innerHTML += ''; }); document.getElementById('additionalCauseDept').value = ''; document.getElementById('additionalCausePerson').value = ''; document.getElementById('additionalCauseDetail').value = ''; openSheet('additional'); } function loadAdditionalInfo() { var id = parseInt(document.getElementById('additionalIssueSelect').value); if (!id) return; var issue = issues.find(function (i) { return i.id === id; }); if (!issue) return; document.getElementById('additionalCauseDept').value = issue.cause_department || ''; document.getElementById('additionalCausePerson').value = issue.cause_person || ''; document.getElementById('additionalCauseDetail').value = issue.cause_detail || ''; } async function saveAdditionalInfo() { var id = parseInt(document.getElementById('additionalIssueSelect').value); if (!id) { showToast('이슈를 선택해주세요.', 'warning'); return; } try { var resp = await fetch(API_BASE_URL + '/issues/' + id + '/management', { method: 'PUT', headers: { 'Authorization': 'Bearer ' + TokenManager.getToken(), 'Content-Type': 'application/json' }, body: JSON.stringify({ cause_department: document.getElementById('additionalCauseDept').value || null, cause_person: document.getElementById('additionalCausePerson').value.trim() || null, cause_detail: document.getElementById('additionalCauseDetail').value.trim() || null }) }); if (resp.ok) { showToast('추가 정보가 저장되었습니다.', 'success'); closeSheet('additional'); await loadIssues(); } else { var err = await resp.json(); throw new Error(err.detail || '저장 실패'); } } catch (e) { showToast('오류: ' + e.message, 'error'); } } // ===== 완료됨 상세보기 ===== function openDetailSheet(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; }); var photos = getPhotoPaths(issue); var cPhotos = getCompletionPhotoPaths(issue); document.getElementById('detailSheetTitle').innerHTML = 'No.' + (issue.project_sequence_no || '-') + ' 상세 정보'; document.getElementById('detailSheetBody').innerHTML = // 기본 정보 '
' + '
기본 정보
' + '
프로젝트: ' + escapeHtml(project ? project.project_name : '-') + '
' + '
부적합명: ' + escapeHtml(getIssueTitle(issue)) + '
' + '
' + escapeHtml(getIssueDetail(issue)) + '
' + '
분류: ' + getCategoryText(issue.final_category || issue.category) + '
' + '
확인자: ' + escapeHtml(getReporterNames(issue)) + '
' + (photos.length ? '
업로드 사진
' + renderPhotoThumbs(photos) + '
' : '') + '
' + // 관리 정보 '
' + '
관리 정보
' + '
해결방안: ' + escapeHtml(cleanManagementComment(issue.management_comment) || '-') + '
' + '
담당부서: ' + getDepartmentText(issue.responsible_department) + '
' + '
담당자: ' + escapeHtml(issue.responsible_person || '-') + '
' + '
원인부서: ' + getDepartmentText(issue.cause_department) + '
' + '
' + // 완료 정보 '
' + '
완료 정보
' + (cPhotos.length ? '
완료 사진
' + renderPhotoThumbs(cPhotos) + '
' : '
완료 사진 없음
') + '
완료 코멘트: ' + escapeHtml(issue.completion_comment || '-') + '
' + (issue.completion_requested_at ? '
완료 신청일: ' + formatKSTDateTime(issue.completion_requested_at) + '
' : '') + (issue.completed_at ? '
최종 완료일: ' + formatKSTDateTime(issue.completed_at) + '
' : '') + '
'; openSheet('detail'); } // ===== 시작 ===== document.addEventListener('DOMContentLoaded', initialize);