feat: Restructure sidebar nav into section-grouped menu
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
Replaces flat 25-item navItems array with 6 labeled sections: Dashboard, Server, Plugin Configs, Operations, Monitoring, Management. Section headers only render when at least one item is visible to the user's permissions. Platform Admin section restyled to match. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -44,34 +44,67 @@ const auth = useAuthStore()
|
||||
const server = useServerStore()
|
||||
const sidebarOpen = ref(false)
|
||||
|
||||
const navItems = [
|
||||
{ name: 'Dashboard', path: '/', icon: LayoutDashboard, permission: null },
|
||||
{ name: 'Server', path: '/server', icon: Server, permission: 'server.view' },
|
||||
{ name: 'Console', path: '/console', icon: Terminal, permission: 'console.view' },
|
||||
{ name: 'Players', path: '/players', icon: Users, permission: 'players.view' },
|
||||
{ name: 'Plugins', path: '/plugins', icon: Puzzle, permission: 'plugins.view' },
|
||||
{ name: 'File Manager', path: '/files', icon: FolderOpen, permission: 'files.view' },
|
||||
{ name: 'Loot Builder', path: '/loot-builder', icon: Crosshair, permission: 'loot.view' },
|
||||
{ name: 'Teleport Config', path: '/teleport-config', icon: Navigation2, permission: 'teleport.view' },
|
||||
{ name: 'Gather Rates', path: '/gather-manager', icon: Pickaxe, permission: 'gather.view' },
|
||||
{ name: 'Auto Doors', path: '/autodoors', icon: DoorOpen, permission: 'autodoors.view' },
|
||||
{ name: 'Kits', path: '/kits', icon: Gift, permission: 'kits.view' },
|
||||
{ name: 'Furnace Splitter', path: '/furnace-splitter', icon: Flame, permission: 'furnacesplitter.view' },
|
||||
{ name: 'Better Chat', path: '/better-chat', icon: MessageSquare, permission: 'betterchat.view' },
|
||||
{ name: 'Timed Execute', path: '/timed-execute', icon: Clock, permission: 'timedexecute.view' },
|
||||
{ name: 'Raidable Bases', path: '/raidable-bases', icon: Swords, permission: 'raidablebases.view' },
|
||||
{ name: 'Auto-Wiper', path: '/wipes', icon: RefreshCw, permission: 'wipes.view' },
|
||||
{ name: 'Maps', path: '/maps', icon: Map, permission: 'maps.view' },
|
||||
{ name: 'Chat Log', path: '/chat', icon: MessageSquare, permission: 'chat.view' },
|
||||
{ name: 'Analytics', path: '/analytics', icon: BarChart3, permission: 'analytics.view' },
|
||||
{ name: 'Schedules', path: '/schedules', icon: Clock, permission: 'schedules.view' },
|
||||
{ name: 'Alerts', path: '/alerts', icon: AlertTriangle, permission: 'alerts.view' },
|
||||
{ name: 'Notifications', path: '/notifications', icon: Bell, permission: 'notifications.view' },
|
||||
{ name: 'Team', path: '/team', icon: UserPlus, permission: null },
|
||||
{ name: 'Store', path: '/store/config', icon: ShoppingBag, permission: 'store.view' },
|
||||
{ name: 'Modules', path: '/modules', icon: Package, permission: 'modules.view' },
|
||||
{ name: 'Changelog', path: '/changelog', icon: FileText, permission: 'changelog.view' },
|
||||
{ name: 'Settings', path: '/settings', icon: Settings, permission: 'settings.view' },
|
||||
type NavItem = { name: string; path: string; icon: any; permission: string | null }
|
||||
type NavSection = { label: string; items: NavItem[] }
|
||||
|
||||
const navSections: NavSection[] = [
|
||||
{
|
||||
label: '',
|
||||
items: [
|
||||
{ name: 'Dashboard', path: '/', icon: LayoutDashboard, permission: null },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Server',
|
||||
items: [
|
||||
{ name: 'Server', path: '/server', icon: Server, permission: 'server.view' },
|
||||
{ name: 'Console', path: '/console', icon: Terminal, permission: 'console.view' },
|
||||
{ name: 'Players', path: '/players', icon: Users, permission: 'players.view' },
|
||||
{ name: 'Plugins', path: '/plugins', icon: Puzzle, permission: 'plugins.view' },
|
||||
{ name: 'File Manager', path: '/files', icon: FolderOpen, permission: 'files.view' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Plugin Configs',
|
||||
items: [
|
||||
{ name: 'Loot Builder', path: '/loot-builder', icon: Crosshair, permission: 'loot.view' },
|
||||
{ name: 'Teleport', path: '/teleport-config', icon: Navigation2, permission: 'teleport.view' },
|
||||
{ name: 'Gather Rates', path: '/gather-manager', icon: Pickaxe, permission: 'gather.view' },
|
||||
{ name: 'Auto Doors', path: '/autodoors', icon: DoorOpen, permission: 'autodoors.view' },
|
||||
{ name: 'Kits', path: '/kits', icon: Gift, permission: 'kits.view' },
|
||||
{ name: 'Furnace Splitter', path: '/furnace-splitter', icon: Flame, permission: 'furnacesplitter.view' },
|
||||
{ name: 'Better Chat', path: '/better-chat', icon: MessageSquare, permission: 'betterchat.view' },
|
||||
{ name: 'Timed Execute', path: '/timed-execute', icon: Clock, permission: 'timedexecute.view' },
|
||||
{ name: 'Raidable Bases', path: '/raidable-bases', icon: Swords, permission: 'raidablebases.view' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Operations',
|
||||
items: [
|
||||
{ name: 'Auto-Wiper', path: '/wipes', icon: RefreshCw, permission: 'wipes.view' },
|
||||
{ name: 'Maps', path: '/maps', icon: Map, permission: 'maps.view' },
|
||||
{ name: 'Schedules', path: '/schedules', icon: Clock, permission: 'schedules.view' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Monitoring',
|
||||
items: [
|
||||
{ name: 'Chat Log', path: '/chat', icon: MessageSquare, permission: 'chat.view' },
|
||||
{ name: 'Analytics', path: '/analytics', icon: BarChart3, permission: 'analytics.view' },
|
||||
{ name: 'Alerts', path: '/alerts', icon: AlertTriangle, permission: 'alerts.view' },
|
||||
{ name: 'Notifications', path: '/notifications', icon: Bell, permission: 'notifications.view' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Management',
|
||||
items: [
|
||||
{ name: 'Team', path: '/team', icon: UserPlus, permission: null },
|
||||
{ name: 'Store', path: '/store/config', icon: ShoppingBag, permission: 'store.view' },
|
||||
{ name: 'Modules', path: '/modules', icon: Package, permission: 'modules.view' },
|
||||
{ name: 'Changelog', path: '/changelog', icon: FileText, permission: 'changelog.view' },
|
||||
{ name: 'Settings', path: '/settings', icon: Settings, permission: 'settings.view' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const adminNavItems = [
|
||||
@@ -96,10 +129,14 @@ function closeSidebar() {
|
||||
sidebarOpen.value = false
|
||||
}
|
||||
|
||||
function canShowNavItem(item: typeof navItems[0]): boolean {
|
||||
function canShowNavItem(item: NavItem): boolean {
|
||||
if (!item.permission) return true
|
||||
return auth.hasPermission(item.permission)
|
||||
}
|
||||
|
||||
function hasVisibleItems(section: NavSection): boolean {
|
||||
return section.items.some(canShowNavItem)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -162,29 +199,35 @@ function canShowNavItem(item: typeof navItems[0]): boolean {
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="flex-1 overflow-y-auto py-2">
|
||||
<RouterLink
|
||||
v-for="item in navItems"
|
||||
v-show="canShowNavItem(item)"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
@click="closeSidebar"
|
||||
class="flex items-center gap-3 px-4 py-2 mx-2 rounded-lg text-sm transition-colors"
|
||||
:class="isActive(item.path)
|
||||
? 'bg-oxide-500/10 text-oxide-400'
|
||||
: 'text-neutral-400 hover:bg-neutral-800 hover:text-neutral-200'"
|
||||
>
|
||||
<component :is="item.icon" class="w-4 h-4" />
|
||||
{{ item.name }}
|
||||
</RouterLink>
|
||||
<template v-for="section in navSections" :key="section.label">
|
||||
<template v-if="hasVisibleItems(section)">
|
||||
<!-- Section Header -->
|
||||
<div v-if="section.label" class="mt-4 mb-1 px-4">
|
||||
<span class="text-[10px] font-semibold uppercase tracking-widest text-neutral-500">{{ section.label }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Section Items -->
|
||||
<RouterLink
|
||||
v-for="item in section.items"
|
||||
v-show="canShowNavItem(item)"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
@click="closeSidebar"
|
||||
class="flex items-center gap-3 px-4 py-2 mx-2 rounded-lg text-sm transition-colors"
|
||||
:class="isActive(item.path)
|
||||
? 'bg-oxide-500/10 text-oxide-400'
|
||||
: 'text-neutral-400 hover:bg-neutral-800 hover:text-neutral-200'"
|
||||
>
|
||||
<component :is="item.icon" class="w-4 h-4" />
|
||||
{{ item.name }}
|
||||
</RouterLink>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- Platform Admin Section (super-admin only) -->
|
||||
<template v-if="auth.isSuperAdmin">
|
||||
<div class="mt-4 mb-2 px-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex-1 border-t border-neutral-700" />
|
||||
<span class="text-[10px] font-semibold uppercase tracking-widest text-oxide-500">Platform</span>
|
||||
<div class="flex-1 border-t border-neutral-700" />
|
||||
</div>
|
||||
<div class="mt-4 mb-1 px-4">
|
||||
<span class="text-[10px] font-semibold uppercase tracking-widest text-oxide-500">Platform</span>
|
||||
</div>
|
||||
<RouterLink
|
||||
v-for="item in adminNavItems"
|
||||
|
||||
Reference in New Issue
Block a user