Added Permission Check Function
This commit is contained in:
parent
660f994036
commit
f0185dc7d5
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -34,3 +34,4 @@ coverage
|
||||||
|
|
||||||
# Vitest
|
# Vitest
|
||||||
__screenshots__/
|
__screenshots__/
|
||||||
|
config.json
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"ENVIRONMENT": "PRODUCTION || DEVELOPMENT",
|
"ENVIRONMENT": "PRODUCTION || DEVELOPMENT",
|
||||||
"PAGE_TITLE": "Dolphin",
|
"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_NAME": "Mavorion Systems",
|
||||||
"CLIENT_LOGO": "",
|
|
||||||
"CLIENT_LOCATION": "LAZIMPAT",
|
"CLIENT_LOCATION": "LAZIMPAT",
|
||||||
"API_BASE_URL": "http://localhost:8000/api/"
|
"CLIENT_LOGO": "",
|
||||||
|
"API_BASE_URL": "http://localhost:8000/api/v1/"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
124
src/core/app/sidebarItems.ts
Normal file
124
src/core/app/sidebarItems.ts
Normal 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;
|
||||||
|
|
@ -4,8 +4,10 @@ export type SidebarItem = {
|
||||||
label: string;
|
label: string;
|
||||||
to?: RouteLocationRaw;
|
to?: RouteLocationRaw;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
isVisible?: boolean;
|
||||||
children?: {
|
children?: {
|
||||||
label: string;
|
label: string;
|
||||||
to: RouteLocationRaw;
|
to: RouteLocationRaw;
|
||||||
|
isVisible?: boolean;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,7 @@ export interface UserDetails {
|
||||||
email: string;
|
email: string;
|
||||||
phone: number | null;
|
phone: number | null;
|
||||||
department: string | null;
|
department: string | null;
|
||||||
|
isSuperUser: boolean;
|
||||||
roles: [];
|
roles: [];
|
||||||
|
permissions: string[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
39
src/core/utils/permission.util.ts
Normal file
39
src/core/utils/permission.util.ts
Normal 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;
|
||||||
|
};
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="app-container" :class="coreStore.isSidebarOpen ? 'sidebar-open' : 'sidebar-close'">
|
<div class="app-container" :class="coreStore.isSidebarOpen ? 'sidebar-open' : 'sidebar-close'">
|
||||||
<Header></Header>
|
<Header></Header>
|
||||||
<div class="body-container">
|
<div class="body-container">
|
||||||
<Sidebar :sidebarItems="sidebarItems"></Sidebar>
|
<Sidebar></Sidebar>
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<RouterView />
|
<RouterView />
|
||||||
|
|
@ -19,130 +19,4 @@ import Header from "./components/Header.vue";
|
||||||
import { useCore } from "@/stores/App/core.store";
|
import { useCore } from "@/stores/App/core.store";
|
||||||
|
|
||||||
const coreStore = useCore();
|
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>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
class="w-[42px] h-[42px] object-cover rounded-full"
|
class="w-[42px] h-[42px] object-cover rounded-full"
|
||||||
/>
|
/>
|
||||||
</span>
|
</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) }}
|
{{ getNameInitials(user.full_name ? user.full_name : user.username) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 { useAuth } from "@/stores/Auth/auth.store";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { ref, onMounted, onUnmounted } from "vue";
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,16 @@
|
||||||
<Icons name="TextAlignJustify" size="30" />
|
<Icons name="TextAlignJustify" size="30" />
|
||||||
</button>
|
</button>
|
||||||
<div class="sidebar-items">
|
<div class="sidebar-items">
|
||||||
<div v-for="(item, index) in sidebarItems">
|
<div v-for="(item, index) in SidebarItems">
|
||||||
<div class="sidebar-header" v-if="!item.to && !item.children">
|
<div
|
||||||
|
class="sidebar-header"
|
||||||
|
v-if="!item.to && !item.children && (item.isVisible != undefined ? item.isVisible : true)"
|
||||||
|
>
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</div>
|
</div>
|
||||||
<RouterLink
|
<RouterLink
|
||||||
:to="item.to"
|
: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="sidebar-default"
|
||||||
:class="isActive(item) ? 'sidebar-item-active' : ''"
|
:class="isActive(item) ? 'sidebar-item-active' : ''"
|
||||||
@click="isActive(item) ? toggleDropdown(-1) : null"
|
@click="isActive(item) ? toggleDropdown(-1) : null"
|
||||||
|
|
@ -24,7 +27,7 @@
|
||||||
<span>{{ item.label }}</span>
|
<span>{{ item.label }}</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<div
|
<div
|
||||||
v-if="item.children && !item.to"
|
v-if="item.children && !item.to && (item.isVisible != undefined ? item.isVisible : true)"
|
||||||
class="sidebar-dropdown"
|
class="sidebar-dropdown"
|
||||||
:class="activeDropdown == index ? 'sidebar-item-open' : ''"
|
:class="activeDropdown == index ? 'sidebar-item-open' : ''"
|
||||||
>
|
>
|
||||||
|
|
@ -40,8 +43,11 @@
|
||||||
|
|
||||||
<div class="sidebar-dropdown-items" v-animate-dropdown>
|
<div class="sidebar-dropdown-items" v-animate-dropdown>
|
||||||
<div class="sidebar-dropdown-item" v-for="childitem in item.children">
|
<div class="sidebar-dropdown-item" v-for="childitem in item.children">
|
||||||
<!-- v-if="childitem.permission ? hasPermission(childitem.permission) : true" -->
|
<RouterLink
|
||||||
<RouterLink :to="childitem.to" :class="isActive(childitem) ? 'sidebar-item-active' : ''">
|
:to="childitem.to"
|
||||||
|
:class="isActive(childitem) ? 'sidebar-item-active' : ''"
|
||||||
|
v-if="childitem.isVisible != undefined ? childitem.isVisible : true"
|
||||||
|
>
|
||||||
<span>{{ childitem.label }}</span>
|
<span>{{ childitem.label }}</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -53,20 +59,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, type PropType, ref } from "vue";
|
import { computed, onMounted, ref } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import type { SidebarItem } from "@/core/types/app/sidebar.type";
|
import type { SidebarItem } from "@/core/types/app/sidebar.type";
|
||||||
import { useCore } from "@/stores/App/core.store";
|
import { useCore } from "@/stores/App/core.store";
|
||||||
|
import sidebarItems from "@/core/app/sidebarItems";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const coreStore = useCore();
|
const coreStore = useCore();
|
||||||
|
|
||||||
const props = defineProps({
|
const SidebarItems = computed(() => sidebarItems());
|
||||||
sidebarItems: {
|
|
||||||
type: Array as PropType<SidebarItem[]>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const activeDropdown = ref(-1);
|
const activeDropdown = ref(-1);
|
||||||
|
|
||||||
|
|
@ -89,12 +91,12 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const checkDropDown = () => {
|
const checkDropDown = () => {
|
||||||
if (props.sidebarItems) {
|
if (SidebarItems.value) {
|
||||||
props.sidebarItems.forEach((item) => {
|
SidebarItems.value.forEach((item) => {
|
||||||
if (item.children && !item.to) {
|
if (item.children && !item.to) {
|
||||||
item.children.forEach((child) => {
|
item.children.forEach((child) => {
|
||||||
if (child.to == router.currentRoute.value.path) {
|
if (child.to == router.currentRoute.value.path) {
|
||||||
toggleDropdown(props.sidebarItems.indexOf(item));
|
toggleDropdown(SidebarItems.value.indexOf(item));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user