You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
4.7 KiB
129 lines
4.7 KiB
<script setup lang="ts">
|
|
import { User, GraduationCap, Building2, Check, AlertCircle, ChevronDown } from 'lucide-vue-next'
|
|
import { Badge } from '@/components/ui/badge'
|
|
import { Alert, AlertDescription } from '@/components/ui/alert'
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from '@/components/ui/dropdown-menu'
|
|
import { Button } from '@/components/ui/button'
|
|
|
|
// Role management composable
|
|
const { activeRole, roles, roleChangedByAdmin, switchRole, fetchRoleStatus, loading } =
|
|
useActiveRole()
|
|
|
|
// Icon mapping for each role
|
|
const roleIcons: Record<string, any> = {
|
|
private: User,
|
|
educator: GraduationCap,
|
|
company: Building2,
|
|
}
|
|
|
|
// Color mapping for each role
|
|
const roleColors: Record<string, string> = {
|
|
private: 'text-purple-600',
|
|
educator: 'text-orange-500',
|
|
company: 'text-blue-600',
|
|
}
|
|
|
|
// Dropdown open/close state
|
|
const dropdownOpen = ref(false)
|
|
|
|
// Fetch role status when dropdown opens
|
|
watch(dropdownOpen, async (isOpen) => {
|
|
if (isOpen && !loading.value) {
|
|
await fetchRoleStatus()
|
|
}
|
|
})
|
|
|
|
// Current role data (for button display)
|
|
const currentRoleData = computed(() => roles.value.find((r) => r.code === activeRole.value))
|
|
|
|
// Handle role switch with error handling
|
|
async function handleRoleSwitch(roleCode: string, hasRole: boolean) {
|
|
if (!hasRole || loading.value) return
|
|
|
|
try {
|
|
await switchRole(roleCode)
|
|
dropdownOpen.value = false // Close dropdown after successful switch
|
|
} catch (error) {
|
|
console.error('Role switch failed:', error)
|
|
// Error is already set in composable, will be displayed if needed
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<DropdownMenu v-model:open="dropdownOpen">
|
|
<DropdownMenuTrigger as-child>
|
|
<Button variant="outline"
|
|
class="gap-2 border-2 border-white/30 bg-white/5 text-white hover:bg-white/10 hover:border-white/50 transition-all duration-200 group"
|
|
title="Rolle wechseln">
|
|
<!-- Context label + role -->
|
|
<span class="text-xs text-white/70 font-normal hidden md:inline">Du kaufst als:</span>
|
|
<component :is="roleIcons[activeRole] || User" class="h-4 w-4 text-white" />
|
|
<span class="font-medium text-white">{{
|
|
currentRoleData?.displayName || 'Privatperson'
|
|
}}</span>
|
|
<ChevronDown class="h-4 w-4 text-white/70 group-hover:text-white transition-colors" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
|
|
<DropdownMenuContent align="start" class="w-[312px]">
|
|
<!-- Admin Notice (if role was changed by admin) -->
|
|
<Alert v-if="roleChangedByAdmin" variant="warning" class="m-2 mb-3">
|
|
<AlertCircle class="h-4 w-4" />
|
|
<AlertDescription class="text-sm">
|
|
Deine Rolle wurde von einem Administrator geändert.
|
|
</AlertDescription>
|
|
</Alert>
|
|
|
|
<DropdownMenuLabel class="text-sm font-normal text-muted-foreground py-3">
|
|
Du kaufst als...
|
|
</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
|
|
<!-- All roles (approved + not-approved) -->
|
|
<DropdownMenuItem v-for="role in roles" :key="role.code" :disabled="!role.hasRole || loading" :class="[
|
|
'gap-3 py-3 cursor-pointer group',
|
|
!role.hasRole && 'opacity-60 cursor-not-allowed',
|
|
activeRole === role.code && 'bg-purple-50 dark:bg-purple-950',
|
|
]" @click="handleRoleSwitch(role.code, role.hasRole)">
|
|
<component :is="roleIcons[role.code]" :class="['h-5 w-5', roleColors[role.code]]" />
|
|
|
|
<div class="flex-1 flex flex-col gap-1">
|
|
<span class="text-sm font-semibold">{{ role.displayName }}</span>
|
|
<span
|
|
class="text-sm text-muted-foreground group-hover:text-foreground group-data-[highlighted]:text-accent-foreground transition-colors">
|
|
{{ role.description }}
|
|
</span>
|
|
</div>
|
|
|
|
<!-- High-contrast checkmark for colorblind accessibility -->
|
|
<div v-if="activeRole === role.code && role.hasRole" class="relative flex-shrink-0">
|
|
<!-- Background circle for contrast -->
|
|
<div class="absolute inset-0 bg-white dark:bg-gray-900 rounded-full opacity-90"></div>
|
|
<!-- Checkmark icon with high contrast -->
|
|
<Check class="h-5 w-5 text-gray-900 dark:text-white relative z-10" />
|
|
</div>
|
|
|
|
<!-- Badge if not approved -->
|
|
<Badge v-if="!role.hasRole" variant="secondary" class="text-xs px-2 py-0.5 flex-shrink-0">
|
|
{{ role.requiresApproval ? 'Demnächst' : 'Gesperrt' }}
|
|
</Badge>
|
|
</DropdownMenuItem>
|
|
|
|
<DropdownMenuSeparator />
|
|
|
|
<div class="px-3 py-2.5 text-sm text-muted-foreground">
|
|
<p class="leading-relaxed">
|
|
💡 Die Rolle bestimmt, welche Produkte und Konditionen dir angezeigt werden.
|
|
</p>
|
|
</div>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</template>
|
|
|