216 lines
7.7 KiB
Vue
216 lines
7.7 KiB
Vue
<script setup lang="ts">
|
|
import {ref, computed, onMounted, watch} from 'vue';
|
|
import {Link, usePage, useForm} from '@inertiajs/inertia-vue3';
|
|
import type { Ref } from 'vue';
|
|
import type { MenuItem } from 'primevue/menuitem';
|
|
import {useGlobalStore} from '../../store/Global';
|
|
import HomeSvg from "../svg/home.svg"
|
|
import documentTextSvg from "../svg/documentText.svg"
|
|
import LogoutSvg from "../svg/logout.svg"
|
|
import CalendarSvg from "../svg/calendar.svg"
|
|
import StatusUpSvg from "../svg/statusUp.svg"
|
|
import PetSvg from "../svg/pet.svg"
|
|
|
|
interface SidebarItem extends MenuItem {
|
|
label: string;
|
|
icon?: any;
|
|
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: HomeSvg,
|
|
to: "/dashboard",
|
|
key: 'dashboard',
|
|
expanded: false,
|
|
permission: null,
|
|
},
|
|
{
|
|
label: "پرونده های باز",
|
|
icon: documentTextSvg,
|
|
to: "/dashboard/documents",
|
|
key: 'documents',
|
|
permission: null,
|
|
},
|
|
{
|
|
label: "کیس ها",
|
|
icon: PetSvg,
|
|
to: "/dashboard/cases",
|
|
key: 'cases',
|
|
permission: null,
|
|
},
|
|
{
|
|
label: "گزارشات",
|
|
icon: StatusUpSvg,
|
|
to: "/dashboard/reports",
|
|
key: 'reports',
|
|
permission: null,
|
|
},
|
|
{
|
|
label: "شیفت ها",
|
|
icon: CalendarSvg,
|
|
to: "/dashboard/shifts",
|
|
key: 'shifts',
|
|
permission: null,
|
|
},
|
|
// {
|
|
// label: "خروج از سامانه",
|
|
// icon: LogoutSvg,
|
|
// to: "/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 loadSvgContent = async (svgName) => {
|
|
try {
|
|
const svgModule = await import(`../../components/svg/${svgName}.svg?raw`)
|
|
return svgModule.default
|
|
} catch (error) {
|
|
return null
|
|
}
|
|
}
|
|
|
|
const updateExpandedKeys = () => {
|
|
const newKeys = {}
|
|
items.value.forEach(item => {
|
|
if (item.to === url.value || item.items?.some(c => url.value.startsWith(c.to || ''))) {
|
|
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="shadow-client-gray/5 w-[16rem] bg-white
|
|
py-4 pt-0 pb-8 fixed h-full top-0 z-30 inset-y-0 right-0 sm:inset-auto transition-transform duration-300"
|
|
:class="[globalStore.sidebarOpen ? 'translate-x-0' : 'translate-x-full sm:translate-x-0']">
|
|
<figure class="w-[16rem] h-[5rem] flex justify-center">
|
|
<img class="!w-full h-full object-contain" src="@/images/SidebarPanelLogo.png" alt="">
|
|
</figure>
|
|
<section class="">
|
|
<div class="grid grid-rows-[1fr_auto] h-[calc(100svh-6.5rem)]">
|
|
<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 === item.to ? 'active' : '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">
|
|
<component v-if="typeof item.icon === 'object'" :is="item.icon" class="flex w-7 items-center justify-start"></component>
|
|
<i v-else-if="item.icon" :class="item.icon" class="flex !text-[1.2rem] -mt-0.5"></i>
|
|
<div class="flex items-center font-semibold text-sm p-2 text-dominant-300 gap-2 w-full">
|
|
{{ item.label }}
|
|
</div>
|
|
</Link>
|
|
<div v-else class="flex items-center h-10 w-full">
|
|
<component v-if="typeof item.icon === 'object'" :is="item.icon" class="flex !text-[1.2rem] -mt-0.5"></component>
|
|
<i v-else-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>
|
|
<Link :href="route('dashboard.logout')" method="get" class="grid grid-cols-2a items-center h-10 justify-start p-4 px-6">
|
|
<LogoutSvg class="" />
|
|
<span class="flex items-center font-semibold text-sm p-2 text-dominant-300 gap-2 w-full">
|
|
خروج از سامانه
|
|
</span>
|
|
</Link>
|
|
</div>
|
|
</section>
|
|
</aside>
|
|
</template>
|