From 9fe18f6b1ac48da031d53a99382319dc20775aa7 Mon Sep 17 00:00:00 2001 From: ajdj100 Date: Mon, 1 Dec 2025 23:57:26 -0500 Subject: [PATCH] Admin navigation permissions --- api/src/services/rolesService.ts | 3 ++- shared/types/roles.ts | 6 +++++ ui/src/components/Navigation/Navbar.vue | 36 ++++++++++++++++--------- ui/src/composables/useAuth.ts | 32 ++++++++++++++++++++++ ui/src/stores/user.ts | 1 + 5 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 shared/types/roles.ts create mode 100644 ui/src/composables/useAuth.ts diff --git a/api/src/services/rolesService.ts b/api/src/services/rolesService.ts index 11fc3ff..b847ddc 100644 --- a/api/src/services/rolesService.ts +++ b/api/src/services/rolesService.ts @@ -1,4 +1,5 @@ import pool from '../db'; +import { Role } from '@app/shared/types/roles' export async function assignUserGroup(userID: number, roleID: number) { @@ -16,7 +17,7 @@ export async function createGroup(name: string, color: string, description: stri return { id: result.insertId, name, color, description }; } -export async function getUserRoles(userID: number) { +export async function getUserRoles(userID: number): Promise { const sql = `SELECT r.id, r.name FROM members_roles mr INNER JOIN roles r ON mr.role_id = r.id diff --git a/shared/types/roles.ts b/shared/types/roles.ts new file mode 100644 index 0000000..a232c52 --- /dev/null +++ b/shared/types/roles.ts @@ -0,0 +1,6 @@ +export interface Role { + id: number; + name: string; + color?: string; + description?: string; +} \ No newline at end of file diff --git a/ui/src/components/Navigation/Navbar.vue b/ui/src/components/Navigation/Navbar.vue index 3cc647f..c610eef 100644 --- a/ui/src/components/Navigation/Navbar.vue +++ b/ui/src/components/Navigation/Navbar.vue @@ -17,8 +17,11 @@ import NavigationMenuLink from '../ui/navigation-menu/NavigationMenuLink.vue'; import NavigationMenuTrigger from '../ui/navigation-menu/NavigationMenuTrigger.vue'; import NavigationMenuContent from '../ui/navigation-menu/NavigationMenuContent.vue'; import { navigationMenuTriggerStyle } from '../ui/navigation-menu/' +import { useAuth } from '@/composables/useAuth'; +import { CircleArrowOutUpRight } from 'lucide-vue-next'; const userStore = useUserStore(); +const auth = useAuth(); //@ts-ignore const APIHOST = import.meta.env.VITE_APIHOST; @@ -88,50 +91,57 @@ function blurAfter() { - + Administration - + Promotions - + Leave of Absence - + Transfer Requests - + Recruitment - + Role Management - - - - Members (debug) - - - + + + Members (debug) + + + diff --git a/ui/src/composables/useAuth.ts b/ui/src/composables/useAuth.ts new file mode 100644 index 0000000..1f1929e --- /dev/null +++ b/ui/src/composables/useAuth.ts @@ -0,0 +1,32 @@ +import { useUserStore } from "@/stores/user" +import { computed } from "vue"; +import { Role } from "@shared/types/roles" + +export function useAuth() { + const userStore = useUserStore(); + + const roles = computed(() => { + return userStore.user?.roleData?.map((r: Role) => r.name) ?? []; + }); + + function isDev() { + return roles.value.includes('Dev'); + } + + function hasRole(roleName: string): boolean { + if (isDev()) return true; + return roles.value.includes(roleName); + } + + function hasAnyRole(roleNames: string[]): boolean { + if (isDev()) return true; + return roles.value.some(name => roleNames.includes(name)) + } + + function hasAllRoles(roleNames: string[]): boolean { + if (isDev()) return true; + return roles.value.every(name => roleNames.includes(name)) + } + + return { hasRole, hasAnyRole, hasAllRoles } +} \ No newline at end of file diff --git a/ui/src/stores/user.ts b/ui/src/stores/user.ts index e50c323..ec75250 100644 --- a/ui/src/stores/user.ts +++ b/ui/src/stores/user.ts @@ -16,6 +16,7 @@ export const useUserStore = defineStore('user', () => { if (res.ok) { const data = await res.json(); + console.log(data); user.value = data; }