사이드바 메뉴 버그 수정

- 테넌트 메뉴: session 대신 로그인 사용자의 tenant_id만 사용
- 메뉴 그룹 토글: 자식 있으면 기본 표시, localStorage 복원 로직 통일
- 프레젠테이션 페이지 CSS: 글로벌 선택자를 .presentation-container로 스코핑
This commit is contained in:
2025-12-16 23:22:44 +09:00
parent 15a2cff453
commit e91789ff3d
7 changed files with 73 additions and 40 deletions

View File

@@ -16,7 +16,8 @@ class SidebarMenuService
public function getUserMenuTree(?User $user = null): Collection
{
$user = $user ?? auth()->user();
$tenantId = session('selected_tenant_id', 1);
// 로그인한 사용자의 tenant_id만 사용 (session 값 무시)
$tenantId = $user?->tenant_id ?? 1;
// 테넌트의 모든 활성 메뉴 조회
$allMenus = Menu::withoutGlobalScopes()

View File

@@ -5,13 +5,14 @@
$isExpanded = $sidebarMenuService->isMenuOrChildActive($menu);
$groupId = 'menu-group-' . $menu->id;
$children = $menu->menuChildren ?? collect();
$hasChildren = $children->isNotEmpty();
$paddingLeft = $depth > 0 ? ($depth * 0.75 + 0.75) . 'rem' : '0.75rem';
@endphp
<li class="pt-4 pb-1 border-t border-gray-200 mt-2">
{{-- 그룹 헤더 (접기/펼치기 버튼) --}}
<button
onclick="toggleGroup('{{ $groupId }}')"
onclick="toggleMenuGroup('{{ $groupId }}')"
class="sidebar-group-header w-full flex items-center justify-between px-3 py-2 text-xs font-bold text-gray-600 uppercase tracking-wider hover:bg-gray-50 rounded"
style="padding-left: {{ $paddingLeft }}"
>
@@ -23,7 +24,7 @@ class="sidebar-group-header w-full flex items-center justify-between px-3 py-2 t
</span>
<svg
id="{{ $groupId }}-icon"
class="w-3 h-3 transition-transform sidebar-text {{ $isExpanded ? 'rotate-180' : '' }}"
class="w-3 h-3 transition-transform sidebar-text {{ $hasChildren ? 'rotate-180' : '' }}"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@@ -32,8 +33,8 @@ class="w-3 h-3 transition-transform sidebar-text {{ $isExpanded ? 'rotate-180' :
</svg>
</button>
{{-- 하위 메뉴 --}}
<ul id="{{ $groupId }}" class="space-y-1 mt-1 {{ $isExpanded ? '' : 'hidden' }}">
{{-- 하위 메뉴 (자식이 있으면 기본 표시, localStorage에서 상태 복원) --}}
<ul id="{{ $groupId }}" class="space-y-1 mt-1" style="display: {{ $hasChildren ? 'block' : 'none' }};">
@foreach($children as $child)
@if($child->menuChildren && $child->menuChildren->isNotEmpty())
{{-- 하위에 그룹이 있는 경우 (중첩 그룹) --}}

View File

@@ -14,12 +14,12 @@
.slide.active { display: flex; animation: slideInRight 0.5s ease-out; }
.slide-content { background: rgba(255, 255, 255, 0.98); border-radius: 24px; padding: 60px; max-width: 1200px; width: 100%; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05); animation: fadeIn 0.8s ease-out; margin: auto 0; border: 1px solid rgba(255, 255, 255, 0.2); }
h1 { color: #2563eb; font-size: 3em; margin-bottom: 20px; text-align: center; font-weight: 800; }
h2 { color: #1e40af; font-size: 2.5em; margin-bottom: 30px; text-align: center; border-bottom: 3px solid #2563eb; padding-bottom: 15px; font-weight: 700; }
h3 { color: #2563eb; font-size: 1.8em; margin: 25px 0 15px 0; font-weight: 700; }
h4 { color: #1e40af; font-size: 1.3em; margin: 15px 0 10px 0; font-weight: 700; }
p, li { font-size: 1.2em; line-height: 1.8; color: #1e293b; margin-bottom: 15px; }
ul, ol { margin-left: 30px; }
.presentation-container h1 { color: #2563eb; font-size: 3em; margin-bottom: 20px; text-align: center; font-weight: 800; }
.presentation-container h2 { color: #1e40af; font-size: 2.5em; margin-bottom: 30px; text-align: center; border-bottom: 3px solid #2563eb; padding-bottom: 15px; font-weight: 700; }
.presentation-container h3 { color: #2563eb; font-size: 1.8em; margin: 25px 0 15px 0; font-weight: 700; }
.presentation-container h4 { color: #1e40af; font-size: 1.3em; margin: 15px 0 10px 0; font-weight: 700; }
.presentation-container p, .presentation-container li { font-size: 1.2em; line-height: 1.8; color: #1e293b; margin-bottom: 15px; }
.presentation-container ul, .presentation-container ol { margin-left: 30px; }
.company-info { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin: 30px 0; }
.info-card { background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%); color: white; padding: 25px; border-radius: 16px; box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2); animation: scaleIn 0.5s ease-out; border: 1px solid rgba(255, 255, 255, 0.1); }
@@ -60,8 +60,8 @@
@media (max-width: 768px) {
.slide-content { padding: 30px; }
h1 { font-size: 2em; } h2 { font-size: 1.8em; } h3 { font-size: 1.4em; }
p, li { font-size: 1em; }
.presentation-container h1 { font-size: 2em; } .presentation-container h2 { font-size: 1.8em; } .presentation-container h3 { font-size: 1.4em; }
.presentation-container p, .presentation-container li { font-size: 1em; }
.navigation { bottom: 15px; right: 15px; }
.nav-btn { padding: 10px 20px; font-size: 0.9em; }
.slide-number { bottom: 15px; left: 15px; padding: 8px 15px; font-size: 0.9em; }

View File

@@ -14,11 +14,11 @@
.slide.active { display: flex; animation: slideInRight 0.5s ease-out; }
.slide-content { background: rgba(255, 255, 255, 0.98); border-radius: 24px; padding: 60px; max-width: 1200px; width: 100%; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05); animation: fadeIn 0.8s ease-out; margin: auto 0; border: 1px solid rgba(255, 255, 255, 0.2); }
h1 { color: #2563eb; font-size: 3em; margin-bottom: 20px; text-align: center; font-weight: 800; }
h2 { color: #1e40af; font-size: 2.5em; margin-bottom: 30px; text-align: center; border-bottom: 3px solid #2563eb; padding-bottom: 15px; font-weight: 700; }
h3 { color: #2563eb; font-size: 1.8em; margin: 25px 0 15px 0; font-weight: 700; }
p, li { font-size: 1.2em; line-height: 1.8; color: #1e293b; margin-bottom: 15px; }
ul { margin-left: 30px; }
.presentation-container h1 { color: #2563eb; font-size: 3em; margin-bottom: 20px; text-align: center; font-weight: 800; }
.presentation-container h2 { color: #1e40af; font-size: 2.5em; margin-bottom: 30px; text-align: center; border-bottom: 3px solid #2563eb; padding-bottom: 15px; font-weight: 700; }
.presentation-container h3 { color: #2563eb; font-size: 1.8em; margin: 25px 0 15px 0; font-weight: 700; }
.presentation-container p, .presentation-container li { font-size: 1.2em; line-height: 1.8; color: #1e293b; margin-bottom: 15px; }
.presentation-container ul { margin-left: 30px; }
.company-info { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin: 30px 0; }
.info-card { background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%); color: white; padding: 25px; border-radius: 16px; box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2); animation: scaleIn 0.5s ease-out; border: 1px solid rgba(255, 255, 255, 0.1); }
@@ -48,8 +48,8 @@
@media (max-width: 768px) {
.slide-content { padding: 30px; }
h1 { font-size: 2em; } h2 { font-size: 1.8em; } h3 { font-size: 1.4em; }
p, li { font-size: 1em; }
.presentation-container h1 { font-size: 2em; } .presentation-container h2 { font-size: 1.8em; } .presentation-container h3 { font-size: 1.4em; }
.presentation-container p, .presentation-container li { font-size: 1em; }
.navigation { bottom: 15px; right: 15px; }
.nav-btn { padding: 10px 20px; font-size: 0.9em; }
.slide-number { bottom: 15px; left: 15px; padding: 8px 15px; font-size: 0.9em; }

View File

@@ -14,11 +14,11 @@
.slide.active { display: flex; animation: slideInRight 0.5s ease-out; }
.slide-content { background: rgba(255, 255, 255, 0.98); border-radius: 24px; padding: 60px; max-width: 1200px; width: 100%; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05); animation: fadeIn 0.8s ease-out; margin: auto 0; border: 1px solid rgba(255, 255, 255, 0.2); }
h1 { color: #2563eb; font-size: 3em; margin-bottom: 20px; text-align: center; font-weight: 800; }
h2 { color: #1e40af; font-size: 2.5em; margin-bottom: 30px; text-align: center; border-bottom: 3px solid #2563eb; padding-bottom: 15px; font-weight: 700; }
h3 { color: #2563eb; font-size: 1.8em; margin: 25px 0 15px 0; font-weight: 700; }
p, li { font-size: 1.2em; line-height: 1.8; color: #1e293b; margin-bottom: 15px; }
ul { margin-left: 30px; }
.presentation-container h1 { color: #2563eb; font-size: 3em; margin-bottom: 20px; text-align: center; font-weight: 800; }
.presentation-container h2 { color: #1e40af; font-size: 2.5em; margin-bottom: 30px; text-align: center; border-bottom: 3px solid #2563eb; padding-bottom: 15px; font-weight: 700; }
.presentation-container h3 { color: #2563eb; font-size: 1.8em; margin: 25px 0 15px 0; font-weight: 700; }
.presentation-container p, .presentation-container li { font-size: 1.2em; line-height: 1.8; color: #1e293b; margin-bottom: 15px; }
.presentation-container ul { margin-left: 30px; }
.company-info { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin: 30px 0; }
.info-card { background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%); color: white; padding: 25px; border-radius: 16px; box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2); animation: scaleIn 0.5s ease-out; border: 1px solid rgba(255, 255, 255, 0.1); }
@@ -48,8 +48,8 @@
@media (max-width: 768px) {
.slide-content { padding: 30px; }
h1 { font-size: 2em; } h2 { font-size: 1.8em; } h3 { font-size: 1.4em; }
p, li { font-size: 1em; }
.presentation-container h1 { font-size: 2em; } .presentation-container h2 { font-size: 1.8em; } .presentation-container h3 { font-size: 1.4em; }
.presentation-container p, .presentation-container li { font-size: 1em; }
.navigation { bottom: 15px; right: 15px; }
.nav-btn { padding: 10px 20px; font-size: 0.9em; }
.slide-number { bottom: 15px; left: 15px; padding: 8px 15px; font-size: 0.9em; }

View File

@@ -49,11 +49,11 @@
border: 1px solid rgba(255, 255, 255, 0.2);
}
h1 { color: #2563eb; font-size: 3em; margin-bottom: 20px; text-align: center; font-weight: 800; }
h2 { color: #1e40af; font-size: 2.5em; margin-bottom: 30px; text-align: center; border-bottom: 3px solid #2563eb; padding-bottom: 15px; font-weight: 700; }
h3 { color: #2563eb; font-size: 1.8em; margin: 25px 0 15px 0; font-weight: 700; }
p, li { font-size: 1.2em; line-height: 1.8; color: #1e293b; margin-bottom: 15px; }
ul { margin-left: 30px; }
.presentation-container h1 { color: #2563eb; font-size: 3em; margin-bottom: 20px; text-align: center; font-weight: 800; }
.presentation-container h2 { color: #1e40af; font-size: 2.5em; margin-bottom: 30px; text-align: center; border-bottom: 3px solid #2563eb; padding-bottom: 15px; font-weight: 700; }
.presentation-container h3 { color: #2563eb; font-size: 1.8em; margin: 25px 0 15px 0; font-weight: 700; }
.presentation-container p, .presentation-container li { font-size: 1.2em; line-height: 1.8; color: #1e293b; margin-bottom: 15px; }
.presentation-container ul { margin-left: 30px; }
.company-info { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin: 30px 0; }
.info-card {
@@ -155,10 +155,10 @@
@media (max-width: 768px) {
.slide-content { padding: 30px; }
h1 { font-size: 2em; }
h2 { font-size: 1.8em; }
h3 { font-size: 1.4em; }
p, li { font-size: 1em; }
.presentation-container h1 { font-size: 2em; }
.presentation-container h2 { font-size: 1.8em; }
.presentation-container h3 { font-size: 1.4em; }
.presentation-container p, .presentation-container li { font-size: 1em; }
.navigation { bottom: 15px; right: 15px; }
.nav-btn { padding: 10px 20px; font-size: 0.9em; }
.slide-number { bottom: 15px; left: 50%; transform: translateX(-50%); padding: 8px 15px; font-size: 0.9em; }

View File

@@ -555,7 +555,7 @@ function toggleSidebar() {
}
}
// 메뉴 그룹 토글
// 메뉴 그룹 토글 (R&D Labs, 개발 도구 등)
function toggleGroup(groupId) {
const sidebar = document.getElementById('sidebar');
// 사이드바가 접힌 상태면 그룹 토글 무시
@@ -577,6 +577,36 @@ function toggleGroup(groupId) {
}
}
// 동적 메뉴 그룹 토글 (DB에서 가져온 메뉴)
function toggleMenuGroup(groupId) {
const sidebar = document.getElementById('sidebar');
// 사이드바가 접힌 상태면 그룹 토글 무시
if (sidebar.classList.contains('sidebar-collapsed') || document.documentElement.classList.contains('sidebar-is-collapsed')) {
return;
}
const group = document.getElementById(groupId);
const icon = document.getElementById(groupId + '-icon');
if (!group) return;
const isHidden = group.style.display === 'none' || group.style.display === '';
if (isHidden) {
group.style.display = 'block';
if (icon) {
icon.classList.add('rotate-180');
}
localStorage.setItem('menu-group-' + groupId, 'visible');
} else {
group.style.display = 'none';
if (icon) {
icon.classList.remove('rotate-180');
}
localStorage.setItem('menu-group-' + groupId, 'hidden');
}
}
// ========== R&D Labs 사이드바 스크롤 함수 ==========
// 사이드바를 최하단으로 스크롤하고 위치 저장
@@ -812,19 +842,20 @@ function initSidebarTooltips() {
}
});
// 메뉴 그룹 상태 복원 (동적 메뉴)
// 메뉴 그룹 상태 복원 (동적 메뉴) - inline style로 통일
document.querySelectorAll('[id^="menu-group-"]').forEach(function(group) {
const groupId = group.id;
const savedState = localStorage.getItem('menu-group-' + groupId);
const icon = document.getElementById(groupId + '-icon');
if (savedState === 'hidden') {
group.classList.add('hidden');
group.style.display = 'none';
icon?.classList.remove('rotate-180');
} else if (savedState === 'visible') {
group.classList.remove('hidden');
group.style.display = 'block';
icon?.classList.add('rotate-180');
}
// savedState가 없으면 서버에서 렌더링한 초기 상태 유지
});
// R&D Labs 탭 상태 복원