Implement Role Management Features and UI Enhancements
- Introduced a new composable `useActiveRole` for managing user roles, including fetching role status and switching roles with server validation. - Updated `RoleSwitcher.vue` to utilize the new composable, enhancing role selection with improved error handling and UI feedback. - Added new API endpoints for role management, including fetching user role status and switching active roles. - Enhanced product visibility logic to filter based on the user's active role, ensuring a tailored experience. - Updated database schema to support last active role tracking for users, improving session management. - Refined UI components across the application to reflect role-based changes and improve user experience.
This commit is contained in:
142
app/composables/useActiveRole.ts
Normal file
142
app/composables/useActiveRole.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* useActiveRole Composable
|
||||
*
|
||||
* Client-side state management for user's active role (RoleSwitcher)
|
||||
*
|
||||
* Features:
|
||||
* - Reactive active role state
|
||||
* - List of all roles with approval status
|
||||
* - Switch role function with server validation
|
||||
* - Admin change notification flag
|
||||
*
|
||||
* Usage:
|
||||
* ```vue
|
||||
* const { activeRole, roles, roleChangedByAdmin, switchRole, fetchRoleStatus } = useActiveRole()
|
||||
*
|
||||
* // Fetch current status (validates server-side)
|
||||
* await fetchRoleStatus()
|
||||
*
|
||||
* // Switch role
|
||||
* await switchRole('educator')
|
||||
* ```
|
||||
*/
|
||||
|
||||
export interface RoleWithStatus {
|
||||
code: string
|
||||
displayName: string
|
||||
description: string
|
||||
hasRole: boolean
|
||||
requiresApproval: boolean
|
||||
}
|
||||
|
||||
export function useActiveRole() {
|
||||
// Use useState for shared state across the app
|
||||
const activeRole = useState<string>('activeRole', () => 'private')
|
||||
const roles = useState<RoleWithStatus[]>('roles', () => [])
|
||||
const roleChangedByAdmin = useState<boolean>('roleChangedByAdmin', () => false)
|
||||
const loading = useState<boolean>('roleLoading', () => false)
|
||||
const error = useState<string | null>('roleError', () => null)
|
||||
const initialized = useState<boolean>('roleInitialized', () => false)
|
||||
|
||||
/**
|
||||
* Fetch current role status from server
|
||||
* - Validates active role (TTL-based)
|
||||
* - Fetches all roles with approval status
|
||||
* - Detects if admin changed user's roles
|
||||
*/
|
||||
async function fetchRoleStatus() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const data = await $fetch('/api/user/role-status')
|
||||
|
||||
activeRole.value = data.activeRoleCode
|
||||
roles.value = data.roles
|
||||
roleChangedByAdmin.value = data.roleChangedByAdmin
|
||||
|
||||
return data
|
||||
} catch (err: any) {
|
||||
console.error('Failed to fetch role status:', err)
|
||||
error.value = 'Fehler beim Laden der Rollen'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to a different role
|
||||
* - Validates server-side that user has the role
|
||||
* - Updates session + database
|
||||
* - Refreshes products list if on products page
|
||||
*
|
||||
* @param roleCode - Role to switch to ('private', 'educator', 'company')
|
||||
* @throws Error if user doesn't have the role or if switch fails
|
||||
*/
|
||||
async function switchRole(roleCode: string) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await $fetch('/api/user/active-role', {
|
||||
method: 'PATCH',
|
||||
body: { roleCode },
|
||||
})
|
||||
|
||||
// Update local state
|
||||
activeRole.value = roleCode
|
||||
roleChangedByAdmin.value = false
|
||||
|
||||
// Refresh products if on products page
|
||||
await refreshNuxtData('products')
|
||||
|
||||
return true
|
||||
} catch (err: any) {
|
||||
console.error('Failed to switch role:', err)
|
||||
error.value = err.data?.message || 'Fehler beim Wechseln der Rolle'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get roles that user actually has (approved only)
|
||||
*/
|
||||
const approvedRoles = computed(() => roles.value.filter((r) => r.hasRole))
|
||||
|
||||
/**
|
||||
* Check if user has multiple roles
|
||||
*/
|
||||
const hasMultipleRoles = computed(() => approvedRoles.value.length > 1)
|
||||
|
||||
/**
|
||||
* Auto-initialize on first use (fetch role status from server)
|
||||
* This ensures the role is correct immediately after login
|
||||
*/
|
||||
const { loggedIn } = useUserSession()
|
||||
if (!initialized.value && loggedIn.value) {
|
||||
initialized.value = true
|
||||
// Fetch initial role status (don't await to avoid blocking)
|
||||
fetchRoleStatus().catch((err) => {
|
||||
// Silent fail - user can still use the app with default role
|
||||
console.warn('Failed to initialize role status:', err)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
// State (useState already returns writable refs)
|
||||
activeRole,
|
||||
roles,
|
||||
approvedRoles,
|
||||
hasMultipleRoles,
|
||||
roleChangedByAdmin,
|
||||
loading,
|
||||
error,
|
||||
|
||||
// Actions
|
||||
fetchRoleStatus,
|
||||
switchRole,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user