From 0e57cb99e884dc72eccc7c2f970de257cb8a1961 Mon Sep 17 00:00:00 2001 From: hyungi Date: Fri, 24 Oct 2025 10:27:45 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EB=B0=B1?= =?UTF-8?q?=EC=97=94=EB=93=9C=20API=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용자 목록 로드를 localStorage에서 AuthAPI.getUsers()로 변경 - 비밀번호 초기화를 localStorage 조작에서 AuthAPI.resetPassword()로 변경 - 사용자 삭제 기능 백엔드 API 연동 확인 - 사용자 추가/목록/삭제 모든 기능이 백엔드 DB와 동기화됨 - localStorage 하드코딩 문제 해결로 일관된 데이터 관리 구현 Fixes: - 사용자 추가 후 목록에 표시되지 않던 문제 - 비밀번호 초기화가 실제 DB에 반영되지 않던 문제 - 백엔드 API와 localStorage 간 데이터 불일치 문제 --- frontend/admin.html | 49 ++++++++++++-- frontend/index.html | 134 +++++++++++++++++++++++++++++++++++++- frontend/issue-view.html | 134 +++++++++++++++++++++++++++++++++++++- frontend/static/js/api.js | 7 ++ 4 files changed, 315 insertions(+), 9 deletions(-) diff --git a/frontend/admin.html b/frontend/admin.html index 7c9e994..1e5878d 100644 --- a/frontend/admin.html +++ b/frontend/admin.html @@ -305,10 +305,14 @@ // 사용자 목록 로드 async function loadUsers() { try { + // 백엔드 API에서 사용자 목록 로드 users = await AuthAPI.getUsers(); displayUsers(); } catch (error) { console.error('사용자 목록 로드 실패:', error); + // API 실패 시 빈 배열로 초기화 + users = []; + displayUsers(); } } @@ -339,18 +343,53 @@ - ${user.username !== 'hyungi' ? ` +
- ` : ''} + ${user.username !== 'hyungi' ? ` + + ` : ''} +
`).join(''); } + // 비밀번호 초기화 + async function resetPassword(username) { + if (!confirm(`${username} 사용자의 비밀번호를 "000000"으로 초기화하시겠습니까?`)) { + return; + } + + try { + // 사용자 ID 찾기 + const user = users.find(u => u.username === username); + if (!user) { + alert('사용자를 찾을 수 없습니다.'); + return; + } + + // 백엔드 API로 비밀번호 초기화 + await AuthAPI.resetPassword(user.id, '000000'); + + alert(`${username} 사용자의 비밀번호가 "000000"으로 초기화되었습니다.`); + + // 목록 새로고침 + await loadUsers(); + + } catch (error) { + alert('비밀번호 초기화에 실패했습니다: ' + error.message); + } + } + // 사용자 삭제 async function deleteUser(username) { if (!confirm(`정말 ${username} 사용자를 삭제하시겠습니까?`)) { diff --git a/frontend/index.html b/frontend/index.html index 713f16a..953b1a8 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -214,9 +214,9 @@ - @@ -500,6 +500,17 @@ } } + // 관리 버튼 클릭 처리 + function handleAdminClick() { + if (currentUser.role === 'admin') { + // 관리자: 사용자 관리 페이지로 이동 + window.location.href = 'admin.html'; + } else { + // 일반 사용자: 비밀번호 변경 모달 표시 + showPasswordChangeModal(); + } + } + // 섹션 전환 // URL 해시 처리 function handleUrlHash() { @@ -1414,6 +1425,125 @@ document.body.appendChild(modal); } + // 비밀번호 변경 모달 표시 + function showPasswordChangeModal() { + const modal = document.createElement('div'); + modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50'; + modal.onclick = (e) => { + if (e.target === modal) modal.remove(); + }; + + modal.innerHTML = ` +
+
+

비밀번호 변경

