From 63bdf4e6897bfb08d8c07b25e7d7306e95afe16d Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Sun, 26 Oct 2025 13:06:13 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=ED=95=A8=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=20=EC=A4=91=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EB=8C=80=EA=B8=B0=20=EC=83=81=ED=83=9C=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🎯 μƒνƒœλ³„ UI κ°œμ„ : - μ§„ν–‰ 쀑: νŒŒλž€μƒ‰ (일반 μƒνƒœ) - κΈ΄κΈ‰: 주황색 (마감 3일 이내) - 지연됨: 빨간색 (마감일 초과) - μ™„λ£Œ λŒ€κΈ°: 보라색 (μ™„λ£Œ μ‹ μ²­ ν›„) πŸ”’ μ™„λ£Œ λŒ€κΈ° μƒνƒœ μ œν•œ: - λͺ¨λ“  μž…λ ₯ ν•„λ“œ λΉ„ν™œμ„±ν™” (readonly/disabled) - 상세 λ‚΄μš© μˆ˜μ • λ²„νŠΌ β†’ 'μ™„λ£Œ λŒ€κΈ° 쀑' ν‘œμ‹œ - μ €μž₯ λ²„νŠΌ 제거 - νšŒμƒ‰ 배경으둜 λΉ„ν™œμ„±ν™” ν‘œμ‹œ πŸ“‹ μ™„λ£Œ λŒ€κΈ° μƒνƒœ 정보 ν‘œμ‹œ: - μ™„λ£Œ μ‹ μ²­ 정보 μ„Ήμ…˜ μΆ”κ°€ - μ™„λ£Œ 사진, μ½”λ©˜νŠΈ, μ‹ μ²­μΌμ‹œ ν‘œμ‹œ - 보라색 ν…Œλ§ˆλ‘œ ꡬ뢄 πŸ”§ 3단계 λ²„νŠΌ μ‹œμŠ€ν…œ: 1. μˆ˜μ •: μ™„λ£Œ λŒ€κΈ° μƒνƒœ ν•΄μ œ β†’ μˆ˜μ • λͺ¨λ“œ μ „ν™˜ 2. 반렀: μ™„λ£Œ μ‹ μ²­ 반렀 + μ‚¬μœ  μž…λ ₯ 3. 확인: λͺ¨λ“  정보 확인 λͺ¨λ‹¬ β†’ μ΅œμ’… μ™„λ£Œ 처리 πŸ“Š μ™„λ£Œ 확인 λͺ¨λ‹¬: - κΈ°λ³Έ 정보 (ν”„λ‘œμ νŠΈ, 뢀적합λͺ…, μƒμ„Έλ‚΄μš©, 원인뢄λ₯˜) - 관리 정보 (ν•΄κ²°λ°©μ•ˆ, λ‹΄λ‹ΉλΆ€μ„œ/자, μ‘°μΉ˜μ˜ˆμƒμΌ) - μ™„λ£Œ μ‹ μ²­ 정보 (μ™„λ£Œ 사진, μ½”λ©˜νŠΈ, μ‹ μ²­μΌμ‹œ) - μ—…λ‘œλ“œ 사진 (원본 사진 1, 2) - μ΅œμ’… 확인 λ²„νŠΌ πŸ”„ API μ—”λ“œν¬μΈνŠΈ (κ΅¬ν˜„ μ˜ˆμ •): - POST /api/issues/{id}/reset-completion (μˆ˜μ • λͺ¨λ“œ μ „ν™˜) - POST /api/issues/{id}/reject-completion (반렀 처리) - POST /api/issues/{id}/final-completion (μ΅œμ’… μ™„λ£Œ) πŸ’‘ μ‚¬μš©μž κ²½ν—˜: - μƒνƒœλ³„ 색상 μ½”λ”©μœΌλ‘œ 직관적 ꡬ뢄 - μ™„λ£Œ λŒ€κΈ° μ‹œ μˆ˜μ • λΆˆκ°€ λͺ…ν™• ν‘œμ‹œ - λͺ¨λ“  정보 ν•œλˆˆμ— 확인 κ°€λŠ₯ν•œ λͺ¨λ‹¬ - 단계별 승인 ν”„λ‘œμ„ΈμŠ€ Expected Result: βœ… μ™„λ£Œ λŒ€κΈ° μƒνƒœ μ‹œκ°μ  ꡬ뢄 βœ… μˆ˜μ • κΈ°λŠ₯ μ μ ˆν•œ μ œν•œ βœ… 체계적인 μ™„λ£Œ 승인 ν”„λ‘œμ„ΈμŠ€ βœ… κ΄€λ¦¬μž μΉœν™”μ  μΈν„°νŽ˜μ΄μŠ€ --- frontend/issues-management.html | 335 ++++++++++++++++++++++++++++++-- 1 file changed, 318 insertions(+), 17 deletions(-) diff --git a/frontend/issues-management.html b/frontend/issues-management.html index b76104b..73452de 100644 --- a/frontend/issues-management.html +++ b/frontend/issues-management.html @@ -708,6 +708,44 @@ // μ§„ν–‰ 쀑 μΉ΄λ“œ 생성 function createInProgressRow(issue, project) { + // μƒνƒœ νŒλ³„ + const isPendingCompletion = issue.completion_requested_at; + const isOverdue = issue.expected_completion_date && new Date(issue.expected_completion_date) < new Date(); + const isUrgent = issue.expected_completion_date && + (new Date(issue.expected_completion_date) - new Date()) / (1000 * 60 * 60 * 24) <= 3 && + !isOverdue; + + // μƒνƒœ μ„€μ • + let statusConfig = { + text: 'μ§„ν–‰ 쀑', + bgColor: 'bg-gradient-to-r from-blue-500 to-blue-600', + icon: 'fas fa-cog fa-spin', + dotColor: 'bg-white' + }; + + if (isPendingCompletion) { + statusConfig = { + text: 'μ™„λ£Œ λŒ€κΈ°', + bgColor: 'bg-gradient-to-r from-purple-500 to-purple-600', + icon: 'fas fa-hourglass-half', + dotColor: 'bg-white' + }; + } else if (isOverdue) { + statusConfig = { + text: '지연됨', + bgColor: 'bg-gradient-to-r from-red-500 to-red-600', + icon: 'fas fa-clock', + dotColor: 'bg-white' + }; + } else if (isUrgent) { + statusConfig = { + text: 'κΈ΄κΈ‰', + bgColor: 'bg-gradient-to-r from-orange-500 to-orange-600', + icon: 'fas fa-exclamation-triangle', + dotColor: 'bg-white' + }; + } + return `
@@ -719,18 +757,38 @@
${project ? project.project_name : 'ν”„λ‘œμ νŠΈ λ―Έμ§€μ •'} + +
+
+ ${statusConfig.text} + +

