From 95be1f6c6e3db453b3449ede4bde81c4e8f6fe75 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Sat, 25 Oct 2025 15:48:53 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=ED=95=A8=20=EC=99=84?= =?UTF-8?q?=EC=A0=84=20=EA=B0=9C=ED=8E=B8=20-=20=ED=8E=B8=EC=A7=91=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=9C=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EB=B0=8F=20=EC=99=84=EB=A3=8C=20=EC=B2=98=EB=A6=AC=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🎯 Major Management Page Overhaul: - ν…Œμ΄λΈ” μ΅œμ†Œ λ„ˆλΉ„ 2000px둜 ν™•μž₯ (쒌우 슀크둀 μ΅œμ ν™”) - μ»¬λŸΌλ³„ κ°œλ³„ λ„ˆλΉ„ μ„€μ •μœΌλ‘œ λ‚΄μš©μ— λ§žλŠ” 크기 μ‘°μ • - νŽΈμ§‘ κ°€λŠ₯ν•œ ν•„λ“œλ“€ (ν•΄κ²°λ°©μ•ˆ, λ‹΄λ‹ΉλΆ€μ„œ, λ‹΄λ‹Ήμž, μ‘°μΉ˜μ˜ˆμƒμΌ, μ›μΈλΆ€μ„œ, 의견) - μ§„ν–‰ 쀑 β†’ μ™„λ£Œλ¨ 처리 λ²„νŠΌ μΆ”κ°€ πŸ“Š Enhanced Table Structure: - μ—…λ‘œλ“œ 사진 2μž₯ ν‘œμ‹œ (photo_path, photo_path2) - μ™„λ£Œ 사진 별도 컬럼으둜 ν‘œμ‹œ - μž‘μ—… 컬럼 μΆ”κ°€ (μ €μž₯ λ²„νŠΌ) - μ™„λ£Œ 확인 컬럼 (μ§„ν–‰ 쀑: μ™„λ£Œ 처리 λ²„νŠΌ, μ™„λ£Œλ¨: μ™„λ£ŒμΌ) ✏️ Editable Fields Implementation: - createEditableField() ν•¨μˆ˜λ‘œ 동적 μž…λ ₯ ν•„λ“œ 생성 - textarea, select, date, text νƒ€μž… 지원 - λΆ€μ„œ 선택 λ“œλ‘­λ‹€μš΄ (생산, ν’ˆμ§ˆ, ꡬ맀, 섀계, μ˜μ—…) - μ‹€μ‹œκ°„ νŽΈμ§‘ 및 μ €μž₯ κΈ°λŠ₯ πŸ”§ Backend API Enhancement: - PUT /api/issues/{issue_id}/management μ—”λ“œν¬μΈνŠΈ μΆ”κ°€ - ManagementUpdateRequest μŠ€ν‚€λ§ˆ ν™œμš© - 관리함 νŽ˜μ΄μ§€ κΆŒν•œ 검증 - μ™„λ£Œ 사진 μ—…λ‘œλ“œ 지원 πŸ“ˆ Smart Sequencing System: - μˆ˜μ‹ ν•¨μ—μ„œ λ„˜μ–΄μ˜¨ μˆœμ„œλŒ€λ‘œ No. ν• λ‹Ή (reviewed_at κΈ°μ€€) - ν”„λ‘œμ νŠΈλ³„ κ·Έλ£Ήν™” ν›„ 순번 μž¬ν• λ‹Ή - μ§„ν–‰ 쀑 A β†’ μ§„ν–‰ 쀑 B β†’ μ™„λ£Œλ¨ C β†’ μ§„ν–‰ 쀑 D = 1, 2, 3, 4 🎨 UI/UX Improvements: - μ»¬λŸΌλ³„ CSS 클래슀둜 μΌκ΄€λœ μŠ€νƒ€μΌλ§ - νŽΈμ§‘ κ°€λŠ₯ν•œ ν•„λ“œ 포컀슀 효과 - 사진 μ»¨ν…Œμ΄λ„ˆλ‘œ 2μž₯ 사진 κΉ”λ”ν•œ 배치 - λ²„νŠΌ 크기 μ΅œμ ν™” (btn-sm 클래슀) πŸš€ Functional Features: - completeIssue(): μ§„ν–‰ 쀑 β†’ μ™„λ£Œλ¨ 처리 - saveIssueChanges(): νŽΈμ§‘λœ ν•„λ“œλ“€ 일괄 μ €μž₯ - μ‹€μ‹œκ°„ λͺ©λ‘ μƒˆλ‘œκ³ μΉ¨ - 확인 λ‹€μ΄μ–Όλ‘œκ·Έλ‘œ μ•ˆμ „ν•œ μž‘μ—… 처리 Expected Result: βœ… 쒌우 슀크둀둜 λͺ¨λ“  정보 νŽΈλ¦¬ν•˜κ²Œ 확인 βœ… κ΄€λ¦¬ν•¨μ—μ„œ ν•„μš”ν•œ 정보 직접 μž…λ ₯/μˆ˜μ • βœ… μ§„ν–‰ μ€‘μ—μ„œ μ™„λ£Œ 처리 원클릭 βœ… μˆ˜μ‹ ν•¨ μˆœμ„œ 기반 체계적인 No. 관리 βœ… μ—…λ‘œλ“œ 사진 2μž₯ + μ™„λ£Œ 사진 λͺ…ν™•ν•œ ꡬ뢄 --- .../__pycache__/issues.cpython-311.pyc | Bin 10718 -> 12997 bytes backend/routers/issues.py | 43 +++ frontend/issues-management.html | 294 +++++++++++++++--- 3 files changed, 290 insertions(+), 47 deletions(-) diff --git a/backend/routers/__pycache__/issues.cpython-311.pyc b/backend/routers/__pycache__/issues.cpython-311.pyc index 3a3a98bad368701775bf33eab9b75fa1f185851a..aa947ac861ce7eda60e7f0b35818590ff64ba51c 100644 GIT binary patch delta 3594 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 + let filteredIssues = allIssues.filter(issue => issue.review_status === 'in_progress' || issue.review_status === 'completed' ); + + // μˆ˜μ‹ ν•¨μ—μ„œ λ„˜μ–΄μ˜¨ μˆœμ„œλŒ€λ‘œ No. μž¬ν• λ‹Ή (reviewed_at κΈ°μ€€) + filteredIssues.sort((a, b) => new Date(a.reviewed_at) - new Date(b.reviewed_at)); + + // ν”„λ‘œμ νŠΈλ³„λ‘œ κ·Έλ£Ήν™”ν•˜μ—¬ No. μž¬ν• λ‹Ή + const projectGroups = {}; + filteredIssues.forEach(issue => { + if (!projectGroups[issue.project_id]) { + projectGroups[issue.project_id] = []; + } + projectGroups[issue.project_id].push(issue); + }); + + // 각 ν”„λ‘œμ νŠΈλ³„λ‘œ 순번 μž¬ν• λ‹Ή + Object.keys(projectGroups).forEach(projectId => { + projectGroups[projectId].forEach((issue, index) => { + issue.project_sequence_no = index + 1; + }); + }); + + issues = filteredIssues; filterIssues(); } else { throw new Error('뢀적합 λͺ©λ‘μ„ 뢈러올 수 μ—†μŠ΅λ‹ˆλ‹€.'); @@ -506,25 +573,26 @@
- - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + ${issues.map(issue => createIssueRow(issue)).join('')} @@ -543,38 +611,98 @@ const project = projects.find(p => p.id === issue.project_id); const statusText = issue.review_status === 'in_progress' ? 'μ§„ν–‰ 쀑' : 'μ™„λ£Œλ¨'; const statusClass = issue.review_status === 'in_progress' ? 'text-blue-600' : 'text-green-600'; + const isInProgress = issue.review_status === 'in_progress'; + const isCompleted = issue.review_status === 'completed'; return ` - - - - + + + + + - - - - - - - - - - - - + + + + + + + `; } + + // νŽΈμ§‘ κ°€λŠ₯ν•œ ν•„λ“œ 생성 ν•¨μˆ˜ + function createEditableField(fieldName, value, type, issueId, editable, options = null) { + if (!editable) { + return value || '-'; + } + + const fieldId = `${fieldName}_${issueId}`; + + switch (type) { + case 'textarea': + return ``; + case 'select': + if (options) { + const optionsHtml = options.map(opt => + `` + ).join(''); + return ``; + } + break; + case 'date': + return ``; + case 'text': + default: + return ``; + } + + return value || '-'; + } + + // λΆ€μ„œ μ˜΅μ…˜ 생성 ν•¨μˆ˜ + function getDepartmentOptions() { + return [ + { value: '', text: 'μ„ νƒν•˜μ„Έμš”' }, + { value: 'production', text: '생산' }, + { value: 'quality', text: 'ν’ˆμ§ˆ' }, + { value: 'purchasing', text: 'ꡬ맀' }, + { value: 'design', text: '섀계' }, + { value: 'sales', text: 'μ˜μ—…' } + ]; + } // λ‚ μ§œ κ·Έλ£Ή ν† κΈ€ ν•¨μˆ˜ function toggleDateGroup(groupId) { @@ -688,6 +816,78 @@ } } + // μ™„λ£Œ 처리 ν•¨μˆ˜ + async function completeIssue(issueId) { + if (!confirm('이 뢀적합을 μ™„λ£Œ μ²˜λ¦¬ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?')) { + return; + } + + try { + const response = await fetch(`/api/inbox/${issueId}/status`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${localStorage.getItem('access_token')}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + review_status: 'completed' + }) + }); + + if (response.ok) { + alert('μ™„λ£Œ μ²˜λ¦¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); + await loadIssues(); // λͺ©λ‘ μƒˆλ‘œκ³ μΉ¨ + } else { + const error = await response.json(); + throw new Error(error.detail || 'μ™„λ£Œ μ²˜λ¦¬μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.'); + } + } catch (error) { + console.error('μ™„λ£Œ 처리 μ‹€νŒ¨:', error); + alert(error.message || 'μ™„λ£Œ 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); + } + } + + // 이슈 변경사항 μ €μž₯ ν•¨μˆ˜ + async function saveIssueChanges(issueId) { + try { + // νŽΈμ§‘λœ ν•„λ“œλ“€μ˜ κ°’ μˆ˜μ§‘ + const updates = {}; + const fields = ['solution', 'responsible_department', 'responsible_person', 'expected_completion_date', 'cause_department', 'management_comment']; + + fields.forEach(field => { + const element = document.getElementById(`${field}_${issueId}`); + if (element) { + let value = element.value.trim(); + if (value === '' || value === 'μ„ νƒν•˜μ„Έμš”') { + value = null; + } + updates[field] = value; + } + }); + + // API 호좜 + const response = await fetch(`/api/issues/${issueId}/management`, { + method: 'PUT', + headers: { + 'Authorization': `Bearer ${localStorage.getItem('access_token')}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(updates) + }); + + if (response.ok) { + alert('변경사항이 μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); + await loadIssues(); // λͺ©λ‘ μƒˆλ‘œκ³ μΉ¨ + } else { + const error = await response.json(); + throw new Error(error.detail || 'μ €μž₯에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.'); + } + } catch (error) { + console.error('μ €μž₯ μ‹€νŒ¨:', error); + alert(error.message || 'μ €μž₯ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); + } + } + // 기타 ν•¨μˆ˜λ“€ function viewIssueDetail(issueId) {
No.ν”„λ‘œμ νŠΈλ‚΄μš©μ›μΈν•΄κ²°λ°©μ•ˆλ‹΄λ‹ΉλΆ€μ„œλ‹΄λ‹Ήμžμ‘°μΉ˜μ˜ˆμƒμΌμ™„λ£Œν™•μΈμΌν™•μΈμžμ›μΈλΆ€μ„œμ˜κ²¬μ‘°μΉ˜κ²°κ³Όμ—…λ‘œλ“œ μ‚¬μ§„μ™„λ£Œ 사진
No.ν”„λ‘œμ νŠΈλ‚΄μš©μ›μΈν•΄κ²°λ°©μ•ˆλ‹΄λ‹ΉλΆ€μ„œλ‹΄λ‹Ήμžμ‘°μΉ˜μ˜ˆμƒμΌ${currentTab === 'in_progress' ? 'μ™„λ£Œ 확인' : 'μ™„λ£Œν™•μΈμΌ'}ν™•μΈμžμ›μΈλΆ€μ„œμ˜κ²¬μ‘°μΉ˜κ²°κ³Όμ—…λ‘œλ“œ μ‚¬μ§„μ™„λ£Œ μ‚¬μ§„μž‘μ—…
${issue.project_sequence_no || '-'}${project ? project.project_name : '-'} - ${issue.final_description || issue.description} +
${issue.project_sequence_no || '-'}${project ? project.project_name : '-'}${issue.final_description || issue.description}${getCategoryText(issue.final_category || issue.category)} + ${createEditableField('solution', issue.solution || '', 'textarea', issue.id, true)} ${getCategoryText(issue.final_category || issue.category)} - ${issue.solution || '-'} + + ${createEditableField('responsible_department', issue.responsible_department || '', 'select', issue.id, true, getDepartmentOptions())} ${getDepartmentText(issue.responsible_department) || '-'}${issue.responsible_person || '-'}${issue.expected_completion_date ? new Date(issue.expected_completion_date).toLocaleDateString('ko-KR') : '-'}${issue.actual_completion_date ? new Date(issue.actual_completion_date).toLocaleDateString('ko-KR') : '-'}${getReporterNames(issue)}${getDepartmentText(issue.cause_department) || '-'} - ${issue.management_comment || '-'} + + ${createEditableField('responsible_person', issue.responsible_person || '', 'text', issue.id, true)} ${statusText} - ${issue.photo_path ? `μ—…λ‘œλ“œ 사진` : '-'} - ${issue.photo_path2 ? `
μ—…λ‘œλ“œ 사진 2` : ''} +
+ ${createEditableField('expected_completion_date', issue.expected_completion_date ? issue.expected_completion_date.split('T')[0] : '', 'date', issue.id, true)} + + ${isInProgress ? + `` : + (issue.actual_completion_date ? new Date(issue.actual_completion_date).toLocaleDateString('ko-KR') : '-') + } + ${getReporterNames(issue)} + ${createEditableField('cause_department', issue.cause_department || '', 'select', issue.id, true, getDepartmentOptions())} + + ${createEditableField('management_comment', issue.management_comment || '', 'textarea', issue.id, true)} + ${statusText} +
+ ${issue.photo_path ? `μ—…λ‘œλ“œ 사진 1` : ''} + ${issue.photo_path2 ? `μ—…λ‘œλ“œ 사진 2` : ''} + ${!issue.photo_path && !issue.photo_path2 ? '-' : ''} +
+
${issue.completion_photo_path ? `μ™„λ£Œ 사진` : '-'} + +