Files
antrean-operasi/components/layout/full/vertical-sidebar/VerticalSidebar.vue
T
2026-02-23 10:09:13 +07:00

290 lines
6.9 KiB
Vue

<script setup lang="ts">
import { ref, shallowRef, onMounted, computed } from "vue";
import { useCustomizerStore } from "~/store/customizer";
import sidebarItems from "./sidebarItem";
import Logo from "../logo/Logo.vue";
import { Icon } from "@iconify/vue";
import { useRoute } from "vue-router";
// MiniSidebar Icons
import MiniSideIcons from "./MinIconItems";
import { useHakAkses } from "~/composables/useHakAkses";
const route = useRoute();
const { getAllowedPages } = useHakAkses();
const allowedPages = ref<string[]>([]);
const isLoadingAccess = ref(true);
// Fetch allowed pages on mount
onMounted(async () => {
try {
allowedPages.value = await getAllowedPages();
console.log('Allowed pages for user:', allowedPages.value);
} catch (error) {
console.error('Error loading allowed pages:', error);
} finally {
isLoadingAccess.value = false;
}
});
// Filter sidebar items based on user's allowed pages
const filteredSidebarItems = computed(() => {
if (isLoadingAccess.value) {
return [];
}
// If no allowed pages, return empty (no access)
if (allowedPages.value.length === 0) {
return [];
}
return sidebarItems.map(section => {
// Filter children based on allowed pages
const filteredChildren = section.children?.filter(child => {
if (child.to) {
return allowedPages.value.includes(child.to);
}
return false;
});
// Only include section if it has visible children
if (filteredChildren && filteredChildren.length > 0) {
return {
...section,
children: filteredChildren
};
}
return null;
}).filter(item => item !== null);
});
const findTitleByPath = (items: any, path: any) => {
let title = "";
for (const item of items) {
if (item.to === path) {
title = item.id;
break;
} else if (item.children) {
for (const child of item.children) {
if (child.to === path) {
title = item.id;
break;
} else if (child.children) {
for (const grandChild of child.children) {
if (grandChild.to === path) {
title = item.id;
break;
}
}
}
}
}
}
return title;
};
const foundId = findTitleByPath(sidebarItems, route.path);
const getCurrent = foundId ? foundId : 1;
const currentMenu = ref<any>(getCurrent);
function showData(data: any) {
currentMenu.value = data;
//customizer.SET_MINI_SIDEBAR(!customizer.mini_sidebar)
}
// MiniSidebar Icons End
const customizer = useCustomizerStore();
const sidebarMenu = shallowRef(sidebarItems);
// Toggle mini sidebar
const toggleMiniSidebar = () => {
customizer.SET_MINI_SIDEBAR(!customizer.mini_sidebar);
};
</script>
<template>
<!-- Minisidebar Icons -->
<!-- LeftSidebar Items -->
<v-navigation-drawer
v-model="customizer.Sidebar_drawer"
elevation="0"
rail-width="90"
app
top="0"
class="leftSidebar"
:rail="customizer.mini_sidebar"
:expand-on-hover="customizer.mini_sidebar"
width="240"
>
<!---Logo part -->
<div class="pa-4 pb-0" :class="{ 'logo-mini': customizer.mini_sidebar }" :style="customizer.mini_sidebar ? '' : ''">
<Logo />
</div>
<!-- ---------------------------------------------- -->
<!---Navigation -->
<!-- ---------------------------------------------- -->
<perfect-scrollbar class="scrollnavbar">
<div class="px-4 py-0 sidebar-menus">
<v-list class="py-1">
<template v-for="(item, i) in sidebarMenu">
<template v-if="currentMenu == item.id">
<!---Item Sub Header -->
<LazyLayoutFullVerticalSidebarNavGroup
:item="item"
v-if="item.header && !customizer.mini_sidebar"
:key="item.title"
/>
<!---If Has Child -->
<template v-for="sItem in item.children">
<LazyLayoutFullVerticalSidebarNavCollapse
class="leftPadding"
:item="sItem"
:level="0"
v-if="sItem.children"
/>
<LazyLayoutFullVerticalSidebarNavItem
:item="sItem"
class="leftPadding"
v-else
/>
</template>
</template>
</template>
</v-list>
</div>
</perfect-scrollbar>
<!-- Toggle Mini Sidebar Button -->
<!-- <div class="sidebar-toggle-wrapper">
<v-divider class="mb-0"></v-divider>
<v-btn
@click="toggleMiniSidebar"
variant="text"
block
class="sidebar-toggle-btn"
:ripple="false"
>
<v-tooltip location="right" v-if="customizer.mini_sidebar">
<template v-slot:activator="{ props }">
<v-icon v-bind="props" size="20">
mdi-lock-open-variant-outline
</v-icon>
</template>
<span>Expand Sidebar</span>
</v-tooltip>
<template v-else>
<v-icon size="20" class="mr-2">
mdi-lock-outline
</v-icon>
<span class="text-caption">Minimize</span>
</template>
</v-btn>
</div> -->
</v-navigation-drawer>
</template>
<style scoped>
.sidebar-toggle-wrapper {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: inherit;
z-index: 1;
}
.sidebar-toggle-btn {
height: 48px !important;
border-radius: 0 !important;
text-transform: none;
letter-spacing: normal;
font-weight: 500;
opacity: 0.7;
transition: all 0.2s ease;
}
.sidebar-toggle-btn:hover {
opacity: 1;
background-color: rgba(var(--v-theme-primary), 0.08);
}
/* Adjust scrollbar height to account for toggle button */
.scrollnavbar {
height: calc(100vh - 120px) !important;
}
/* Logo styling for mini mode */
.logo-mini :deep(img) {
transition: all 0.3s ease;
max-width: 500px;
margin-left: 20px;
height: auto;
}
/* Rail mode enhancements */
:deep(.v-navigation-drawer--rail) {
.sidebar-menus {
padding-left: 0 !important;
padding-right: 0 !important;
}
.v-list-item {
justify-content: center;
padding-left: 18px !important;
padding-right: 8px !important;
min-height: 48px;
margin: 4px 8px;
}
.v-list-item__prepend {
margin-right: 0 !important;
}
.v-list-item-title {
display: none;
}
.v-list-item__append {
display: none;
}
.v-list-item--active {
border-radius: 12px;
}
.v-list-item__overlay {
border-radius: 12px;
}
}
/* Show full menu on hover */
:deep(.v-navigation-drawer--rail.v-navigation-drawer--expand-on-hover:hover) {
.sidebar-menus {
padding-left: 16px !important;
padding-right: 16px !important;
}
.v-list-item {
justify-content: flex-start;
padding-left: 16px !important;
}
.v-list-item-title {
display: block;
}
.v-list-item__prepend {
margin-right: 16px !important;
}
.v-list-item__append {
display: flex;
}
}
</style>