Files
TK-FB-Project/web-ui/js/load-navbar.js
Hyungi Ahn 90d3e32992 feat: 일일순회점검 시스템 구축 및 관리 기능 개선
- 일일순회점검 시스템 신규 구현
  - DB 테이블: patrol_checklist_items, daily_patrol_sessions, patrol_check_records, workplace_items, item_types
  - API: /api/patrol/* 엔드포인트
  - 프론트엔드: 지도 기반 작업장 점검 UI

- 설비 관리 기능 개선
  - 구매 관련 필드 추가 (구매일, 가격, 공급업체 등)
  - 설비 코드 자동 생성 (TKP-XXX 형식)

- 작업장 관리 개선
  - 레이아웃 이미지 업로드 기능
  - 마커 위치 저장 기능

- 부서 관리 기능 추가
- 사이드바 네비게이션 카테고리 재구성
- 이미지 401 오류 수정 (정적 파일 경로 처리)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:41:41 +09:00

289 lines
8.2 KiB
JavaScript

// /js/load-navbar.js
import { getUser, clearAuthData } from './auth.js';
import { loadComponent } from './component-loader.js';
import { config } from './config.js';
// 역할 이름을 한글로 변환하는 맵
const ROLE_NAMES = {
'system admin': '시스템 관리자',
'admin': '관리자',
'system': '시스템 관리자',
'leader': '그룹장',
'user': '작업자',
'support': '지원팀',
'default': '사용자',
};
/**
* 네비게이션 바 DOM을 사용자 정보와 역할에 맞게 수정하는 프로세서입니다.
* @param {Document} doc - 파싱된 HTML 문서 객체
*/
async function processNavbarDom(doc) {
const currentUser = getUser();
if (!currentUser) return;
// 1. 역할 및 페이지 권한 기반 메뉴 필터링
await filterMenuByPageAccess(doc, currentUser);
// 2. 사용자 정보 채우기
populateUserInfo(doc, currentUser);
}
/**
* 사용자의 페이지 접근 권한에 따라 메뉴 항목을 필터링합니다.
* @param {Document} doc - 파싱된 HTML 문서 객체
* @param {object} currentUser - 현재 사용자 객체
*/
async function filterMenuByPageAccess(doc, currentUser) {
const userRole = (currentUser.role || '').toLowerCase();
// Admin은 모든 메뉴 표시 + .admin-only 요소 활성화
if (userRole === 'admin' || userRole === 'system admin') {
doc.querySelectorAll('.admin-only').forEach(el => el.classList.add('visible'));
return;
}
try {
// 사용자의 페이지 접근 권한 조회
const cached = localStorage.getItem('userPageAccess');
let accessiblePages = null;
if (cached) {
const cacheData = JSON.parse(cached);
// 캐시가 5분 이내인 경우 사용
if (Date.now() - cacheData.timestamp < 5 * 60 * 1000) {
accessiblePages = cacheData.pages;
}
}
// 캐시가 없으면 API 호출
if (!accessiblePages) {
const response = await fetch(`${window.API_BASE_URL}/users/${currentUser.user_id}/page-access`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (!response.ok) {
console.error('페이지 권한 조회 실패:', response.status);
return;
}
const data = await response.json();
accessiblePages = data.data.pageAccess || [];
// 캐시 저장
localStorage.setItem('userPageAccess', JSON.stringify({
pages: accessiblePages,
timestamp: Date.now()
}));
}
// 접근 가능한 페이지 키 목록
const accessiblePageKeys = accessiblePages
.filter(p => p.can_access === 1)
.map(p => p.page_key);
// 메뉴 항목에 data-page-key 속성이 있으면 해당 권한 체크
const menuItems = doc.querySelectorAll('[data-page-key]');
menuItems.forEach(item => {
const pageKey = item.getAttribute('data-page-key');
// 대시보드와 프로필 페이지는 모든 사용자 접근 가능
if (pageKey === 'dashboard' || pageKey.startsWith('profile.')) {
return;
}
// 권한이 없으면 메뉴 항목 제거
if (!accessiblePageKeys.includes(pageKey)) {
item.remove();
}
});
// Admin 전용 메뉴는 무조건 제거
doc.querySelectorAll('.admin-only').forEach(el => el.remove());
} catch (error) {
console.error('메뉴 필터링 오류:', error);
}
}
/**
* 네비게이션 바에 사용자 정보를 채웁니다.
* @param {Document} doc - 파싱된 HTML 문서 객체
* @param {object} user - 현재 사용자 객체
*/
function populateUserInfo(doc, user) {
const displayName = user.name || user.username;
// 대소문자 구분 없이 처리
const roleLower = (user.role || '').toLowerCase();
const roleName = ROLE_NAMES[roleLower] || ROLE_NAMES.default;
const elements = {
'userName': displayName,
'userRole': roleName,
'userInitial': displayName.charAt(0),
};
for (const id in elements) {
const el = doc.getElementById(id);
if (el) el.textContent = elements[id];
}
// 메인 대시보드 URL 설정
const dashboardBtn = doc.getElementById('dashboardBtn');
if (dashboardBtn) {
dashboardBtn.href = '/pages/dashboard.html';
}
}
/**
* 네비게이션 바와 관련된 모든 이벤트를 설정합니다.
*/
function setupNavbarEvents() {
const logoutButton = document.getElementById('logoutBtn');
if (logoutButton) {
logoutButton.addEventListener('click', () => {
if (confirm('로그아웃 하시겠습니까?')) {
clearAuthData();
window.location.href = config.paths.loginPage;
}
});
}
}
/**
* 현재 날짜와 시간을 업데이트하는 함수
*/
function updateDateTime() {
const now = new Date();
// 시간 업데이트
const timeElement = document.getElementById('timeValue');
if (timeElement) {
timeElement.textContent = now.toLocaleTimeString('ko-KR', { hour12: false });
}
// 날짜 업데이트
const dateElement = document.getElementById('dateValue');
if (dateElement) {
const days = ['일', '월', '화', '수', '목', '금', '토'];
const month = now.getMonth() + 1;
const date = now.getDate();
const day = days[now.getDay()];
dateElement.textContent = `${month}${date}일 (${day})`;
}
}
// 날씨 아이콘 매핑
const WEATHER_ICONS = {
clear: '☀️',
rain: '🌧️',
snow: '❄️',
heat: '🔥',
cold: '🥶',
wind: '💨',
fog: '🌫️',
dust: '😷',
cloudy: '⛅',
overcast: '☁️'
};
// 날씨 조건명
const WEATHER_NAMES = {
clear: '맑음',
rain: '비',
snow: '눈',
heat: '폭염',
cold: '한파',
wind: '강풍',
fog: '안개',
dust: '미세먼지',
cloudy: '구름많음',
overcast: '흐림'
};
/**
* 날씨 정보를 가져와서 업데이트하는 함수
*/
async function updateWeather() {
try {
const token = localStorage.getItem('token');
if (!token) return;
const response = await fetch(`${window.API_BASE_URL}/tbm/weather/current`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('날씨 API 호출 실패');
}
const result = await response.json();
if (result.success && result.data) {
const { temperature, conditions, weatherData } = result.data;
// 온도 표시
const tempElement = document.getElementById('weatherTemp');
if (tempElement && temperature !== null && temperature !== undefined) {
tempElement.textContent = `${Math.round(temperature)}°C`;
}
// 날씨 아이콘 및 설명
const iconElement = document.getElementById('weatherIcon');
const descElement = document.getElementById('weatherDesc');
if (conditions && conditions.length > 0) {
const primaryCondition = conditions[0];
if (iconElement) {
iconElement.textContent = WEATHER_ICONS[primaryCondition] || '🌤️';
}
if (descElement) {
descElement.textContent = WEATHER_NAMES[primaryCondition] || '맑음';
}
} else {
if (iconElement) iconElement.textContent = '☀️';
if (descElement) descElement.textContent = '맑음';
}
// 날씨 섹션 표시
const weatherSection = document.getElementById('weatherSection');
if (weatherSection) {
weatherSection.style.opacity = '1';
}
}
} catch (error) {
console.warn('날씨 정보 로드 실패:', error.message);
// 실패해도 기본값 표시
const descElement = document.getElementById('weatherDesc');
if (descElement) {
descElement.textContent = '날씨 정보 없음';
}
}
}
// 메인 로직: DOMContentLoaded 시 실행
document.addEventListener('DOMContentLoaded', async () => {
if (getUser()) {
// 1. 컴포넌트 로드 및 DOM 수정
await loadComponent('navbar', '#navbar-container', processNavbarDom);
// 2. DOM에 삽입된 후에 이벤트 리스너 설정
setupNavbarEvents();
// 3. 실시간 날짜/시간 업데이트 시작
updateDateTime();
setInterval(updateDateTime, 1000);
// 4. 날씨 정보 로드 (10분마다 갱신)
updateWeather();
setInterval(updateWeather, 10 * 60 * 1000);
}
});