${getIssueTitle(issue)}

- - + ${isPendingCompletion ? ` + + + + + ` : ` + + + + `}
@@ -744,9 +802,15 @@ - + ${!isPendingCompletion ? ` + + ` : ` + + μ™„λ£Œ λŒ€κΈ° 쀑 + + `}
@@ -793,7 +857,7 @@ - +
@@ -801,7 +865,7 @@ - ${getDepartmentOptions().map(opt => `` ).join('')} @@ -812,7 +876,7 @@ - +
@@ -820,16 +884,43 @@ - + + + ${isPendingCompletion ? ` +
+

+ μ™„λ£Œ μ‹ μ²­ 정보 +

+
+
+ + ${issue.completion_photo_path ? ` +
+ μ™„λ£Œ 사진 +
+ ` : '

μ™„λ£Œ 사진 μ—†μŒ

'} +
+
+ +

${issue.completion_comment || 'μ½”λ©˜νŠΈ μ—†μŒ'}

+
+
+ +

${new Date(issue.completion_requested_at).toLocaleString('ko-KR')}

+
+
+
+ ` : ''} + -
+
- - μ§„ν–‰ 쀑 + + ${statusConfig.text} - + 신고일: ${new Date(issue.report_date).toLocaleDateString('ko-KR')}
@@ -1638,6 +1729,216 @@ alert('μ €μž₯ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); } } + + // μ™„λ£Œ λŒ€κΈ° μƒνƒœ κ΄€λ ¨ ν•¨μˆ˜λ“€ + function editIssue(issueId) { + // μˆ˜μ • λͺ¨λ“œλ‘œ μ „ν™˜ (μ™„λ£Œ λŒ€κΈ° μƒνƒœλ₯Ό ν•΄μ œ) + if (confirm('μ™„λ£Œ λŒ€κΈ° μƒνƒœλ₯Ό ν•΄μ œν•˜κ³  μˆ˜μ • λͺ¨λ“œλ‘œ μ „ν™˜ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?')) { + // μ™„λ£Œ μ‹ μ²­ 정보 μ΄ˆκΈ°ν™” API 호좜 + resetCompletionRequest(issueId); + } + } + + function rejectCompletion(issueId) { + const reason = prompt('반렀 μ‚¬μœ λ₯Ό μž…λ ₯ν•˜μ„Έμš”:'); + if (reason && reason.trim()) { + // 반렀 처리 API 호좜 + rejectCompletionRequest(issueId, reason.trim()); + } + } + + function confirmCompletion(issueId) { + // λͺ¨λ“  정보 확인 λͺ¨λ‹¬ μ—΄κΈ° + openCompletionConfirmModal(issueId); + } + + // μ™„λ£Œ μ‹ μ²­ μ΄ˆκΈ°ν™” (μˆ˜μ • λͺ¨λ“œλ‘œ μ „ν™˜) + async function resetCompletionRequest(issueId) { + try { + const response = await fetch(`/api/issues/${issueId}/reset-completion`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${localStorage.getItem('access_token')}`, + 'Content-Type': 'application/json' + } + }); + + if (response.ok) { + alert('μ™„λ£Œ λŒ€κΈ° μƒνƒœκ°€ ν•΄μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. μˆ˜μ •μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.'); + loadManagementData(); // νŽ˜μ΄μ§€ μƒˆλ‘œκ³ μΉ¨ + } else { + const error = await response.json(); + alert(`μƒνƒœ λ³€κ²½ μ‹€νŒ¨: ${error.detail || 'μ•Œ 수 μ—†λŠ” 였λ₯˜'}`); + } + } catch (error) { + console.error('μƒνƒœ λ³€κ²½ 였λ₯˜:', error); + alert('μƒνƒœ λ³€κ²½ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); + } + } + + // μ™„λ£Œ μ‹ μ²­ 반렀 + async function rejectCompletionRequest(issueId, reason) { + try { + const response = await fetch(`/api/issues/${issueId}/reject-completion`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${localStorage.getItem('access_token')}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + rejection_reason: reason + }) + }); + + if (response.ok) { + alert('μ™„λ£Œ 신청이 λ°˜λ €λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); + loadManagementData(); // νŽ˜μ΄μ§€ μƒˆλ‘œκ³ μΉ¨ + } else { + const error = await response.json(); + alert(`반렀 처리 μ‹€νŒ¨: ${error.detail || 'μ•Œ 수 μ—†λŠ” 였λ₯˜'}`); + } + } catch (error) { + console.error('반렀 처리 였λ₯˜:', error); + alert('반렀 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); + } + } + + // μ™„λ£Œ 확인 λͺ¨λ‹¬ μ—΄κΈ° + function openCompletionConfirmModal(issueId) { + const issue = issues.find(i => i.id === issueId); + if (!issue) return; + + const project = projects.find(p => p.id === issue.project_id); + + // λͺ¨λ‹¬ λ‚΄μš© 생성 + const modalContent = ` +
+
+
+ +
+

