Added Permission Check Function

This commit is contained in:
Sandip Ghimire 2025-12-17 15:22:23 +05:45
parent 660f994036
commit f0185dc7d5
10 changed files with 192 additions and 148 deletions

1
.gitignore vendored
View File

@ -34,3 +34,4 @@ coverage
# Vitest
__screenshots__/
config.json

View File

@ -1,9 +1,9 @@
{
"ENVIRONMENT": "PRODUCTION || DEVELOPMENT",
"PAGE_TITLE": "Dolphin",
"PAGE_TITLE_LOGO": "http://localhost:3000/img/Dolphin/dolphinfav.ico",
"PAGE_TITLE_LOGO": "/img/Dolphin/dolphinfav.ico",
"CLIENT_NAME": "Mavorion Systems",
"CLIENT_LOGO": "",
"CLIENT_LOCATION": "LAZIMPAT",
"API_BASE_URL": "http://localhost:8000/api/"
"CLIENT_LOGO": "",
"API_BASE_URL": "http://localhost:8000/api/v1/"
}

View File

@ -0,0 +1,124 @@
import type { SidebarItem } from "../types/app/sidebar.type";
const sidebarItems = (): SidebarItem[] => {
return [
{
label: "Dashboard",
},
{
label: "Dashboard",
icon: "Home",
to: "/",
},
{
label: "Stock Verification",
icon: "Blocks",
children: [
{
label: "Opening Stock",
to: "/stock-verification/opening-stock/",
},
{
label: "Closing Stock",
to: "/stock-verification/closing-stock/",
},
],
},
{
label: "Components",
},
{
label: "Content Layout",
icon: "Package",
to: "/component/content-layout",
},
{
label: "Modal",
icon: "MessageSquareCode",
to: "/component/modal",
},
{
label: "Switch",
icon: "ToggleLeft",
to: "/component/switch",
},
{
label: "Toggle",
icon: "ToggleRight",
to: "/component/toggle",
},
{
label: "Tabulator",
icon: "Table",
to: "/component/tabulator",
},
{
label: "Towser",
icon: "Blinds",
to: "/component/towser",
},
{
label: "Date Range",
icon: "CalendarRange",
to: "/component/date-range",
},
{
label: "Date Selector",
icon: "Calendar",
to: "/component/date-selector",
},
{
label: "Wizard Stepper",
icon: "WandSparkles",
to: "/component/wizard",
},
{
label: "Loader",
icon: "Loader",
to: "/component/loader",
},
{
label: "Directives",
},
{
label: "Input Error",
icon: "TriangleAlert",
to: "/directives/input-error",
},
{
label: "Input Currency",
icon: "CircleDollarSign",
to: "/directives/input-currency",
},
{
label: "Input Select",
icon: "TextCursorInput",
to: "/directives/input-select",
},
{
label: "Tooltip",
icon: "BookOpenText",
to: "/directives/tooltip",
},
{
label: "CSS",
},
{
label: "Buttons",
icon: "SquareArrowLeft",
to: "/css/button",
},
{
label: "Input",
icon: "TextCursor",
to: "/css/input",
},
{
label: "Font",
icon: "CaseSensitive",
to: "/css/font",
},
];
};
export default sidebarItems;

View File

@ -4,8 +4,10 @@ export type SidebarItem = {
label: string;
to?: RouteLocationRaw;
icon?: string;
isVisible?: boolean;
children?: {
label: string;
to: RouteLocationRaw;
isVisible?: boolean;
}[];
};

View File

@ -11,5 +11,7 @@ export interface UserDetails {
email: string;
phone: number | null;
department: string | null;
isSuperUser: boolean;
roles: [];
permissions: string[];
}

View File

@ -0,0 +1,39 @@
import { useAuth } from "@/stores/Auth/auth.store";
import { storeToRefs } from "pinia";
export const hasPermission = (
permission: string | undefined | null | Array<string>,
requireAll: boolean = true,
allowSuperUser: boolean = true
) => {
const authStore = useAuth();
const { user } = storeToRefs(authStore);
if (user.value && user.value.isSuperUser && allowSuperUser) {
return true;
}
const p = new Set(user.value.permissions);
if (!p) return false;
if (!permission) return true;
if (Array.isArray(permission)) {
if (requireAll) {
return permission.every((perm) => p.has(perm));
} else {
return permission.some((perm) => p.has(perm));
}
}
return p?.has(permission);
};
export const isSuperUser = () => {
const authStore = useAuth();
const { user } = storeToRefs(authStore);
if (user.value && user.value.isSuperUser) {
return true;
}
return false;
};

View File

