From dcd96ffb6877c1cce9a479b4f90a5c053f94aee1 Mon Sep 17 00:00:00 2001 From: Bastian Masanek Date: Wed, 5 Nov 2025 01:33:46 +0100 Subject: [PATCH] Enhance Role-Based Visibility and Navigation Logic - Introduced role visibility checks in AreaTabs.vue to filter displayed areas based on the user's active role, improving user experience and accessibility. - Updated RoleSwitcher.vue to enhance accessibility with a high-contrast checkmark for better visibility. - Modified useActiveRole.ts to streamline role initialization and refresh logic for role-based product visibility. - Added explicit keys for role-based data fetching in product-related pages to ensure accurate data refresh. - Enhanced API endpoint for product retrieval to return 404 if a product is not accessible based on the user's role, ensuring security and clarity. --- app/components/navigation/AreaTabs.vue | 36 +++++++++++++++++++--- app/components/navigation/RoleSwitcher.vue | 11 +++++-- app/composables/useActiveRole.ts | 24 +++++++-------- app/pages/educator/index.vue | 1 + app/pages/experimenta/index.vue | 1 + app/pages/products/[id].vue | 19 +++++++++++- app/pages/products/index.vue | 1 + server/api/products/[id].get.ts | 31 ++++++++++++++++++- 8 files changed, 102 insertions(+), 22 deletions(-) diff --git a/app/components/navigation/AreaTabs.vue b/app/components/navigation/AreaTabs.vue index f2d2d7e..6b69482 100644 --- a/app/components/navigation/AreaTabs.vue +++ b/app/components/navigation/AreaTabs.vue @@ -3,6 +3,8 @@ import { Wrench, FlaskConical, Ticket, Sparkles, GraduationCap, Home } from 'luc import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Badge } from '@/components/ui/badge' +type RoleCode = 'private' | 'educator' | 'company' + interface ProductArea { id: string label: string @@ -11,6 +13,7 @@ interface ProductArea { visible: boolean badge?: string route: string + roleVisibility?: 'all' | RoleCode[] } const areas: ProductArea[] = [ @@ -21,6 +24,7 @@ const areas: ProductArea[] = [ enabled: true, visible: true, route: '/', + roleVisibility: 'all', }, { id: 'makerspace', @@ -29,6 +33,7 @@ const areas: ProductArea[] = [ enabled: true, visible: true, route: '/products', + roleVisibility: 'all', }, { id: 'educator', @@ -38,6 +43,7 @@ const areas: ProductArea[] = [ visible: true, badge: 'Demnächst', route: '/educator', + roleVisibility: ['educator'], }, { id: 'experimenta', @@ -47,6 +53,7 @@ const areas: ProductArea[] = [ visible: true, badge: 'Demnächst', route: '/experimenta', + roleVisibility: 'all', }, { id: 'labs', @@ -56,22 +63,41 @@ const areas: ProductArea[] = [ visible: false, badge: 'Demnächst', route: '/labs', + roleVisibility: ['educator', 'company'], }, ] const route = useRoute() +const { activeRole } = useActiveRole() + +// Filter areas by role visibility +const visibleAreas = computed(() => { + return areas.filter(area => { + // Legacy visible flag check + if (!area.visible) return false + + // No role requirement = visible to all (backward compatible) + if (!area.roleVisibility) return true + + // Explicitly set to 'all' + if (area.roleVisibility === 'all') return true + + // Check if user's active role matches + return area.roleVisibility.includes(activeRole.value as RoleCode) + }) +}) const currentArea = computed(() => { - // Determine current area based on route - check areas array dynamically + // Determine current area based on route - check visibleAreas array dynamically const currentPath = route.path // Exact match for root path if (currentPath === '/') { - return areas.find(area => area.route === '/')?.id || '' + return visibleAreas.value.find(area => area.route === '/')?.id || '' } // Find area where route path starts with area.route - const matchedArea = areas.find(area => + const matchedArea = visibleAreas.value.find(area => area.route !== '/' && currentPath.startsWith(area.route) ) @@ -90,7 +116,7 @@ function navigateToArea(area: ProductArea) {