Files
ntspi-app/resources/js/Pages/Dashboard/Components/SidebarNav.vue
T
F4ilji 63e456088d feat(dashboard): add Roles CRUD with permission-based menu filtering
- Roles CRUD: List/Create/Update/Delete actions, controller, requests
- Roles Vue pages: Index, Create, Edit (Options API, DashboardLayout)
- SidebarNav: filter menu items by user permissions via Inertia shared data
- HandleInertiaRequests: share permissions and roles arrays to frontend
- HttpKernel: register dashboard.permission middleware alias
- SidebarNavItem: add shield-check icon mapping
2026-08-03 10:39:15 +05:00

85 lines
2.4 KiB
Vue

<template>
<nav class="flex-1 overflow-y-auto px-3 py-4">
<div class="space-y-1">
<SidebarNavItem
v-for="item in visibleMenuItems"
:key="item.key"
:item="item"
:mobile="mobile"
:is-expanded="expandedKey === item.key"
@child-click="$emit('child-click')"
/>
</div>
<!-- Divider -->
<div class="my-4 border-t border-layer-line"></div>
<!-- Quick Actions Section -->
<div>
<h3 class="px-3 mb-2 text-xs font-semibold text-muted-foreground-1 uppercase tracking-wide">
Быстрые действия
</h3>
<div class="space-y-1">
<a
v-for="action in quickActions"
:key="action.label"
:href="action.external ? action.href : route(action.route)"
:target="action.external ? '_blank' : null"
:rel="action.external ? 'noopener noreferrer' : null"
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium text-foreground hover:bg-primary-50 hover:text-primary transition-all duration-200 group"
>
<component :is="iconMap[action.icon]" class="w-5 h-5 flex-shrink-0" />
<span>{{ action.label }}</span>
<DashboardIcon
v-if="action.external"
name="arrow-up-right"
size="4"
class="ml-auto text-muted-foreground-2 group-hover:text-primary"
/>
</a>
</div>
</div>
</nav>
</template>
<script setup>
import { computed } from 'vue';
import { usePage } from '@inertiajs/vue3';
import { menuItems } from './menuConfig';
import { quickActions } from './quickActionsConfig';
import SidebarNavItem from './SidebarNavItem.vue';
import DashboardIcon from './DashboardIcon.vue';
defineProps({
mobile: { type: Boolean, default: false },
});
defineEmits(['child-click']);
const page = usePage();
const userPermissions = computed(() => page.props.auth?.permissions ?? []);
const visibleMenuItems = computed(() => {
return menuItems.filter(item => {
if (!item.permission) return true;
return userPermissions.value.includes(item.permission);
});
});
const iconMap = {
cog: DashboardIcon,
};
const expandedKey = computed(() => {
const currentRoute = route().current();
for (const item of visibleMenuItems.value) {
if (item.activePrefixes && item.activePrefixes.some(prefix => currentRoute.startsWith(prefix))) {
return item.key;
}
}
return null;
});
</script>