+ +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ `; + + document.body.appendChild(modal); + + // 폼 제출 이벤트 처리 + document.getElementById('passwordChangeForm').addEventListener('submit', handlePasswordChange); + } + + // 비밀번호 변경 처리 + async function handlePasswordChange(e) { + e.preventDefault(); + + const currentPassword = document.getElementById('currentPassword').value; + const newPassword = document.getElementById('newPassword').value; + const confirmPassword = document.getElementById('confirmPassword').value; + + // 새 비밀번호 확인 + if (newPassword !== confirmPassword) { + alert('새 비밀번호가 일치하지 않습니다.'); + return; + } + + // 현재 비밀번호 확인 (localStorage 기반) + let users = JSON.parse(localStorage.getItem('work-report-users') || '[]'); + + // 기본 사용자가 없으면 생성 + if (users.length === 0) { + users = [ + { + username: 'hyungi', + full_name: '관리자', + password: 'djg3-jj34-X3Q3', + role: 'admin' + } + ]; + localStorage.setItem('work-report-users', JSON.stringify(users)); + } + + let user = users.find(u => u.username === currentUser.username); + + // 사용자가 없으면 기본값으로 생성 + if (!user) { + const username = currentUser.username; + user = { + username: username, + full_name: username === 'hyungi' ? '관리자' : username, + password: 'djg3-jj34-X3Q3', + role: username === 'hyungi' ? 'admin' : 'user' + }; + users.push(user); + localStorage.setItem('work-report-users', JSON.stringify(users)); + } + + if (user.password !== currentPassword) { + alert('현재 비밀번호가 올바르지 않습니다.'); + return; + } + + try { + // 비밀번호 변경 + user.password = newPassword; + localStorage.setItem('work-report-users', JSON.stringify(users)); + + // 현재 사용자 정보도 업데이트 + currentUser.password = newPassword; + localStorage.setItem('currentUser', JSON.stringify(currentUser)); + + showToastMessage('비밀번호가 성공적으로 변경되었습니다.'); + document.querySelector('.fixed').remove(); // 모달 닫기 + + } catch (error) { + alert('비밀번호 변경에 실패했습니다: ' + error.message); + } + } + // 토스트 메시지 표시 function showToastMessage(message, type = 'success') { const toast = document.createElement('div'); diff --git a/frontend/issue-view.html b/frontend/issue-view.html index bd090b9..945e03e 100644 --- a/frontend/issue-view.html +++ b/frontend/issue-view.html @@ -106,9 +106,9 @@ - @@ -599,6 +599,136 @@ return div; } + // 관리 버튼 클릭 처리 + function handleAdminClick() { + if (currentUser.role === 'admin') { + // 관리자: 사용자 관리 페이지로 이동 + window.location.href = 'admin.html'; + } else { + // 일반 사용자: 비밀번호 변경 모달 표시 + showPasswordChangeModal(); + } + } + + // 비밀번호 변경 모달 표시 + function showPasswordChangeModal() { + const modal = document.createElement('div'); + modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50'; + modal.onclick = (e) => { + if (e.target === modal) modal.remove(); + }; + + modal.innerHTML = ` +
+
+

비밀번호 변경

