From c16fc53f3b6e18042fe08c515d6ab414d7f8ced6 Mon Sep 17 00:00:00 2001 From: hyungi Date: Sun, 26 Oct 2025 15:28:23 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=20=EB=B0=8F=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=B3=80=EA=B2=BD=20=EA=B8=B0=EB=8A=A5=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 주요 변경사항: - 비활성화된 프로젝트 관리 기능 추가 * 프로젝트 관리 페이지에 접을 수 있는 비활성 프로젝트 섹션 추가 * 비활성화된 프로젝트 복구 기능 제공 * 업로드 시에는 활성 프로젝트만 표시되도록 API 호출 분리 - 헤더 비밀번호 변경 기능 완전 구현 * CommonHeader.js에 완전한 비밀번호 변경 모달 구현 * ESC 키 지원, 실시간 유효성 검사, 토스트 메시지 추가 * 중복 코드 제거 및 통일된 함수 호출 구조 - 수신함 수정 내용 표시 문제 해결 * description 우선 표시로 최신 수정 내용 반영 * 관리함에서 final_description/final_category 업데이트 로직 추가 - 현황판 날짜 그룹화 개선 * 업로드일 기준에서 관리함 진입일(reviewed_at) 기준으로 변경 * Invalid Date 오류 해결 - 프로젝트 관리 페이지 JavaScript 오류 수정 * 중복 변수 선언 및 함수 참조 오류 해결 * 페이지 초기화 로직 개선 기술적 개선: - API 호출 최적화 (active_only 매개변수 명시적 전달) - 프론트엔드 표시 우선순위 통일 (description || final_description) - 백엔드 final_* 필드 업데이트 로직 추가 --- backend/__pycache__/main.cpython-311.pyc | Bin 3601 -> 3601 bytes .../__pycache__/database.cpython-311.pyc | Bin 1153 -> 1153 bytes .../__pycache__/models.cpython-311.pyc | Bin 11842 -> 11842 bytes .../__pycache__/schemas.cpython-311.pyc | Bin 20724 -> 20724 bytes .../routers/__pycache__/auth.cpython-311.pyc | Bin 10982 -> 10982 bytes .../__pycache__/daily_work.cpython-311.pyc | Bin 8865 -> 8865 bytes .../routers/__pycache__/inbox.cpython-311.pyc | Bin 19547 -> 19547 bytes .../__pycache__/issues.cpython-311.pyc | Bin 17981 -> 17981 bytes .../__pycache__/management.cpython-311.pyc | Bin 8331 -> 8838 bytes .../page_permissions.cpython-311.pyc | Bin 14385 -> 14385 bytes .../__pycache__/projects.cpython-311.pyc | Bin 6578 -> 6578 bytes .../__pycache__/reports.cpython-311.pyc | Bin 6846 -> 6846 bytes backend/routers/management.py | 12 ++ .../__pycache__/auth_service.cpython-311.pyc | Bin 4811 -> 4811 bytes .../__pycache__/file_service.cpython-311.pyc | Bin 8203 -> 8203 bytes frontend/app.html | 40 +---- frontend/index.html | 4 +- frontend/issues-archive.html | 13 +- frontend/issues-dashboard.html | 25 +-- frontend/issues-inbox.html | 23 +-- frontend/issues-management.html | 6 +- frontend/project-management.html | 137 ++++++++++------ frontend/static/js/api.js | 2 +- frontend/static/js/app.js | 55 +------ .../static/js/components/common-header.js | 147 +++++++++++++++++- 25 files changed, 295 insertions(+), 169 deletions(-) diff --git a/backend/__pycache__/main.cpython-311.pyc b/backend/__pycache__/main.cpython-311.pyc index ba44a634813131001969816c8fa1aeda61ae8216..693e0969ff38e7b1c8d920354028df05b57abebe 100644 GIT binary patch delta 19 ZcmbOzGf{?XIWI340}$-ku#tb%pWdwKt delta 20 acmZqVY~Ub0~&uIWI340}$-ku#w9`4*)(s1)Bf> delta 19 ZcmX>Ub0~&uIWI340}wo#wUNt04*)*Q1+M@A diff --git a/backend/database/__pycache__/schemas.cpython-311.pyc b/backend/database/__pycache__/schemas.cpython-311.pyc index b07a5ff60b9db5c0550c611bd5fe8e2ff3129c77..1576070cc26749313e8c98a6b5a0e4ec3cc64cf0 100644 GIT binary patch delta 21 bcmeyeknzhxMy}<&yj%=Guw%nUu2%s7RN4n$ delta 21 bcmeyeknzhxMy}<&yj%=G5VdL}*Q)>kQiulo diff --git a/backend/routers/__pycache__/auth.cpython-311.pyc b/backend/routers/__pycache__/auth.cpython-311.pyc index dcd7677a64ec8e6202b7c6243302f255627967b3..a70b93af612fe7322594e921f2974f75d76698f2 100644 GIT binary patch delta 20 acmaDB`Ye=tIWI340}$-k@OLBkZ7l#zS_g9g delta 20 acmaDB`Ye=tIWI340}#}4{MpETTMGb3&jyeH diff --git a/backend/routers/__pycache__/daily_work.cpython-311.pyc b/backend/routers/__pycache__/daily_work.cpython-311.pyc index 2b3e23f6b6ae8713cac06757d83dbbd46f0cb12b..196390903a0b0b269b4270b2f2e3007e49766ffd 100644 GIT binary patch delta 20 acmZ4Jy3mz-IWI340}yD(oZ84eO$h)wcm-$x delta 20 acmZ4Jy3mz-IWI340}zxd}UN|O=Owhjc{>_tQkhfS|d+VZXoYFSsSpNQ9-Y?MP{S| znUOW^CdgqanK=41LIf#|`&3Nu}VZUpG?P3l&|`2G^xsLI?2sw0wOF z*~le#NT7M1>QO-S9La>;d4{|XkI0q|-Y8992*CkQD znp`t%%^X`3WotevO040_)HAv~ykyPA9lXm@v}^$soQrh=+PzY zT44*$sRYMUY5+6>ngGrCrlp8JPQtFpq-9vGKLXXG1Rjoj497010tzPp%>WMp%>(j8CN5OaD~GW`5^>R-F}3$ zaS9kS7~^UP7EOh5TB&QP3`?9*LLHTo9#2t1r8}WnRZ}Ya9J0k#%FaO*zMr%72j}J+ A=Kufz delta 626 zcmZp3?RMl_&dbZi00i^a{LRRh*vR*soymk{^IZ`O#>x7eHB7O;H?QaHWfVP8!+_eBvZp)Bvr${KxA?thlpH?#4<((hSfj}0mZB}>?smeKu#@tiT33AVv4MPYM2*@ zffP?}VC9>vFV4y+4(8405tf#$VabB)Eir_d!oZLvIr%(?7)OfK9OhsKP3g_Ad6<|c zH}cmoN=@D`Avak@pn38EfoewA$u@!-+(AHn%0OJ)&NMk+@CKV0tLg^^vCY0hT8xu_ z2-`4U;82--OG>w5g h1E;_TP7n*E5KMezW?+)O!YK8D2_#a&IC-Ap3jj^fjFbQX diff --git a/backend/routers/__pycache__/page_permissions.cpython-311.pyc b/backend/routers/__pycache__/page_permissions.cpython-311.pyc index 31ea5562018512c16622bd56d9d5af35d3b5bad0..179afc9ed243fdfa64c781e5c845bf4e65cf4227 100644 GIT binary patch delta 19 Zcmdm3u(5z^IWI340}$-ku#rpM0sua01)2Z= delta 19 Zcmdm3u(5z^IWI340}wnb+sLJE0RTQ+1&#m! diff --git a/backend/routers/__pycache__/projects.cpython-311.pyc b/backend/routers/__pycache__/projects.cpython-311.pyc index 94c478721305e0ed4bf9545691fcdb26e8381515..9cdddd0f6e4de906752e15c8472735f1e15e77e4 100644 GIT binary patch delta 19 ZcmdmFyvdkrIWI340}vSe+Q_v+5&$)s1x5e> delta 19 ZcmdmFyvdkrIWI340}vGd*~qm*5&$ECt#C diff --git a/backend/routers/management.py b/backend/routers/management.py index 0ee0ac8..021a508 100644 --- a/backend/routers/management.py +++ b/backend/routers/management.py @@ -76,6 +76,18 @@ async def update_issue( import traceback traceback.print_exc() continue + elif field == 'final_description' and value: + # final_description 업데이트 시 description도 함께 업데이트 + issue.final_description = value + issue.description = value + print(f"✅ final_description 및 description 업데이트: {value[:50]}...") + continue + elif field == 'final_category' and value: + # final_category 업데이트 시 category도 함께 업데이트 + issue.final_category = value + issue.category = value + print(f"✅ final_category 및 category 업데이트: {value}") + continue elif field == 'expected_completion_date' and value: # 날짜 필드 처리 if not value.endswith('T00:00:00'): diff --git a/backend/services/__pycache__/auth_service.cpython-311.pyc b/backend/services/__pycache__/auth_service.cpython-311.pyc index 0e0ad90153a53948f8442dffdfdb7e1cf9db1525..fa1fa5587e72c8f2d3dd9e6c83971ee4a1b0aea2 100644 GIT binary patch delta 19 ZcmX@DdRmohIWI340}vSe+Q@ZK2mm*11#SQU delta 19 ZcmX@DdRmohIWI340}vGd*~oQJ2mm<<1+xGE diff --git a/backend/services/__pycache__/file_service.cpython-311.pyc b/backend/services/__pycache__/file_service.cpython-311.pyc index 204bcbc74db17e37ba88698cfa1634854b249199..73dfb695f9aff8a128918196d99dc615b6699eac 100644 GIT binary patch delta 19 ZcmeBn=yu>*&dbZi00cWWY~*58001&h1h4=A delta 19 ZcmeBn=yu>*&dbZi00gVnZRBE9001%Y1fT!_ diff --git a/frontend/app.html b/frontend/app.html index 31541bc..3c82d63 100644 --- a/frontend/app.html +++ b/frontend/app.html @@ -183,7 +183,7 @@
- @@ -296,44 +296,6 @@
- - diff --git a/frontend/index.html b/frontend/index.html index cdd5e8a..8f4140c 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1192,8 +1192,8 @@ console.log('=== 프로젝트 로드 시작 (API) ==='); try { - // API에서 프로젝트 로드 (인증 없이) - const apiProjects = await ProjectsAPI.getAll(false); + // API에서 활성 프로젝트만 로드 (업로드용) + const apiProjects = await ProjectsAPI.getAll(true); // API 데이터를 UI 형식으로 변환 const projects = apiProjects.map(p => ({ diff --git a/frontend/issues-archive.html b/frontend/issues-archive.html index e070e84..a7a2b3b 100644 --- a/frontend/issues-archive.html +++ b/frontend/issues-archive.html @@ -322,9 +322,9 @@ if (response.ok) { const allIssues = await response.json(); - // 완료, 보관, 취소된 부적합만 필터링 + // 폐기된 부적합만 필터링 (폐기함 전용) issues = allIssues.filter(issue => - ['completed', 'archived', 'cancelled'].includes(issue.status) + issue.review_status === 'disposed' ); filterIssues(); @@ -422,8 +422,11 @@ container.innerHTML = filteredIssues.map(issue => { const project = projects.find(p => p.id === issue.project_id); - const completedDate = new Date(issue.updated_at || issue.created_at).toLocaleDateString('ko-KR'); - const cardClass = issue.status === 'completed' ? 'completed-card' : 'archived-card'; + + // 폐기함은 폐기된 것만 표시 + const completedDate = issue.disposed_at ? new Date(issue.disposed_at).toLocaleDateString('ko-KR') : 'Invalid Date'; + const statusText = '폐기'; + const cardClass = 'archived-card'; return `
${issue.reporter?.username || '알 수 없음'} ${issue.category ? `${getCategoryText(issue.category)}` : ''} - 완료: ${completedDate} + ${statusText}: ${completedDate}
diff --git a/frontend/issues-dashboard.html b/frontend/issues-dashboard.html index 0483cbd..db4c719 100644 --- a/frontend/issues-dashboard.html +++ b/frontend/issues-dashboard.html @@ -423,22 +423,29 @@ emptyState.classList.add('hidden'); - // 날짜별로 그룹화 + // 날짜별로 그룹화 (관리함 진입일 기준) const groupedByDate = {}; + const dateObjects = {}; // 정렬용 Date 객체 저장 + filteredIssues.forEach(issue => { - const dateKey = new Date(issue.report_date).toLocaleDateString('ko-KR'); + // reviewed_at이 있으면 관리함 진입일, 없으면 report_date 사용 + const dateToUse = issue.reviewed_at || issue.report_date; + const dateObj = new Date(dateToUse); + const dateKey = dateObj.toLocaleDateString('ko-KR'); + if (!groupedByDate[dateKey]) { groupedByDate[dateKey] = []; + dateObjects[dateKey] = dateObj; } groupedByDate[dateKey].push(issue); }); // 날짜별 그룹 생성 const dateGroups = Object.keys(groupedByDate) - .sort((a, b) => new Date(b) - new Date(a)) // 최신순 + .sort((a, b) => dateObjects[b] - dateObjects[a]) // 최신순 .map(dateKey => { const issues = groupedByDate[dateKey]; - const formattedDate = new Date(dateKey).toLocaleDateString('ko-KR', { + const formattedDate = dateObjects[dateKey].toLocaleDateString('ko-KR', { year: 'numeric', month: '2-digit', day: '2-digit' @@ -451,7 +458,7 @@ ${formattedDate} (${issues.length}건) - 업로드일 + 관리함 진입일
@@ -468,14 +475,14 @@ // 부적합명 추출 (첫 번째 줄) function getIssueTitle(issue) { - const description = issue.final_description || issue.description || ''; + const description = issue.description || issue.final_description || ''; const lines = description.split('\n'); return lines[0] || '부적합명 없음'; } // 상세 내용 추출 (두 번째 줄부터) function getIssueDetail(issue) { - const description = issue.final_description || issue.description || ''; + const description = issue.description || issue.final_description || ''; const lines = description.split('\n'); return lines.slice(1).join('\n') || '상세 내용 없음'; } @@ -605,7 +612,7 @@
- ${getCategoryText(issue.final_category || issue.category)} + ${getCategoryText(issue.category || issue.final_category)}
@@ -838,7 +845,7 @@ if (selectedProject) { filterByProject(); } else { - loadDashboardData(); + initializeDashboard(); } } diff --git a/frontend/issues-inbox.html b/frontend/issues-inbox.html index 87fb7e2..dbc69d3 100644 --- a/frontend/issues-inbox.html +++ b/frontend/issues-inbox.html @@ -812,7 +812,7 @@ -

