250 lines
9.6 KiB
Vue
250 lines
9.6 KiB
Vue
<script setup lang="ts">
|
|
import {ref, computed, onMounted, watch} from 'vue';
|
|
import {Link, usePage} from '@inertiajs/inertia-vue3';
|
|
import type { Ref } from 'vue';
|
|
import type { MenuItem } from 'primevue/menuitem';
|
|
import {useGlobalStore} from '../store/Global';
|
|
|
|
interface SidebarItem extends MenuItem {
|
|
label: string;
|
|
icon?: string;
|
|
to?: string;
|
|
expanded?: boolean;
|
|
items?: SidebarItem[];
|
|
permission?: string | null;
|
|
}
|
|
|
|
const globalStore = useGlobalStore();
|
|
const {url ,props} = usePage();
|
|
const currentUser = ref(props.value.auth.User);
|
|
|
|
const userPermissions = computed(() => props.value.auth.permissions ?? []);
|
|
|
|
function hasPermission(permission) {
|
|
if (!permission) return true;
|
|
return userPermissions.value.includes(permission);
|
|
}
|
|
|
|
function filterSidebarItems(items) {
|
|
return items
|
|
.filter(item => hasPermission(item.permission))
|
|
.map(item => {
|
|
if (item.items) {
|
|
const filteredChildren = filterSidebarItems(item.items);
|
|
if (filteredChildren.length > 0) {
|
|
return { ...item, items: filteredChildren };
|
|
} else if (!item.permission) {
|
|
return null;
|
|
}
|
|
}
|
|
return item;
|
|
})
|
|
.filter(Boolean);
|
|
}
|
|
|
|
const filteredItems = computed(() => filterSidebarItems(items.value));
|
|
|
|
// @ts-ignore
|
|
const items: Ref<SidebarItem[]> = ref([
|
|
{
|
|
label: "داشبورد",
|
|
icon: "fi fi-rr-dashboard",
|
|
to: "/admin/dashboard",
|
|
key: 'panel',
|
|
expanded: false,
|
|
permission: null,
|
|
},
|
|
{
|
|
label: "همه کیس ها",
|
|
icon: "fi fi-rr-document",
|
|
to: "",
|
|
key: 'pets',
|
|
permission: null,
|
|
expanded: false,
|
|
items: [
|
|
{ label: "همه کیس ها", icon: "fi fi-rr-wifi-1", to: "/admin/pets" , permission: "pets.index" },
|
|
{ label: "افزودن کیس جدید", icon: "fi fi-rr-wifi-1", to: "/admin/pets/create" , permission: "pets.create" },
|
|
],
|
|
},
|
|
{
|
|
label: "همه پرونده ها",
|
|
icon: "fi fi-rr-document",
|
|
to: "",
|
|
key: 'documents',
|
|
permission: null,
|
|
expanded: false,
|
|
items: [
|
|
{ label: "همه پرونده ها", icon: "fi fi-rr-wifi-1", to: "/admin/documents" , permission: "documents.index" },
|
|
{ label: "افزودن پرونده جدید", icon: "fi fi-rr-wifi-1", to: "/admin/documents/create" , permission: "documents.create" },
|
|
],
|
|
},
|
|
{
|
|
label: "همه گزارش ها",
|
|
icon: "fi fi-rr-document",
|
|
to: "/admin/reports",
|
|
key: 'reports',
|
|
permission: "reports.index",
|
|
expanded: false,
|
|
},
|
|
{
|
|
label: "مدیریت رسانه",
|
|
icon: "fi fi-rr-gallery",
|
|
to: "/admin/medium",
|
|
key: 'medium',
|
|
permission: "medium.index",
|
|
},
|
|
{
|
|
label: "مدیریت دسترسی ها",
|
|
icon: "fi fi-rr-user-permissions",
|
|
key: 'roles',
|
|
expanded: false,
|
|
permission: null,
|
|
items: [
|
|
{ label: " مدیریت مقام ها", icon: "fi fi-rr-wifi-1", to: "/admin/roles", permission: "roles.index" },
|
|
{ label: "مدیریت سطح ها", icon: "fi fi-rr-wifi-1", to: "/admin/permissions", permission: "permissions.index" },
|
|
],
|
|
},
|
|
{
|
|
label: "کاربران",
|
|
icon: "fi fi-rr-user",
|
|
key: 'users',
|
|
expanded: false,
|
|
permission: null,
|
|
items: [
|
|
{ label: "همه کاربران", icon: "fi fi-rr-wifi-1", to: "/admin/users", permission: "users.index" },
|
|
{ label: "افزودن کاربر جدید", icon: "fi fi-rr-wifi-1", to: "/admin/users/create", permission: "users.create" },
|
|
],
|
|
},
|
|
{
|
|
label: "تنظیمات",
|
|
icon: "fi fi-rr-settings",
|
|
key: 'settings',
|
|
expanded: false,
|
|
permission: null,
|
|
items: [
|
|
{ label: "اطلاعات کاربری",icon: "fi fi-rr-wifi-1", to: "/admin/setting-profile", permission: null },
|
|
{ label: "تنظیمات امنیتی", icon: "fi fi-rr-wifi-1", to: "/admin/setting-profile/security", permission: null },
|
|
{ label: "دستگاه های فعال", icon: "fi fi-rr-wifi-1", to: "/admin/setting-profile/sessions", permission: null },
|
|
],
|
|
},
|
|
{
|
|
label: "خروج از حساب",
|
|
icon: "fi fi-rr-sign-out-alt",
|
|
to: "/admin/auth/logout",
|
|
permission: null,
|
|
},
|
|
]);
|
|
const toggleSubmenu = (item: SidebarItem) => {
|
|
// expandedKeys.value = {};
|
|
// item.expanded = !item.expanded;
|
|
};
|
|
|
|
const closeSidebarOnClick = () => {
|
|
globalStore.sidebarOpen = false;
|
|
};
|
|
|
|
const expandedKeys = ref<Record<string, boolean>>({});
|
|
|
|
|
|
onMounted(() => {
|
|
globalStore.current_user = currentUser;
|
|
const saved = localStorage.getItem('expandedKeys')
|
|
if (saved) expandedKeys.value = JSON.parse(saved)
|
|
updateExpandedKeys()
|
|
|
|
document.addEventListener('click', (e) => {
|
|
if (e.target.closest('.p-panelmenu-content a')) {
|
|
globalStore.sidebarOpen = false;
|
|
}
|
|
});
|
|
});
|
|
|
|
watch(expandedKeys, v => {
|
|
localStorage.setItem('expandedKeys', JSON.stringify(v))
|
|
})
|
|
watch(() => url.value, () => {
|
|
updateExpandedKeys()
|
|
})
|
|
|
|
const isActive = (item: SidebarItem): boolean => {
|
|
return expandedKeys.value[item.key] === true;
|
|
};
|
|
|
|
const updateExpandedKeys = () => {
|
|
const newKeys = {}
|
|
const currentPath = url.value.split('?')[0];
|
|
|
|
items.value.forEach(item => {
|
|
if (item.to === currentPath || item.items?.some(c => {
|
|
const itemPath = c.to || '';
|
|
return currentPath.startsWith(itemPath) && itemPath !== '';
|
|
})) {
|
|
newKeys[item.key] = true
|
|
}
|
|
})
|
|
expandedKeys.value = newKeys
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="globalStore.sidebarOpen"
|
|
class="fixed inset-0 bg-black/10 backdrop-blur-sm bg-opacity-50 z-10 sm:hidden"
|
|
@click="globalStore.sidebarOpen = false">
|
|
</div>
|
|
<aside class="!bg-white shadow-all shadow-client-gray/5 w-[16rem]
|
|
py-4 z-10 pb-8 fixed sm:relative inset-y-0 right-0 sm:inset-auto transition-transform duration-300 z-20"
|
|
:class="[globalStore.sidebarOpen ? 'translate-x-0' : 'translate-x-full sm:translate-x-0']">
|
|
<section class="sticky top-10 right-0 ">
|
|
<div class="grid grid-cols-a border-b-[1px] border-client-gray/5 pb-6 justify-center gap-2 items-center">
|
|
<figure class="grid grid-cols-a justify-center">
|
|
<Avatar v-if="globalStore.current_user?.profile_url" :image="globalStore.current_user?.profile_url" size="xlarge" shape="circle" />
|
|
<Avatar v-else image="/images/profile_default.png" size="xlarge" shape="circle" />
|
|
</figure>
|
|
<div class="text-xl grid grid-cols-a text-center text-client-Fontcolor font-semibold">
|
|
<span class="text-lg font-extrabold mt-2">{{ (globalStore.current_user.first_name ?? '' ) + ' ' + (globalStore.current_user.last_name ?? '')}} </span>
|
|
<span class="text-xs mt-1">{{ globalStore.current_user.email }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="overflow-y-auto h-full">
|
|
<PanelMenu
|
|
:pt="{
|
|
header: 'custom-header',
|
|
headerAction: 'custom-header-action',
|
|
menuContent: 'custom-menu-content',
|
|
menuitem: 'custom-menuitem',
|
|
action: 'custom-action',
|
|
icon: 'custom-icon',
|
|
submenuIcon: 'custom-submenu-icon !text-xl',
|
|
}"
|
|
v-model:expanded-keys="expandedKeys"
|
|
:model="filteredItems"
|
|
class="custom-menu px-4">
|
|
<template #item="{ item, root, hasSubmenu, toggle }">
|
|
<div
|
|
:class="(url.split('?')[0]) === item.to ? 'bg-client-primary !text-white font-bold' : 'text-client-Fontcolor'"
|
|
class="flex items-center h-10 justify-between pl-2 text-client-Fontcolor rounded-lg cursor-pointer w-full"
|
|
@click="hasSubmenu ? toggleSubmenu(item) : null">
|
|
<div class="px-2 flex w-full items-center">
|
|
<Link v-if="item.to" :href="item.to" class="flex items-center h-10 w-full" @click="globalStore.sidebarOpen = false">
|
|
<i v-if="item.icon" :class="item.icon" class="flex !text-[1.2rem] -mt-0.5"></i>
|
|
<div class="flex items-center font-normal text-sm p-2 gap-2 w-full">
|
|
{{ item.label }}
|
|
</div>
|
|
</Link>
|
|
<div v-else class="flex items-center h-10 w-full">
|
|
<i v-if="item.icon" :class="item.icon" class="flex !text-[1.2rem] -mt-0.5"></i>
|
|
<span class="w-full p-2 text-sm text-client-Fontcolor">{{ item.label }}</span>
|
|
</div>
|
|
</div>
|
|
<i v-if="hasSubmenu"
|
|
class="fi fi-rr-angle-small-left !text-sm flex transition-transform duration-300"
|
|
:class="[{ 'rotate-90': item.expanded || isActive }]">
|
|
</i>
|
|
</div>
|
|
</template>
|
|
</PanelMenu>
|
|
</div>
|
|
</section>
|
|
</aside>
|
|
</template>
|