feat: 사이드바 토글 및 툴팁 기능 개선
- 햄버거 버튼을 사이드바 로고 영역으로 이동 - 접힌 상태에서 'S' 버튼 표시 (클릭 시 확장) - 접힌 상태 메뉴 아이콘에 JavaScript 기반 툴팁 추가 - 이벤트 위임 방식으로 페이지 전환 시에도 안정적 동작 - 브라우저 기본 title 툴팁과 충돌 방지 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,18 +6,26 @@
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>@yield('title', 'Dashboard') - {{ config('app.name') }}</title>
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
<!-- 사이드바 상태 즉시 적용 (깜빡임 방지) -->
|
||||
<script>
|
||||
(function() {
|
||||
if (localStorage.getItem('sidebar-collapsed') === 'true') {
|
||||
document.documentElement.classList.add('sidebar-is-collapsed');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
@stack('styles')
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<div class="flex h-screen overflow-hidden">
|
||||
<div class="flex h-screen overflow-hidden" id="app-container">
|
||||
<!-- Sidebar -->
|
||||
@include('partials.sidebar')
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<div class="flex-1 flex flex-col overflow-hidden min-w-0">
|
||||
<!-- Header -->
|
||||
@include('partials.header')
|
||||
|
||||
@@ -214,6 +222,45 @@ function showError(message) {
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 사이드바 토글 스크립트 -->
|
||||
<script>
|
||||
// 사이드바 상태 관리
|
||||
const SIDEBAR_STATE_KEY = 'sidebar-collapsed';
|
||||
|
||||
function toggleSidebar() {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const html = document.documentElement;
|
||||
const isCollapsed = sidebar.classList.contains('sidebar-collapsed') || html.classList.contains('sidebar-is-collapsed');
|
||||
|
||||
if (isCollapsed) {
|
||||
// 펼치기
|
||||
sidebar.classList.remove('sidebar-collapsed');
|
||||
html.classList.remove('sidebar-is-collapsed');
|
||||
localStorage.setItem(SIDEBAR_STATE_KEY, 'false');
|
||||
} else {
|
||||
// 접기
|
||||
sidebar.classList.add('sidebar-collapsed');
|
||||
html.classList.add('sidebar-is-collapsed');
|
||||
localStorage.setItem(SIDEBAR_STATE_KEY, 'true');
|
||||
}
|
||||
}
|
||||
|
||||
// 페이지 로드 시 저장된 사이드바 상태 복원 (클래스 동기화)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const html = document.documentElement;
|
||||
const savedState = localStorage.getItem(SIDEBAR_STATE_KEY);
|
||||
|
||||
if (savedState === 'true') {
|
||||
sidebar.classList.add('sidebar-collapsed');
|
||||
html.classList.add('sidebar-is-collapsed');
|
||||
} else {
|
||||
sidebar.classList.remove('sidebar-collapsed');
|
||||
html.classList.remove('sidebar-is-collapsed');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,30 +1,53 @@
|
||||
<!-- Sidebar -->
|
||||
<aside class="w-64 bg-white shadow-lg flex-shrink-0">
|
||||
<aside id="sidebar" class="sidebar bg-white shadow-lg flex-shrink-0 transition-all duration-300 ease-in-out w-64">
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Logo / Brand -->
|
||||
<div class="flex items-center justify-center h-16 border-b border-gray-200">
|
||||
<span class="text-xl font-bold text-gray-900">{{ config('app.name') }}</span>
|
||||
<div class="flex items-center h-16 border-b border-gray-200 px-3">
|
||||
<!-- 펼쳐진 상태: 햄버거 버튼 + 로고 -->
|
||||
<div class="sidebar-expanded-only flex items-center gap-2 w-full">
|
||||
<button
|
||||
type="button"
|
||||
onclick="toggleSidebar()"
|
||||
class="p-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
title="메뉴 접기"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
</button>
|
||||
<span class="text-xl font-bold text-gray-900">{{ config('app.name') }}</span>
|
||||
</div>
|
||||
<!-- 접힌 상태: S 버튼 (클릭하면 확장) -->
|
||||
<button
|
||||
type="button"
|
||||
onclick="toggleSidebar()"
|
||||
class="sidebar-collapsed-only hidden w-full p-2 text-xl font-bold text-gray-900 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
title="메뉴 펼치기"
|
||||
>
|
||||
S
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Menu -->
|
||||
<nav class="flex-1 overflow-y-auto p-4">
|
||||
<nav class="flex-1 overflow-y-auto p-4 sidebar-nav">
|
||||
<ul class="space-y-1">
|
||||
<!-- 대시보드 -->
|
||||
<li>
|
||||
<a href="{{ route('dashboard') }}"
|
||||
class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('dashboard') ? 'bg-primary text-white hover:bg-primary' : '' }}">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('dashboard') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
title="대시보드">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
<span class="font-medium">대시보드</span>
|
||||
<span class="font-medium sidebar-text">대시보드</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- 프로젝트 관리 그룹 -->
|
||||
<li class="pt-4 pb-1 border-t border-gray-200 mt-2">
|
||||
<button onclick="toggleGroup('pm-group')" class="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">
|
||||
<span>프로젝트 관리</span>
|
||||
<svg id="pm-group-icon" class="w-3 h-3 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<button onclick="toggleGroup('pm-group')" 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">
|
||||
<span class="sidebar-text">프로젝트 관리</span>
|
||||
<svg id="pm-group-icon" class="w-3 h-3 transition-transform sidebar-text" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -32,31 +55,34 @@ class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
<li>
|
||||
<a href="{{ route('pm.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('pm.index') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="프로젝트 대시보드">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
<span class="font-medium">프로젝트 대시보드</span>
|
||||
<span class="font-medium sidebar-text">프로젝트 대시보드</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('pm.projects.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('pm.projects.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="프로젝트">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
|
||||
</svg>
|
||||
<span class="font-medium">프로젝트</span>
|
||||
<span class="font-medium sidebar-text">프로젝트</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('daily-logs.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('daily-logs.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="일일 스크럼">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span class="font-medium">일일 스크럼</span>
|
||||
<span class="font-medium sidebar-text">일일 스크럼</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -64,9 +90,9 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
|
||||
<!-- 시스템 관리 그룹 -->
|
||||
<li class="pt-4 pb-1 border-t border-gray-200 mt-2">
|
||||
<button onclick="toggleGroup('system-group')" class="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">
|
||||
<span>시스템 관리</span>
|
||||
<svg id="system-group-icon" class="w-3 h-3 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<button onclick="toggleGroup('system-group')" 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">
|
||||
<span class="sidebar-text">시스템 관리</span>
|
||||
<svg id="system-group-icon" class="w-3 h-3 transition-transform sidebar-text" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -74,41 +100,45 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
<li>
|
||||
<a href="{{ route('tenants.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('tenants.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="테넌트 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||
</svg>
|
||||
<span class="font-medium">테넌트 관리</span>
|
||||
<span class="font-medium sidebar-text">테넌트 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('users.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('users.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="사용자 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||
</svg>
|
||||
<span class="font-medium">사용자 관리</span>
|
||||
<span class="font-medium sidebar-text">사용자 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('departments.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('departments.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="부서 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||
</svg>
|
||||
<span class="font-medium">부서 관리</span>
|
||||
<span class="font-medium sidebar-text">부서 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('menus.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('menus.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="메뉴 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
<span class="font-medium">메뉴 관리</span>
|
||||
<span class="font-medium sidebar-text">메뉴 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -116,9 +146,9 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
|
||||
<!-- 권한 관리 그룹 -->
|
||||
<li class="pt-4 pb-1 border-t border-gray-200 mt-2">
|
||||
<button onclick="toggleGroup('permission-group')" class="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">
|
||||
<span>권한 관리</span>
|
||||
<svg id="permission-group-icon" class="w-3 h-3 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<button onclick="toggleGroup('permission-group')" 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">
|
||||
<span class="sidebar-text">권한 관리</span>
|
||||
<svg id="permission-group-icon" class="w-3 h-3 transition-transform sidebar-text" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -126,61 +156,67 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
<li>
|
||||
<a href="{{ route('roles.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('roles.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="역할 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
<span class="font-medium">역할 관리</span>
|
||||
<span class="font-medium sidebar-text">역할 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('permissions.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('permissions.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="권한 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
<span class="font-medium">권한 관리</span>
|
||||
<span class="font-medium sidebar-text">권한 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('role-permissions.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('role-permissions.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="역할 권한 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
<span class="font-medium">역할 권한 관리</span>
|
||||
<span class="font-medium sidebar-text">역할 권한 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('department-permissions.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('department-permissions.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="부서 권한 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||
</svg>
|
||||
<span class="font-medium">부서 권한 관리</span>
|
||||
<span class="font-medium sidebar-text">부서 권한 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('user-permissions.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('user-permissions.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="개인 권한 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
</svg>
|
||||
<span class="font-medium">개인 권한 관리</span>
|
||||
<span class="font-medium sidebar-text">개인 권한 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('permission-analyze.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('permission-analyze.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="권한 분석">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
<span class="font-medium">권한 분석</span>
|
||||
<span class="font-medium sidebar-text">권한 분석</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -188,9 +224,9 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
|
||||
<!-- 생산 관리 그룹 -->
|
||||
<li class="pt-4 pb-1 border-t border-gray-200 mt-2">
|
||||
<button onclick="toggleGroup('production-group')" class="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">
|
||||
<span>생산 관리</span>
|
||||
<svg id="production-group-icon" class="w-3 h-3 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<button onclick="toggleGroup('production-group')" 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">
|
||||
<span class="sidebar-text">생산 관리</span>
|
||||
<svg id="production-group-icon" class="w-3 h-3 transition-transform sidebar-text" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -198,61 +234,67 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
<li>
|
||||
<a href="{{ route('item-fields.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('item-fields.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="품목기준 필드 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
|
||||
</svg>
|
||||
<span class="font-medium">품목기준 필드 관리</span>
|
||||
<span class="font-medium sidebar-text">품목기준 필드 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('quote-formulas.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('quote-formulas.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="견적수식 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span class="font-medium">견적수식 관리</span>
|
||||
<span class="font-medium sidebar-text">견적수식 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="제품 관리 (준비중)">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
||||
</svg>
|
||||
<span class="font-medium">제품 관리</span>
|
||||
<span class="ml-auto text-xs text-gray-300">준비중</span>
|
||||
<span class="font-medium sidebar-text">제품 관리</span>
|
||||
<span class="ml-auto text-xs text-gray-300 sidebar-text">준비중</span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="자재 관리 (준비중)">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4" />
|
||||
</svg>
|
||||
<span class="font-medium">자재 관리</span>
|
||||
<span class="ml-auto text-xs text-gray-300">준비중</span>
|
||||
<span class="font-medium sidebar-text">자재 관리</span>
|
||||
<span class="ml-auto text-xs text-gray-300 sidebar-text">준비중</span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="BOM 관리 (준비중)">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" />
|
||||
</svg>
|
||||
<span class="font-medium">BOM 관리</span>
|
||||
<span class="ml-auto text-xs text-gray-300">준비중</span>
|
||||
<span class="font-medium sidebar-text">BOM 관리</span>
|
||||
<span class="ml-auto text-xs text-gray-300 sidebar-text">준비중</span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="카테고리 관리 (준비중)">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
|
||||
</svg>
|
||||
<span class="font-medium">카테고리 관리</span>
|
||||
<span class="ml-auto text-xs text-gray-300">준비중</span>
|
||||
<span class="font-medium sidebar-text">카테고리 관리</span>
|
||||
<span class="ml-auto text-xs text-gray-300 sidebar-text">준비중</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -260,9 +302,9 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
|
||||
<!-- 콘텐츠 관리 그룹 -->
|
||||
<li class="pt-4 pb-1 border-t border-gray-200 mt-2">
|
||||
<button onclick="toggleGroup('content-group')" class="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">
|
||||
<span>콘텐츠 관리</span>
|
||||
<svg id="content-group-icon" class="w-3 h-3 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<button onclick="toggleGroup('content-group')" 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">
|
||||
<span class="sidebar-text">콘텐츠 관리</span>
|
||||
<svg id="content-group-icon" class="w-3 h-3 transition-transform sidebar-text" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -270,11 +312,12 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
<li>
|
||||
<a href="{{ route('boards.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('boards.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="게시판 관리">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" />
|
||||
</svg>
|
||||
<span class="font-medium">게시판 관리</span>
|
||||
<span class="font-medium sidebar-text">게시판 관리</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -282,42 +325,45 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
|
||||
<!-- 시스템 그룹 -->
|
||||
<li class="pt-4 pb-1 border-t border-gray-200 mt-2">
|
||||
<button onclick="toggleGroup('system-settings-group')" class="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">
|
||||
<span>시스템</span>
|
||||
<svg id="system-settings-group-icon" class="w-3 h-3 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<button onclick="toggleGroup('system-settings-group')" 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">
|
||||
<span class="sidebar-text">시스템</span>
|
||||
<svg id="system-settings-group-icon" class="w-3 h-3 transition-transform sidebar-text" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<ul id="system-settings-group" class="space-y-1 mt-1">
|
||||
<li>
|
||||
<span class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="시스템 설정 (준비중)">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
<span class="font-medium">시스템 설정</span>
|
||||
<span class="ml-auto text-xs text-gray-300">준비중</span>
|
||||
<span class="font-medium sidebar-text">시스템 설정</span>
|
||||
<span class="ml-auto text-xs text-gray-300 sidebar-text">준비중</span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="감사 로그 (준비중)">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<span class="font-medium">감사 로그</span>
|
||||
<span class="ml-auto text-xs text-gray-300">준비중</span>
|
||||
<span class="font-medium sidebar-text">감사 로그</span>
|
||||
<span class="ml-auto text-xs text-gray-300 sidebar-text">준비중</span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('archived-records.index') }}"
|
||||
class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('archived-records.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
style="padding-left: 2rem;">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
style="padding-left: 2rem;"
|
||||
title="삭제된 데이터 백업">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
|
||||
</svg>
|
||||
<span class="font-medium">삭제된 데이터 백업</span>
|
||||
<span class="font-medium sidebar-text">삭제된 데이터 백업</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -328,38 +374,39 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
|
||||
<!-- 개발 도구 (하단 고정) -->
|
||||
<div class="border-t border-gray-200 p-2 bg-gray-50">
|
||||
<button onclick="toggleGroup('dev-tools-group')" class="w-full flex items-center justify-between px-3 py-2 text-xs font-bold text-gray-600 uppercase tracking-wider hover:bg-gray-100 rounded">
|
||||
<button onclick="toggleGroup('dev-tools-group')" 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-100 rounded">
|
||||
<span class="flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-4 h-4 text-orange-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
개발 도구
|
||||
<span class="sidebar-text">개발 도구</span>
|
||||
</span>
|
||||
<svg id="dev-tools-group-icon" class="w-3 h-3 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg id="dev-tools-group-icon" class="w-3 h-3 transition-transform sidebar-text" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<ul id="dev-tools-group" class="space-y-1 mt-1">
|
||||
<li>
|
||||
<a href="{{ route('dev-tools.flow-tester.index') }}"
|
||||
class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('dev-tools.flow-tester.*') ? 'bg-primary text-white hover:bg-primary' : '' }}">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-700 hover:bg-gray-100 {{ request()->routeIs('dev-tools.flow-tester.*') ? 'bg-primary text-white hover:bg-primary' : '' }}"
|
||||
title="API 플로우 테스터">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span class="font-medium">API 플로우 테스터</span>
|
||||
<span class="font-medium sidebar-text">API 플로우 테스터</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- User Info (하단) -->
|
||||
<div class="p-4 border-t border-gray-200">
|
||||
<div class="p-4 border-t border-gray-200 sidebar-user-info">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-primary text-white flex items-center justify-center font-bold">
|
||||
<div class="w-10 h-10 rounded-full bg-primary text-white flex items-center justify-center font-bold flex-shrink-0">
|
||||
{{ mb_strtoupper(mb_substr(auth()->user()->name ?? 'U', 0, 1)) }}
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex-1 min-w-0 sidebar-text">
|
||||
<p class="text-sm font-medium text-gray-900 truncate">
|
||||
{{ auth()->user()->name ?? 'User' }}
|
||||
</p>
|
||||
@@ -372,8 +419,181 @@ class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-700 hover:
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<style>
|
||||
/* 사이드바 토글 스타일 */
|
||||
.sidebar {
|
||||
width: 16rem; /* w-64 = 256px */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* 접힌 상태에서 overflow 설정 (툴팁 표시를 위해) */
|
||||
html.sidebar-is-collapsed #sidebar,
|
||||
.sidebar.sidebar-collapsed {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav {
|
||||
overflow-y: auto !important;
|
||||
overflow-x: visible !important;
|
||||
}
|
||||
|
||||
/* 사이드바 내부 컨테이너도 overflow visible 설정 */
|
||||
html.sidebar-is-collapsed #sidebar > div,
|
||||
.sidebar.sidebar-collapsed > div {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
/* 접힌 상태에서 레이아웃 컨테이너 overflow 해제 (툴팁 표시를 위해) */
|
||||
html.sidebar-is-collapsed #app-container {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
/* 접힌 상태 - html 클래스 기반 (초기 로드 깜빡임 방지) */
|
||||
html.sidebar-is-collapsed #sidebar,
|
||||
.sidebar.sidebar-collapsed {
|
||||
width: 4rem; /* w-16 = 64px */
|
||||
}
|
||||
|
||||
/* 접힌 상태에서 텍스트 숨김 */
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-text,
|
||||
.sidebar.sidebar-collapsed .sidebar-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 접힌 상태에서 그룹 헤더 숨김 */
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-group-header,
|
||||
.sidebar.sidebar-collapsed .sidebar-group-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 접힌 상태에서 네비게이션 패딩 조정 */
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
/* 접힌 상태에서 메뉴 아이템 중앙 정렬 */
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav a,
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav span,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav a,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav span {
|
||||
justify-content: center;
|
||||
padding-left: 0.75rem !important;
|
||||
padding-right: 0.75rem !important;
|
||||
}
|
||||
|
||||
/* 접힌 상태에서 사용자 정보 숨김 (아바타만 표시) */
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-user-info,
|
||||
.sidebar.sidebar-collapsed .sidebar-user-info {
|
||||
padding: 0.5rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-user-info > div,
|
||||
.sidebar.sidebar-collapsed .sidebar-user-info > div {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 접힌 상태 로고 토글 */
|
||||
.sidebar .sidebar-collapsed-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-expanded-only,
|
||||
.sidebar.sidebar-collapsed .sidebar-expanded-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-collapsed-only,
|
||||
.sidebar.sidebar-collapsed .sidebar-collapsed-only {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 접힌 상태에서 개발 도구 영역 조정 */
|
||||
html.sidebar-is-collapsed #sidebar .border-t.border-gray-200.p-2,
|
||||
.sidebar.sidebar-collapsed .border-t.border-gray-200.p-2 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
/* 접힌 상태에서 메뉴 아이콘 호버 툴팁 */
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav a,
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav > ul > li > ul > li > a,
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav > ul > li > ul > li > span,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav a,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav > ul > li > ul > li > a,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav > ul > li > ul > li > span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav a::after,
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav > ul > li > ul > li > span::after,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav a::after,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav > ul > li > ul > li > span::after {
|
||||
content: attr(title);
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
margin-left: 0.75rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background-color: #1f2937;
|
||||
color: white;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
border-radius: 0.375rem;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.15s ease-in-out, visibility 0.15s ease-in-out;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav a:hover::after,
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav > ul > li > ul > li > span:hover::after,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav a:hover::after,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav > ul > li > ul > li > span:hover::after {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* 툴팁 화살표 */
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav a::before,
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav > ul > li > ul > li > span::before,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav a::before,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav > ul > li > ul > li > span::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
margin-left: 0.25rem;
|
||||
border: 6px solid transparent;
|
||||
border-right-color: #1f2937;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.15s ease-in-out, visibility 0.15s ease-in-out;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav a:hover::before,
|
||||
html.sidebar-is-collapsed #sidebar .sidebar-nav > ul > li > ul > li > span:hover::before,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav a:hover::before,
|
||||
.sidebar.sidebar-collapsed .sidebar-nav > ul > li > ul > li > span:hover::before {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function toggleGroup(groupId) {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
// 사이드바가 접힌 상태면 그룹 토글 무시
|
||||
if (sidebar.classList.contains('sidebar-collapsed')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const group = document.getElementById(groupId);
|
||||
const icon = document.getElementById(groupId + '-icon');
|
||||
|
||||
@@ -399,5 +619,87 @@ function toggleGroup(groupId) {
|
||||
icon.style.transform = 'rotate(-90deg)';
|
||||
}
|
||||
});
|
||||
|
||||
// 사이드바 접힌 상태에서 메뉴 아이템 호버 시 툴팁 표시
|
||||
initSidebarTooltips();
|
||||
});
|
||||
</script>
|
||||
|
||||
// JavaScript 기반 툴팁 (overflow 제약 우회) - 이벤트 위임 방식
|
||||
function initSidebarTooltips() {
|
||||
// 이미 초기화되었으면 스킵
|
||||
if (document.getElementById('sidebar-tooltip')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 툴팁 엘리먼트 생성
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.id = 'sidebar-tooltip';
|
||||
tooltip.className = 'fixed bg-gray-800 text-white text-sm font-medium px-3 py-2 rounded-lg shadow-lg z-[9999] pointer-events-none opacity-0 transition-opacity duration-150 whitespace-nowrap';
|
||||
tooltip.style.cssText = 'display: none;';
|
||||
document.body.appendChild(tooltip);
|
||||
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
let currentTarget = null;
|
||||
|
||||
// 이벤트 위임: sidebar에서 mouseover/mouseout 처리
|
||||
sidebar.addEventListener('mouseover', function(e) {
|
||||
// 사이드바가 펼쳐진 상태면 무시
|
||||
if (!sidebar.classList.contains('sidebar-collapsed') && !document.documentElement.classList.contains('sidebar-is-collapsed')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 가장 가까운 a 또는 span[title] 찾기
|
||||
const target = e.target.closest('.sidebar-nav a[title], .sidebar-nav span[title], #dev-tools-group a[title]');
|
||||
if (!target || target === currentTarget) return;
|
||||
|
||||
currentTarget = target;
|
||||
const title = target.getAttribute('title');
|
||||
if (!title) return;
|
||||
|
||||
// data-tooltip에 저장하고 title 제거 (브라우저 기본 툴팁 방지)
|
||||
if (!target.hasAttribute('data-tooltip')) {
|
||||
target.setAttribute('data-tooltip', title);
|
||||
}
|
||||
target.removeAttribute('title');
|
||||
|
||||
// 툴팁 표시
|
||||
tooltip.textContent = title;
|
||||
tooltip.style.display = 'block';
|
||||
|
||||
// 위치 계산
|
||||
const rect = target.getBoundingClientRect();
|
||||
tooltip.style.left = (rect.right + 12) + 'px';
|
||||
tooltip.style.top = (rect.top + (rect.height / 2) - (tooltip.offsetHeight / 2)) + 'px';
|
||||
|
||||
// 페이드 인
|
||||
requestAnimationFrame(() => {
|
||||
tooltip.style.opacity = '1';
|
||||
});
|
||||
});
|
||||
|
||||
sidebar.addEventListener('mouseout', function(e) {
|
||||
const target = e.target.closest('.sidebar-nav a, .sidebar-nav span, #dev-tools-group a');
|
||||
if (!target) return;
|
||||
|
||||
// relatedTarget이 같은 요소 내부면 무시
|
||||
const relatedTarget = e.relatedTarget;
|
||||
if (relatedTarget && target.contains(relatedTarget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// title 속성 복원
|
||||
const originalTitle = target.getAttribute('data-tooltip');
|
||||
if (originalTitle) {
|
||||
target.setAttribute('title', originalTitle);
|
||||
}
|
||||
|
||||
currentTarget = null;
|
||||
tooltip.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
if (tooltip.style.opacity === '0') {
|
||||
tooltip.style.display = 'none';
|
||||
}
|
||||
}, 150);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user