feat: localStorage 문제 해결 및 시스템 개선

- localStorage와 DB ID 불일치 문제 해결
- 프로젝트별 보고서 시간 필터링 수정
- 일반 사용자에게 일일공수 메뉴 숨김
- 공통 헤더 및 인증 시스템 구현
- 프로젝트별 일일공수 분리 기능 추가 (ProjectDailyWork 모델)
- IssuesAPI에서 project_id 누락 문제 수정
- 사용자 인증 통합 (TokenManager 기반)
This commit is contained in:
hyungi
2025-10-24 12:24:24 +09:00
parent b024a178d0
commit 5fe51ab1d5
9 changed files with 358 additions and 87 deletions

View File

@@ -135,6 +135,7 @@ const IssuesAPI = {
const dataToSend = {
category: issueData.category,
description: issueData.description,
project_id: issueData.project_id,
photo: issueData.photos && issueData.photos.length > 0 ? issueData.photos[0] : null,
photo2: issueData.photos && issueData.photos.length > 1 ? issueData.photos[1] : null
};

View File

@@ -0,0 +1,68 @@
// 공통 인증 및 네비게이션 관리
class AuthCommon {
static init(currentPage = '') {
// 토큰 기반 사용자 정보 확인
const user = TokenManager.getUser();
if (!user) {
window.location.href = 'index.html';
return null;
}
// 전역 currentUser 설정
window.currentUser = user;
// 헤더 생성 (페이지별로 다른 active 상태)
CommonHeader.init(currentPage);
// 사용자 정보 표시
this.updateUserDisplay(user);
// 네비게이션 권한 업데이트
this.updateNavigation(user);
return user;
}
static updateUserDisplay(user) {
const userDisplayElement = document.getElementById('userDisplay');
if (userDisplayElement) {
const displayName = user.full_name || user.username;
userDisplayElement.textContent = `${displayName} (${user.username})`;
}
}
static updateNavigation(user) {
const isAdmin = user.role === 'admin';
// 관리자 전용 메뉴들
const adminMenus = [
'dailyWorkBtn',
'listBtn',
'summaryBtn',
'projectBtn',
'adminBtn'
];
adminMenus.forEach(menuId => {
const element = document.getElementById(menuId);
if (element) {
element.style.display = isAdmin ? '' : 'none';
}
});
}
static logout() {
AuthAPI.logout();
}
}
// 전역 함수들
function logout() {
AuthCommon.logout();
}
function showSection(sectionName) {
if (typeof window.showSection === 'function') {
window.showSection(sectionName);
}
}

View File

@@ -0,0 +1,106 @@
// 공통 헤더 생성 및 관리
class CommonHeader {
static create(currentPage = '') {
return `
<!-- 헤더 -->
<header class="bg-white shadow-sm sticky top-0 z-50">
<div class="container mx-auto px-4 py-3">
<div class="flex justify-between items-center">
<h1 class="text-xl font-bold text-gray-800">
<i class="fas fa-clipboard-check text-blue-500 mr-2"></i>작업보고서
</h1>
<div class="flex items-center gap-4">
<span class="text-sm text-gray-600" id="userDisplay"></span>
<button onclick="AuthCommon.logout()" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-sign-out-alt"></i>
</button>
</div>
</div>
</div>
</header>
<!-- 네비게이션 -->
<nav class="bg-white border-b">
<div class="container mx-auto px-4">
<div class="flex gap-2 py-2 overflow-x-auto">
<a href="daily-work.html" class="nav-link" id="dailyWorkBtn" style="display: none;">
<i class="fas fa-calendar-check mr-2"></i>일일 공수
</a>
${this.getNavButton('index.html', 'mainBtn', 'fas fa-camera-retro', '부적합 등록', currentPage === 'main')}
${this.getNavButton('issue-view.html', 'issueViewBtn', 'fas fa-search', '부적합 조회', currentPage === 'issue-view')}
${this.getNavButtonInternal('list', 'listBtn', 'fas fa-list', '목록 관리', currentPage === 'list')}
${this.getNavButtonInternal('summary', 'summaryBtn', 'fas fa-chart-bar', '보고서', currentPage === 'summary')}
<a href="project-management.html" class="nav-link" style="display:none;" id="projectBtn">
<i class="fas fa-folder-open mr-2"></i>프로젝트 관리
</a>
<a href="admin.html" class="nav-link" style="display:none;" id="adminBtn">
<i class="fas fa-users-cog mr-2"></i>관리
</a>
</div>
</div>
</nav>
`;
}
static getNavButton(href, id, iconClass, text, isActive = false) {
const activeClass = isActive ? ' active' : '';
return `<a href="${href}" class="nav-link${activeClass}" id="${id}">
<i class="${iconClass} mr-2"></i>${text}
</a>`;
}
static getNavButtonInternal(section, id, iconClass, text, isActive = false) {
const activeClass = isActive ? ' active' : '';
if (section === 'list' || section === 'summary') {
return `<button class="nav-link${activeClass}" onclick="showSection('${section}')" style="display:none;" id="${id}">
<i class="${iconClass} mr-2"></i>${text}
</button>`;
}
return `<a href="index.html#${section}" class="nav-link${activeClass}" style="display:none;" id="${id}">
<i class="${iconClass} mr-2"></i>${text}
</a>`;
}
static init(currentPage = '') {
// 헤더 HTML 삽입
const headerContainer = document.getElementById('header-container');
if (headerContainer) {
headerContainer.innerHTML = this.create();
}
// 현재 페이지 활성화
this.setActivePage(currentPage);
}
static setActivePage(currentPage) {
// 모든 nav-link에서 active 클래스 제거
document.querySelectorAll('.nav-link').forEach(link => {
link.classList.remove('active');
});
// 현재 페이지에 active 클래스 추가
const activeElement = document.getElementById(currentPage);
if (activeElement) {
activeElement.classList.add('active');
}
}
}
// 관리자 버튼 클릭 처리 (전역 함수)
function handleAdminClick() {
if (window.currentUser && window.currentUser.role === 'admin') {
window.location.href = 'admin.html';
} else {
// 비밀번호 변경 모달 표시
if (typeof showPasswordChangeModal === 'function') {
showPasswordChangeModal();
}
}
}
// 섹션 전환 (메인 페이지용)
function showSection(sectionName) {
if (typeof window.showSection === 'function') {
window.showSection(sectionName);
}
}