${issue.description}

+

${issue.final_description || issue.description}

@@ -822,7 +822,7 @@
- ${getCategoryText(issue.category)} + ${getCategoryText(issue.category || issue.final_category)}
@@ -1075,10 +1075,10 @@
- ${issue.description} + ${issue.description || issue.final_description}
- ${getCategoryText(issue.category)} + ${getCategoryText(issue.category || issue.final_category)} 신고자: ${issue.reporter_name} ${issue.duplicate_count > 0 ? `중복 ${issue.duplicate_count}건` : ''}
@@ -1176,8 +1176,8 @@ originalInfo.innerHTML = `
프로젝트: ${project ? project.project_name : '미지정'}
-
카테고리: ${getCategoryText(issue.category)}
-
설명: ${issue.description}
+
카테고리: ${getCategoryText(issue.category || issue.final_category)}
+
설명: ${issue.description || issue.final_description}
등록자: ${issue.reporter?.username || '알 수 없음'}
등록일: ${new Date(issue.report_date).toLocaleDateString('ko-KR')}
@@ -1196,12 +1196,13 @@ reviewProjectSelect.appendChild(option); }); - // 현재 값들로 폼 초기화 - document.getElementById('reviewCategory').value = issue.category; - // 기존 description을 title과 description으로 분리 (첫 번째 줄을 title로 사용) - const lines = issue.description.split('\n'); + // 현재 값들로 폼 초기화 (최신 내용 우선 사용) + document.getElementById('reviewCategory').value = issue.category || issue.final_category; + // 최신 description을 title과 description으로 분리 (첫 번째 줄을 title로 사용) + const currentDescription = issue.description || issue.final_description; + const lines = currentDescription.split('\n'); document.getElementById('reviewTitle').value = lines[0] || ''; - document.getElementById('reviewDescription').value = lines.slice(1).join('\n') || issue.description; + document.getElementById('reviewDescription').value = lines.slice(1).join('\n') || currentDescription; document.getElementById('reviewModal').classList.remove('hidden'); } diff --git a/frontend/issues-management.html b/frontend/issues-management.html index bcda536..1a44b4c 100644 --- a/frontend/issues-management.html +++ b/frontend/issues-management.html @@ -693,14 +693,14 @@ // 부적합명 추출 (첫 번째 줄) function getIssueTitle(issue) { - const description = issue.final_description || issue.description || ''; + const description = issue.description || issue.final_description || ''; const lines = description.split('\n'); return lines[0] || '부적합명 없음'; } // 상세 내용 추출 (두 번째 줄부터) function getIssueDetail(issue) { - const description = issue.final_description || issue.description || ''; + const description = issue.description || issue.final_description || ''; const lines = description.split('\n'); return lines.slice(1).join('\n') || '상세 내용 없음'; } @@ -833,7 +833,7 @@
- ${getCategoryText(issue.final_category || issue.category)} + ${getCategoryText(issue.category || issue.final_category)}
diff --git a/frontend/project-management.html b/frontend/project-management.html index 7820d55..e7c23b2 100644 --- a/frontend/project-management.html +++ b/frontend/project-management.html @@ -142,6 +142,24 @@
+ + +
+
+
+ +

비활성화된 프로젝트

+ 0 +
+
+ 클릭하여 펼치기/접기 +
+
+ +
+ +
+
@@ -170,40 +188,50 @@ console.log('🚀 캐시 버스터:', cacheBuster); - +