fix(tkuser): 권한 기반 탭 자동 라우팅 — 제한 사용자 진입 시 첫 허용 탭 표시
users 권한 없는 일반 사용자가 빈 화면(비밀번호 변경 폼만) 보이던 문제 수정. 허용된 탭으로 자동 전환하고, 탭 버튼에 data-tab 속성 추가하여 프로그래밍적 switchTab() 호출 시 active 버튼도 정확히 갱신되도록 개선. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,43 +34,43 @@
|
||||
<nav id="tabNav" class="bg-white border-b shadow-sm sticky top-14 z-40 hidden">
|
||||
<div id="tabNavInner" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex gap-1 py-2 overflow-x-auto">
|
||||
<button class="tab-btn active px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('users')">
|
||||
<button class="tab-btn active px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="users" onclick="switchTab('users', event)">
|
||||
<i class="fas fa-users mr-2"></i>사용자
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('projects')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="projects" onclick="switchTab('projects', event)">
|
||||
<i class="fas fa-folder-open mr-2"></i>프로젝트
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('workplaces')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="workplaces" onclick="switchTab('workplaces', event)">
|
||||
<i class="fas fa-building mr-2"></i>작업장
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('workers')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="workers" onclick="switchTab('workers', event)">
|
||||
<i class="fas fa-hard-hat mr-2"></i>작업자
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('departments')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="departments" onclick="switchTab('departments', event)">
|
||||
<i class="fas fa-sitemap mr-2"></i>부서
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('permissions')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="permissions" onclick="switchTab('permissions', event)">
|
||||
<i class="fas fa-shield-alt mr-2"></i>권한
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('issueTypes')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="issueTypes" onclick="switchTab('issueTypes', event)">
|
||||
<i class="fas fa-exclamation-triangle mr-2"></i>이슈 유형
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('tasks')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="tasks" onclick="switchTab('tasks', event)">
|
||||
<i class="fas fa-tasks mr-2"></i>작업
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('vacations')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="vacations" onclick="switchTab('vacations', event)">
|
||||
<i class="fas fa-umbrella-beach mr-2"></i>휴가
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('partners')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="partners" onclick="switchTab('partners', event)">
|
||||
<i class="fas fa-truck mr-2"></i>협력업체
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('vendors')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="vendors" onclick="switchTab('vendors', event)">
|
||||
<i class="fas fa-store mr-2"></i>업체
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('consumables')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="consumables" onclick="switchTab('consumables', event)">
|
||||
<i class="fas fa-box-open mr-2"></i>소모품
|
||||
</button>
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" onclick="switchTab('notificationRecipients')">
|
||||
<button class="tab-btn px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap" data-tab="notificationRecipients" onclick="switchTab('notificationRecipients', event)">
|
||||
<i class="fas fa-bell mr-2"></i>알림 수신자
|
||||
</button>
|
||||
</div>
|
||||
@@ -2000,9 +2000,9 @@
|
||||
</div>
|
||||
|
||||
<!-- JS: Core (config, token, api, toast, helpers, init) -->
|
||||
<script src="/static/js/tkuser-core.js?v=2026031601"></script>
|
||||
<script src="/static/js/tkuser-core.js?v=2026031602"></script>
|
||||
<!-- JS: Tabs -->
|
||||
<script src="/static/js/tkuser-tabs.js?v=2026031401"></script>
|
||||
<script src="/static/js/tkuser-tabs.js?v=2026031602"></script>
|
||||
<!-- JS: Individual modules -->
|
||||
<script src="/static/js/tkuser-users.js?v=2026031601"></script>
|
||||
<script src="/static/js/tkuser-projects.js?v=2026031401"></script>
|
||||
|
||||
@@ -128,9 +128,6 @@ async function init() {
|
||||
await loadDepartmentsCache();
|
||||
await loadUsers();
|
||||
} else {
|
||||
// 비밀번호 변경은 항상 표시
|
||||
document.getElementById('passwordChangeSection').classList.remove('hidden');
|
||||
|
||||
// tkuser 탭별 권한 확인
|
||||
try {
|
||||
const result = await api(`/permissions/users/${currentUser.id}/effective-permissions`);
|
||||
@@ -154,10 +151,8 @@ async function init() {
|
||||
document.getElementById('tabNav').classList.remove('hidden');
|
||||
// 비허용 탭 버튼 숨김
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||||
const onclick = btn.getAttribute('onclick') || '';
|
||||
const match = onclick.match(/switchTab\('(\w+)'\)/);
|
||||
if (match) {
|
||||
const tabName = match[1];
|
||||
const tabName = btn.getAttribute('data-tab');
|
||||
if (tabName) {
|
||||
// permissions 탭은 admin 전용
|
||||
if (tabName === 'permissions') {
|
||||
btn.style.display = 'none';
|
||||
@@ -167,12 +162,20 @@ async function init() {
|
||||
}
|
||||
});
|
||||
|
||||
// tkuser.users 권한 시 adminSection 표시
|
||||
// tkuser.users 권한 시 adminSection + passwordChangeSection 표시
|
||||
if (currentUserAllowedTabs.has('users')) {
|
||||
document.getElementById('adminSection').classList.remove('hidden');
|
||||
document.getElementById('passwordChangeSection').classList.remove('hidden');
|
||||
await loadDepartmentsCache();
|
||||
await loadUsers();
|
||||
} else {
|
||||
// users 권한 없음 → tab-users 숨기고 첫 허용 탭으로 자동 전환
|
||||
document.getElementById('tab-users').classList.add('hidden');
|
||||
switchTab([...currentUserAllowedTabs][0]);
|
||||
}
|
||||
} else {
|
||||
// 아무 권한 없음 → 비밀번호 변경만 표시 (fallback)
|
||||
document.getElementById('passwordChangeSection').classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
@@ -194,7 +197,7 @@ async function init() {
|
||||
'consumables','notificationRecipients'];
|
||||
const urlTab = new URLSearchParams(location.search).get('tab');
|
||||
if (urlTab && ALLOWED_TABS.includes(urlTab)) {
|
||||
const tabBtn = document.querySelector(`.tab-btn[onclick*="switchTab('${urlTab}')"]`);
|
||||
const tabBtn = document.querySelector(`.tab-btn[data-tab="${urlTab}"]`);
|
||||
if (tabBtn && tabBtn.style.display !== 'none') {
|
||||
tabBtn.click();
|
||||
const url = new URL(location);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ===== Tab ===== */
|
||||
function switchTab(name) {
|
||||
function switchTab(name, event) {
|
||||
// 권한 guard: currentUserAllowedTabs가 Set이면 허용된 탭만 접근
|
||||
if (typeof currentUserAllowedTabs !== 'undefined'
|
||||
&& currentUserAllowedTabs
|
||||
@@ -8,7 +8,12 @@ function switchTab(name) {
|
||||
document.querySelectorAll('[id^="tab-"]').forEach(el => el.classList.add('hidden'));
|
||||
document.getElementById('tab-' + name)?.classList.remove('hidden');
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
||||
event.currentTarget.classList.add('active');
|
||||
if (event?.currentTarget) {
|
||||
event.currentTarget.classList.add('active');
|
||||
} else {
|
||||
const btn = document.querySelector(`.tab-btn[data-tab="${name}"]`);
|
||||
if (btn) btn.classList.add('active');
|
||||
}
|
||||
// 사이드바 레이아웃 탭에서 main/nav/header 너비 확장
|
||||
const mainEl = document.querySelector('main');
|
||||
const navInner = document.getElementById('tabNavInner');
|
||||
|
||||
Reference in New Issue
Block a user