+ + μ™„λ£Œ 확인 - No.${issue.project_sequence_no || '-'} +

+ +
+ + +
+ +
+
+

κΈ°λ³Έ 정보

+
+
ν”„λ‘œμ νŠΈ: ${project ? project.project_name : '-'}
+
뢀적합λͺ…: ${getIssueTitle(issue)}
+
μƒμ„Έλ‚΄μš©: ${getIssueDetail(issue)}
+
원인뢄λ₯˜: ${getCategoryText(issue.final_category || issue.category)}
+
+
+ +
+

관리 정보

+
+
ν•΄κ²°λ°©μ•ˆ: ${issue.solution || '-'}
+
λ‹΄λ‹ΉλΆ€μ„œ: ${issue.responsible_department || '-'}
+
λ‹΄λ‹Ήμž: ${issue.responsible_person || '-'}
+
μ‘°μΉ˜μ˜ˆμƒμΌ: ${issue.expected_completion_date ? new Date(issue.expected_completion_date).toLocaleDateString('ko-KR') : '-'}
+
+
+
+ + +
+
+

μ™„λ£Œ μ‹ μ²­ 정보

+
+
+ μ™„λ£Œ 사진: + ${issue.completion_photo_path ? ` +
+ μ™„λ£Œ 사진 +
+ ` : '

μ™„λ£Œ 사진 μ—†μŒ

'} +
+
+ μ™„λ£Œ μ½”λ©˜νŠΈ: +

${issue.completion_comment || 'μ½”λ©˜νŠΈ μ—†μŒ'}

+
+
+ μ‹ μ²­μΌμ‹œ: +

${new Date(issue.completion_requested_at).toLocaleString('ko-KR')}

+
+
+
+ +
+

μ—…λ‘œλ“œ 사진

+
+ ${issue.photo_path ? `μ—…λ‘œλ“œ 사진 1` : '
사진 μ—†μŒ
'} + ${issue.photo_path2 ? `μ—…λ‘œλ“œ 사진 2` : '
사진 μ—†μŒ
'} +
+
+
+
+ + +
+ + +
+
+
+
+ `; + + // λͺ¨λ‹¬μ„ body에 μΆ”κ°€ + document.body.insertAdjacentHTML('beforeend', modalContent); + } + + // μ™„λ£Œ 확인 λͺ¨λ‹¬ λ‹«κΈ° + function closeCompletionConfirmModal() { + const modal = document.getElementById('completionConfirmModal'); + if (modal) { + modal.remove(); + } + } + + // μ΅œμ’… μ™„λ£Œ 확인 + async function finalConfirmCompletion(issueId) { + if (!confirm('이 뢀적합을 μ΅œμ’… μ™„λ£Œ μ²˜λ¦¬ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?\nμ™„λ£Œ 처리 ν›„μ—λŠ” μˆ˜μ •ν•  수 μ—†μŠ΅λ‹ˆλ‹€.')) { + return; + } + + try { + const response = await fetch(`/api/issues/${issueId}/final-completion`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${localStorage.getItem('access_token')}`, + 'Content-Type': 'application/json' + } + }); + + if (response.ok) { + alert('뢀적합이 μ΅œμ’… μ™„λ£Œ μ²˜λ¦¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); + closeCompletionConfirmModal(); + loadManagementData(); // νŽ˜μ΄μ§€ μƒˆλ‘œκ³ μΉ¨ + } else { + const error = await response.json(); + alert(`μ™„λ£Œ 처리 μ‹€νŒ¨: ${error.detail || 'μ•Œ 수 μ—†λŠ” 였λ₯˜'}`); + } + } catch (error) { + console.error('μ™„λ£Œ 처리 였λ₯˜:', error); + alert('μ™„λ£Œ 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); + } + }