+ +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ `; + + document.body.appendChild(modal); + + // 폼 제출 이벤트 처리 + document.getElementById('passwordChangeForm').addEventListener('submit', handlePasswordChange); + } + + // 비밀번호 변경 처리 + async function handlePasswordChange(e) { + e.preventDefault(); + + const currentPassword = document.getElementById('currentPassword').value; + const newPassword = document.getElementById('newPassword').value; + const confirmPassword = document.getElementById('confirmPassword').value; + + // 새 비밀번호 확인 + if (newPassword !== confirmPassword) { + alert('새 비밀번호가 일치하지 않습니다.'); + return; + } + + // 현재 비밀번호 확인 (localStorage 기반) + let users = JSON.parse(localStorage.getItem('work-report-users') || '[]'); + + // 기본 사용자가 없으면 생성 + if (users.length === 0) { + users = [ + { + username: 'hyungi', + full_name: '관리자', + password: 'djg3-jj34-X3Q3', + role: 'admin' + } + ]; + localStorage.setItem('work-report-users', JSON.stringify(users)); + } + + let user = users.find(u => u.username === currentUser.username); + + // 사용자가 없으면 기본값으로 생성 + if (!user) { + const username = currentUser.username; + user = { + username: username, + full_name: username === 'hyungi' ? '관리자' : username, + password: 'djg3-jj34-X3Q3', + role: username === 'hyungi' ? 'admin' : 'user' + }; + users.push(user); + localStorage.setItem('work-report-users', JSON.stringify(users)); + } + + if (user.password !== currentPassword) { + alert('현재 비밀번호가 올바르지 않습니다.'); + return; + } + + try { + // 비밀번호 변경 + user.password = newPassword; + localStorage.setItem('work-report-users', JSON.stringify(users)); + + // 현재 사용자 정보도 업데이트 + currentUser.password = newPassword; + localStorage.setItem('currentUser', JSON.stringify(currentUser)); + + alert('비밀번호가 성공적으로 변경되었습니다.'); + document.querySelector('.fixed').remove(); // 모달 닫기 + + } catch (error) { + alert('비밀번호 변경에 실패했습니다: ' + error.message); + } + } + // 로그아웃 함수 function logout() { localStorage.removeItem('currentUser'); diff --git a/frontend/static/js/api.js b/frontend/static/js/api.js index 844eb8b..b4678ec 100644 --- a/frontend/static/js/api.js +++ b/frontend/static/js/api.js @@ -125,6 +125,13 @@ const AuthAPI = { current_password: currentPassword, new_password: newPassword }) + }), + + resetPassword: (userId, newPassword = '000000') => apiRequest(`/auth/users/${userId}`, { + method: 'PUT', + body: JSON.stringify({ + password: newPassword + }) }) }; From 85f20aa0511f11fe4c7313f2d6e52b3ebab76629 Mon Sep 17 00:00:00 2001 From: hyungi Date: Fri, 24 Oct 2025 10:31:47 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20=EC=9E=84=EC=8B=9C=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=ED=81=B4=EB=A6=B0=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 제거된 파일들: - migrate-data.html - 데이터 마이그레이션 도구 (완료됨) - debug-data.html - 디버깅 도구 (완료됨) - fix-api-data.html - API 데이터 수정 도구 (완료됨) - fix-project-id.html - 프로젝트 ID 수정 도구 (완료됨) - create-project-api.html - 프로젝트 API 생성 도구 (완료됨) - chart.html - 구 버전 차트 페이지 (사용 안함) - 루트 디렉토리 중복 파일들 (index.html, daily-work.html) 코드 정리: - 디버깅용 console.log 제거 - 이미지 압축 로그 정리 - 필터링 디버깅 로그 정리 서비스 배포 준비를 위한 클린업 작업 --- chart.html | 417 ------------------- daily-work.html | 378 ----------------- frontend/chart.html | 417 ------------------- frontend/create-project-api.html | 193 --------- frontend/debug-data.html | 158 -------- frontend/fix-api-data.html | 171 -------- frontend/fix-project-id.html | 134 ------ frontend/index.html | 17 - frontend/issue-view.html | 5 - frontend/migrate-data.html | 307 -------------- index.html | 671 ------------------------------- 11 files changed, 2868 deletions(-) delete mode 100644 chart.html delete mode 100644 daily-work.html delete mode 100644 frontend/chart.html delete mode 100644 frontend/create-project-api.html delete mode 100644 frontend/debug-data.html delete mode 100644 frontend/fix-api-data.html delete mode 100644 frontend/fix-project-id.html delete mode 100644 frontend/migrate-data.html delete mode 100644 index.html diff --git a/chart.html b/chart.html deleted file mode 100644 index ef7d45e..0000000 --- a/chart.html +++ /dev/null @@ -1,417 +0,0 @@ - - - - - - M-Project - 차트 - - - - - - - -
-
-

- 차트 -

-
- - - - -
-
-
- - -
- -
-
-
-
-

전체 사진

-

0

-
- -
-
- -
-
-
-

오늘

-

0

-
- -
-
- -
-
-
-

이번 주

-

0

-
- -
-
- -
-
-
-

가장 많은

-

-

-
- -
-
-
- - -
- -
-

카테고리별 분포

- -
- - -
-

최근 7일 추이

- -
-
- - -
-
-

전체 갤러리

- -
- - -
-
- - - - - - - diff --git a/daily-work.html b/daily-work.html deleted file mode 100644 index 983eb09..0000000 --- a/daily-work.html +++ /dev/null @@ -1,378 +0,0 @@ - - - - - - 일일 공수 입력 - - - - - -
- -
-
-
-

- 일일 공수 입력 -

- - 돌아가기 - -
-
-
- - -
- -
-

- 공수 입력 -

- -
- -
- - -
- - -
- - -

기본 근무시간: 8시간/인

-
- - -
-
- - -
- - - -
- - -
-
- 예상 총 공수 - 0시간 -
-
- - - -
-
- - -
-

- 최근 입력 내역 -

-
- -
-
-
-
- - - - diff --git a/frontend/chart.html b/frontend/chart.html deleted file mode 100644 index ef7d45e..0000000 --- a/frontend/chart.html +++ /dev/null @@ -1,417 +0,0 @@ - - - - - - M-Project - 차트 - - - - - - - -
-
-

- 차트 -

-
- - - - -
-
-
- - -
- -
-
-
-
-

전체 사진

-

0

-
- -
-
- -
-
-
-

오늘

-

0

-
- -
-
- -
-
-
-

이번 주

-

0

-
- -
-
- -
-
-
-

가장 많은

-

-

-
- -
-
-
- - -
- -
-

카테고리별 분포

- -
- - -
-

최근 7일 추이

- -
-
- - -
-
-

전체 갤러리

- -
- - -
-
- - - - - - - diff --git a/frontend/create-project-api.html b/frontend/create-project-api.html deleted file mode 100644 index 9525173..0000000 --- a/frontend/create-project-api.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - API 프로젝트 생성 - M Project - - - - - -
-
-

- API 프로젝트 생성 -

- -
-
-

🎯 작업 내용

-

- 백엔드 API를 통해 TKR-25009R M Project를 생성합니다.
- 데이터베이스에 실제 프로젝트 레코드가 생성됩니다. -

-
- -
- -
- - - - - API 데이터 수정 도구로 이동 - - - - 디버그 도구로 이동 - -
-
-
- - - - diff --git a/frontend/debug-data.html b/frontend/debug-data.html deleted file mode 100644 index e77f324..0000000 --- a/frontend/debug-data.html +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - 데이터 디버그 - M Project - - - - - -
-
-

- 데이터 디버그 -

- -
- - -
- -
-
-
-
- - - - diff --git a/frontend/fix-api-data.html b/frontend/fix-api-data.html deleted file mode 100644 index d8c7ed8..0000000 --- a/frontend/fix-api-data.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - API 데이터 수정 - M Project - - - - - -
-
-

- API 데이터 수정 -

- -
-
-

⚠️ 주의사항

-

- 이 작업은 API 데이터베이스의 모든 부적합 사항에 TKR-25009R 프로젝트 정보를 추가합니다.
- 관리자(hyungi) 계정으로 로그인한 상태에서만 실행하세요. -

-
- -
- -
- - - - - 디버그 도구로 이동 - - - - 메인으로 돌아가기 - -
-
-
- - - - diff --git a/frontend/fix-project-id.html b/frontend/fix-project-id.html deleted file mode 100644 index 53acc1e..0000000 --- a/frontend/fix-project-id.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - 프로젝트 ID 수정 - M Project - - - - -
-
-

- 프로젝트 ID 수정 -

- -
-
-

📋 작업 내용

-

- 큰 타임스탬프 ID를 작은 정수 ID로 변경합니다.
- TKR-25009R 프로젝트 ID: 17612642797041 -

-
- -
- -
- - - - - API 데이터 수정 도구로 이동 - - - - 디버그 도구로 이동 - -
-
-
- - - - diff --git a/frontend/index.html b/frontend/index.html index 953b1a8..37a6907 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -602,8 +602,6 @@ // 원본 파일 크기 확인 const originalSize = file.size; - console.log(`원본 이미지 크기: ${ImageUtils.formatFileSize(originalSize)}`); - // 이미지 압축 const compressedImage = await ImageUtils.compressImage(file, { maxWidth: 1280, @@ -611,11 +609,6 @@ quality: 0.75 }); - // 압축된 크기 확인 - const compressedSize = ImageUtils.getBase64Size(compressedImage); - console.log(`압축된 이미지 크기: ${ImageUtils.formatFileSize(compressedSize)}`); - console.log(`압축률: ${Math.round((1 - compressedSize/originalSize) * 100)}%`); - currentPhotos.push(compressedImage); updatePhotoPreview(); } @@ -829,7 +822,6 @@ updateProgress(90); updateLoadingMessage('완료 처리 중...', '거의 다 되었습니다'); - console.log(`업로드 완료: ${uploadTime}ms`); updateProgress(100); @@ -1607,19 +1599,13 @@ let dailyWorkTotal = 0; const dailyWorkData = JSON.parse(localStorage.getItem('daily-work-data') || '[]'); - console.log('일일공수 데이터:', dailyWorkData); - console.log('선택된 프로젝트 ID:', selectedProjectId); - if (selectedProjectId) { // 선택된 프로젝트의 일일 공수만 합계 dailyWorkData.forEach(dayWork => { - console.log('일일공수 항목:', dayWork); if (dayWork.projects) { dayWork.projects.forEach(project => { - console.log('프로젝트:', project, '매칭 확인:', project.projectId == selectedProjectId); if (project.projectId == selectedProjectId || project.projectId.toString() === selectedProjectId.toString()) { dailyWorkTotal += project.hours || 0; - console.log('시간 추가:', project.hours, '누적:', dailyWorkTotal); } }); } @@ -1627,13 +1613,10 @@ } else { // 전체 프로젝트의 일일 공수 합계 dailyWorkData.forEach(dayWork => { - console.log('전체 일일공수 항목:', dayWork); dailyWorkTotal += dayWork.totalHours || 0; }); } - console.log('최종 일일공수 합계:', dailyWorkTotal); - // 부적합 사항 해결 시간 계산 (필터링된 이슈만) const issueHours = filteredIssues.reduce((sum, issue) => sum + (issue.work_hours || 0), 0); const categoryCount = {}; diff --git a/frontend/issue-view.html b/frontend/issue-view.html index 945e03e..211ae32 100644 --- a/frontend/issue-view.html +++ b/frontend/issue-view.html @@ -317,8 +317,6 @@ } function filterIssues() { - console.log('필터링 시작 - 전체 이슈:', issues.length); - // 필터 값 가져오기 const selectedProjectId = document.getElementById('projectFilter').value; const reviewStatusFilter = document.getElementById('reviewStatusFilter').value; @@ -332,7 +330,6 @@ const issueProjectId = issue.project_id || issue.projectId; return issueProjectId && (issueProjectId == selectedProjectId || issueProjectId.toString() === selectedProjectId.toString()); }); - console.log('프로젝트 필터 후:', filteredIssues.length); } // 검토 상태 필터 적용 @@ -341,13 +338,11 @@ const isCompleted = isReviewCompleted(issue); return reviewStatusFilter === 'completed' ? isCompleted : !isCompleted; }); - console.log('검토 상태 필터 후:', filteredIssues.length); } // 날짜 필터 적용 if (dateFilter) { filteredIssues = filterByDate(filteredIssues, dateFilter); - console.log('날짜 필터 후:', filteredIssues.length); } // 전역 변수에 필터링된 결과 저장 diff --git a/frontend/migrate-data.html b/frontend/migrate-data.html deleted file mode 100644 index 3f61b23..0000000 --- a/frontend/migrate-data.html +++ /dev/null @@ -1,307 +0,0 @@ - - - - - - 데이터 마이그레이션 - M Project - - - - - -
-
-

- 데이터 마이그레이션 -

- -
-
-

⚠️ 주의사항

-

- 이 작업은 기존 데이터를 "M Project"로 마이그레이션합니다.
- 관리자(hyungi) 계정으로 로그인한 상태에서만 실행하세요. -

-
- -
- -
- - - - - 메인으로 돌아가기 - -
-
-
- - - - diff --git a/index.html b/index.html deleted file mode 100644 index e5fb7bb..0000000 --- a/index.html +++ /dev/null @@ -1,671 +0,0 @@ - - - - - - 작업보고서 시스템 - - - - - - -
-
-
- -

작업보고서 시스템

-

부적합 사항 관리 및 공수 계산

-
- -
-
- - -
- -
- - -
- - -
-
-
- - - - - - - \ No newline at end of file