@ -2,7 +2,7 @@
<div class="app-container" :class="coreStore.isSidebarOpen ? 'sidebar-open' : 'sidebar-close'">
<Header></Header>
<div class="body-container">
<Sidebar :sidebarItems="sidebarItems"></Sidebar>
<Sidebar></Sidebar>
<div class="main-container">
<div class="relative">
<RouterView />
@ -19,130 +19,4 @@ import Header from "./components/Header.vue";
import { useCore } from "@/stores/App/core.store";
const coreStore = useCore();
import type { SidebarItem } from "@/core/types/app/sidebar.type";
const sidebarItems: SidebarItem[] = [
{
label: "Dashboard",
},
{
label: "Dashboard",
icon: "Home",
to: "/",
},
{
label: "Stock Verification",
icon: "Blocks",
children: [
{
label: "Opening Stock",
to: "/stock-verification/opening-stock/",
},
{
label: "Closing Stock",
to: "/stock-verification/closing-stock/",
},
],
},
{
label: "Components",
},
{
label: "Content Layout",
icon: "Package",
to: "/component/content-layout",
},
{
label: "Modal",
icon: "MessageSquareCode",
to: "/component/modal",
},
{
label: "Switch",
icon: "ToggleLeft",
to: "/component/switch",
},
{
label: "Toggle",
icon: "ToggleRight",
to: "/component/toggle",
},
{
label: "Tabulator",
icon: "Table",
to: "/component/tabulator",
},
{
label: "Towser",
icon: "Blinds",
to: "/component/towser",
},
{
label: "Date Range",
icon: "CalendarRange",
to: "/component/date-range",
},
{
label: "Date Selector",
icon: "Calendar",
to: "/component/date-selector",
},
{
label: "Wizard Stepper",
icon: "WandSparkles",
to: "/component/wizard",
},
{
label: "Loader",
icon: "Loader",
to: "/component/loader",
},
// {
// label: "ImageCropper Upload",
// icon: "Crop",
// to: "/component/image-cropper",
// },
{
label: "Directives",
},
{
label: "Input Error",
icon: "TriangleAlert",
to: "/directives/input-error",
},
{
label: "Input Currency",
icon: "CircleDollarSign",
to: "/directives/input-currency",
},
{
label: "Input Select",
icon: "TextCursorInput",
to: "/directives/input-select",
},
{
label: "Tooltip",
icon: "BookOpenText",
to: "/directives/tooltip",
},
{
label: "CSS",
},
{
label: "Buttons",
icon: "SquareArrowLeft",
to: "/css/button",
},
{
label: "Input",
icon: "TextCursor",
to: "/css/input",
},
{
label: "Font",
icon: "CaseSensitive",
to: "/css/font",
},
];
</script>

View File

@ -41,7 +41,7 @@
class="w-[42px] h-[42px] object-cover rounded-full"
/>
</span>
<span v-else class="h-[38px] my-auto text-[28px]">
<span v-else class="h-[38px] my-auto text-[24px]">
{{ getNameInitials(user.full_name ? user.full_name : user.username) }}
</span>
</div>
@ -65,7 +65,7 @@
</template>
<script setup lang="ts">
import { getNameInitials } from "@/core/utils/Common";
import { getNameInitials } from "@/core/utils/common.util";
import { useAuth } from "@/stores/Auth/auth.store";
import { storeToRefs } from "pinia";
import { ref, onMounted, onUnmounted } from "vue";

View File

@ -7,13 +7,16 @@
<Icons name="TextAlignJustify" size="30" />
</button>
<div class="sidebar-items">
<div v-for="(item, index) in sidebarItems">
<div class="sidebar-header" v-if="!item.to && !item.children">
<div v-for="(item, index) in SidebarItems">
<div
class="sidebar-header"
v-if="!item.to && !item.children && (item.isVisible != undefined ? item.isVisible : true)"
>
{{ item.label }}
</div>
<RouterLink
:to="item.to"
v-if="item.to && !item.children"
v-if="item.to && !item.children && (item.isVisible != undefined ? item.isVisible : true)"
class="sidebar-default"
:class="isActive(item) ? 'sidebar-item-active' : ''"
@click="isActive(item) ? toggleDropdown(-1) : null"
@ -24,7 +27,7 @@
<span>{{ item.label }}</span>
</RouterLink>
<div
v-if="item.children && !item.to"
v-if="item.children && !item.to && (item.isVisible != undefined ? item.isVisible : true)"
class="sidebar-dropdown"
:class="activeDropdown == index ? 'sidebar-item-open' : ''"
>
@ -40,8 +43,11 @@
<div class="sidebar-dropdown-items" v-animate-dropdown>
<div class="sidebar-dropdown-item" v-for="childitem in item.children">
<!-- v-if="childitem.permission ? hasPermission(childitem.permission) : true" -->
<RouterLink :to="childitem.to" :class="isActive(childitem) ? 'sidebar-item-active' : ''">
<RouterLink
:to="childitem.to"
:class="isActive(childitem) ? 'sidebar-item-active' : ''"
v-if="childitem.isVisible != undefined ? childitem.isVisible : true"
>
<span>{{ childitem.label }}</span>
</RouterLink>
</div>
@ -53,20 +59,16 @@
</template>
<script setup lang="ts">
import { onMounted, type PropType, ref } from "vue";
import { computed, onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import type { SidebarItem } from "@/core/types/app/sidebar.type";
import { useCore } from "@/stores/App/core.store";
import sidebarItems from "@/core/app/sidebarItems";
const router = useRouter();
const coreStore = useCore();
const props = defineProps({
sidebarItems: {
type: Array as PropType<SidebarItem[]>,
required: true,
},
});
const SidebarItems = computed(() => sidebarItems());
const activeDropdown = ref(-1);
@ -89,12 +91,12 @@ onMounted(() => {
});
const checkDropDown = () => {
if (props.sidebarItems) {
props.sidebarItems.forEach((item) => {
if (SidebarItems.value) {
SidebarItems.value.forEach((item) => {
if (item.children && !item.to) {
item.children.forEach((child) => {
if (child.to == router.currentRoute.value.path) {
toggleDropdown(props.sidebarItems.indexOf(item));
toggleDropdown(SidebarItems.value.indexOf(item));
}
});
}