const Auth = (() => { const KEY_TOKEN = 'ev_token'; const KEY_ROLE = 'ev_role'; const KEY_NAME = 'ev_name'; const KEY_ID = 'ev_uid'; function save(token, role, name, id) { localStorage.setItem(KEY_TOKEN, token); localStorage.setItem(KEY_ROLE, role); localStorage.setItem(KEY_NAME, name); localStorage.setItem(KEY_ID, id); } function token() { return localStorage.getItem(KEY_TOKEN); } function role() { return localStorage.getItem(KEY_ROLE); } function name() { return localStorage.getItem(KEY_NAME); } function uid() { return localStorage.getItem(KEY_ID); } function logout() { [KEY_TOKEN, KEY_ROLE, KEY_NAME, KEY_ID].forEach(k => localStorage.removeItem(k)); location.href = '/pages/login.html'; } function require(allowedRoles) { if (!token()) { // 로그인 후 원래 페이지로 돌아올 수 있도록 현재 URL 저장 if (location.pathname !== '/pages/login.html') { sessionStorage.setItem('ev_redirect', location.pathname + location.search); } logout(); return false; } if (allowedRoles && !allowedRoles.includes(role())) { alert('접근 권한이 없습니다. (현재 역할: ' + (role() || '없음') + ')'); logout(); return false; } return true; } function renderNav(el) { if (!el) return; el.innerHTML = ` ${name()} [${role()}] 로그아웃 `; // 모바일 오버레이 삽입 (중복 방지) if (!document.getElementById('mobileNavOverlay')) { const ov = document.createElement('div'); ov.id = 'mobileNavOverlay'; ov.className = 'mobile-nav-overlay'; ov.addEventListener('click', closeMobileNav); document.body.appendChild(ov); } // 사이드바 링크 클릭 시 드로어 닫기 + 하단 로그아웃 주입 (모바일용) setTimeout(() => { document.querySelectorAll('.sidebar a').forEach(a => { a.addEventListener('click', closeMobileNav); }); const sidebar = document.querySelector('.sidebar'); if (sidebar && !sidebar.querySelector('.sidebar-user-footer')) { const footer = document.createElement('div'); footer.className = 'sidebar-user-footer'; footer.innerHTML = `
${name()} [${role()}]
🚪 로그아웃`; sidebar.appendChild(footer); } }, 0); } function toggleMobileNav() { const sidebar = document.querySelector('.sidebar'); const overlay = document.getElementById('mobileNavOverlay'); if (!sidebar) return; const opening = !sidebar.classList.contains('mobile-open'); sidebar.classList.toggle('mobile-open', opening); if (overlay) overlay.classList.toggle('show', opening); } function closeMobileNav() { var sb = document.querySelector('.sidebar'); if (sb) sb.classList.remove('mobile-open'); var ov = document.getElementById('mobileNavOverlay'); if (ov) ov.classList.remove('show'); } function statusBadge(status) { const map = { pending_approval: '승인대기', pending: '접수', in_progress: '처리중', done: '완료', waiting: '부품대기', revisit: '재방문', closed: '상황종료', registered: '등록', reviewing: '검토중', developing: '개발중', deployed: '배포완료', observer: '옵저버', }; return `${map[status] || status}`; } function costStatusBadge(s) { const map = { pending:'미처리', billed:'청구완료', waived:'면제', settled:'정산완료' }; return `${map[s] || s}`; } function fmtDt(dt) { if (!dt) return '-'; return new Date(dt).toLocaleString('ko-KR', {year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'}); } return { save, token, role, name, uid, logout, require, renderNav, toggleMobileNav, closeMobileNav, statusBadge, costStatusBadge, fmtDt }; })();