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:
Bastian Masanek
2025-11-05 01:04:26 +01:00
parent 0e450684c6
commit f9125e744b
16 changed files with 1573 additions and 88 deletions

View File

@@ -0,0 +1,64 @@
/**
* PATCH /api/user/active-role
*
* Switch user's active role (used by RoleSwitcher component)
*
* Request body:
* {
* "roleCode": "educator"
* }
*
* Response:
* {
* "success": true,
* "activeRoleCode": "educator"
* }
*
* Validates that user has the requested role before switching
* Updates both session (immediate) and database (preference)
*/
import { z } from 'zod'
import { setUserActiveRole } from '../../utils/role-session'
const switchRoleSchema = z.object({
roleCode: z.enum(['private', 'educator', 'company'], {
errorMap: () => ({ message: 'Ungültige Rolle' }),
}),
})
export default defineEventHandler(async (event) => {
// Require authentication
await requireUserSession(event)
// Validate request body
const body = await readBody(event)
const { roleCode } = switchRoleSchema.parse(body)
try {
// Set active role (validates + updates session + saves to DB)
await setUserActiveRole(event, roleCode)
return {
success: true,
activeRoleCode: roleCode,
}
} catch (error: any) {
// setUserActiveRole throws 403 if user doesn't have role
if (error.statusCode === 403) {
setResponseStatus(event, 403)
return {
success: false,
message: error.message || 'Du hast diese Rolle nicht',
}
}
// Other errors
console.error('Role switch error:', error)
setResponseStatus(event, 500)
return {
success: false,
message: 'Fehler beim Wechseln der Rolle',
}
}
})

View File

@@ -0,0 +1,69 @@
/**
* GET /api/user/role-status
*
* Get user's active role and all available roles (for RoleSwitcher dropdown)
*
* Response:
* {
* "activeRoleCode": "private",
* "roles": [
* {
* "code": "private",
* "displayName": "Privatperson",
* "description": "Private Nutzung",
* "hasRole": true,
* "requiresApproval": false
* },
* {
* "code": "educator",
* "displayName": "Pädagoge",
* "description": "Lehrkräfte und Schulen",
* "hasRole": false,
* "requiresApproval": true
* },
* ...
* ],
* "roleChangedByAdmin": false
* }
*
* - Validates active role with TTL (re-checks DB every 5min)
* - Returns ALL roles (approved + not-approved) for dropdown
* - Includes "hasRole" flag to show which roles user actually has
*/
import { asc, eq } from 'drizzle-orm'
import { roles } from '../../database/schema'
import { getUserActiveRole } from '../../utils/role-session'
import { getUserApprovedRoleCodes } from '../../utils/roles'
export default defineEventHandler(async (event) => {
const session = await requireUserSession(event)
// Get active role (validates with TTL, auto-fallback if revoked)
const activeRole = await getUserActiveRole(event)
// Get user's approved role codes
const approvedRoleCodes = await getUserApprovedRoleCodes(session.user.id)
// Get ALL roles from database (for dropdown: show all, disabled if not approved)
const db = useDatabase()
const allRoles = await db.query.roles.findMany({
where: eq(roles.active, true),
orderBy: asc(roles.sortOrder),
})
// Map roles with "hasRole" status
const rolesWithStatus = allRoles.map((role) => ({
code: role.code,
displayName: role.displayName,
description: role.description,
hasRole: approvedRoleCodes.includes(role.code),
requiresApproval: role.requiresApproval,
}))
return {
activeRoleCode: activeRole,
roles: rolesWithStatus,
roleChangedByAdmin: session.roleChangedByAdmin || false,
}
})