13 KiB
Rollen-System Dokumentation
Version: 1.0 Letztes Update: Januar 2025
Überblick
Das Rollen-System von my.experimenta.science ermöglicht es, Produkte und UI-Komponenten basierend auf der aktiven Rolle des Benutzers anzuzeigen oder zu verstecken. Dies ermöglicht rollenspezifische Preise, Produkte und Funktionen.
1. Rollen-Typen
1.1 Verfügbare Rollen
| Code | Display Name | Beschreibung | Approval erforderlich |
|---|---|---|---|
private |
Privatperson | Private Nutzung | Nein (auto-assigned) |
educator |
Pädagoge | Lehrkräfte und Schulen | Ja (Post-MVP) |
company |
Unternehmen | Geschäftskunden | Ja (Post-MVP) |
1.2 TypeScript Type Definition
type RoleCode = 'private' | 'educator' | 'company'
interface Role {
code: RoleCode // Primary key
displayName: string // "Privatperson", "Pädagoge", "Unternehmen"
description: string
requiresApproval: boolean // false for 'private', true for others
sortOrder: number
active: boolean
}
2. Automatische Rollen-Zuweisung
2.1 Auto-Assignment bei First Login (MVP)
Alle neuen Benutzer erhalten automatisch die Rolle private beim ersten Login:
// server/api/auth/login.post.ts
if (!user) {
// Neuer User - erstelle Profil
const [newUser] = await db.insert(users).values({...}).returning()
// Auto-assign 'private' role
await assignRoleToUser(newUser.id, 'private', {
adminNotes: 'Auto-assigned on first login',
})
}
Wichtig:
- Status ist immer
approved(kein Approval-Workflow in MVP) - Jeder User hat mindestens eine Rolle
- Safety-Check: Existing users ohne Rollen bekommen auch
privaterole
3. Rollen-basierte Produktsichtbarkeit
3.1 Konzept
Produkte sind NUR sichtbar, wenn:
- Das Produkt
product_role_visibilityEinträge hat - Der User eine genehmigte (
approved) Rolle hat, die inproduct_role_visibilityvorkommt
Opt-in Visibility:
- Produkt ohne
product_role_visibilityEinträge → Unsichtbar für ALLE - User ohne genehmigte Rollen → Sieht KEINE Produkte
3.2 Database Schema
-- Many-to-Many: Product ↔ Roles
CREATE TABLE product_role_visibility (
id UUID PRIMARY KEY,
product_id UUID NOT NULL REFERENCES products(id),
role_code TEXT NOT NULL REFERENCES roles(code),
UNIQUE(product_id, role_code)
);
3.3 Automatische Zuordnung (ERP Import)
Beim Import aus NAV ERP werden Produkte automatisch Rollen zugeordnet:
// server/utils/roles.ts
const categoryRoleMapping = {
'makerspace-annual-pass': ['private', 'educator'],
'annual-pass': ['private'],
'educator-annual-pass': ['educator'],
'company-annual-pass': ['company']
}
// server/api/erp/products.post.ts
await assignRolesToProductByCategory(product.id, category)
3.4 API Filtering Pattern
// server/api/products/index.get.ts
export default defineEventHandler(async (event) => {
const { user } = await getUserSession(event)
// Unauthenticated users see NO products
if (!user) return []
// Get visible product IDs for user's roles
const visibleProductIds = await getVisibleProductIdsForUser(user.id)
// Fetch products with role filter
const products = await db.query.products.findMany({
where: and(
eq(products.active, true),
inArray(products.id, visibleProductIds)
)
})
return products
})
4. Rollen-basierte Menüpunkt-Sichtbarkeit
4.1 Konzept
Navigation-Tabs können für bestimmte Rollen ein-/ausgeblendet werden:
roleVisibility: 'all'→ Sichtbar für alle RollenroleVisibility: ['educator']→ Nur für PädagogenroleVisibility: ['educator', 'company']→ Für mehrere Rollen
4.2 Implementation (AreaTabs.vue)
interface ProductArea {
id: string
label: string
icon: any
enabled: boolean
visible: boolean
badge?: string
route: string
roleVisibility?: 'all' | RoleCode[] // NEW
}
const areas: ProductArea[] = [
{ id: 'start', roleVisibility: 'all' }, // Alle Rollen
{ id: 'makerspace', roleVisibility: 'all' }, // Alle Rollen
{ id: 'educator', roleVisibility: ['educator'] }, // Nur Pädagogen
{ id: 'experimenta', roleVisibility: 'all' }, // Alle Rollen
]
const { activeRole } = useActiveRole()
const visibleAreas = computed(() => {
return areas.filter(area => {
if (!area.visible) return false
if (!area.roleVisibility) return true
if (area.roleVisibility === 'all') return true
return area.roleVisibility.includes(activeRole.value as RoleCode)
})
})
4.3 Automatische Updates
Menüpunkte aktualisieren sich automatisch beim Rollenwechsel:
- ✅ Vue Computed Property reagiert auf
activeRoleÄnderungen - ✅ Keine manuellen Refresh-Calls nötig
- ✅ Sofortige UI-Updates
5. Client-Side Composable
5.1 useActiveRole()
// app/composables/useActiveRole.ts
export function useActiveRole() {
const activeRole = useState<string>('activeRole', () => 'private')
const roles = useState<RoleWithStatus[]>('roles', () => [])
// Fetch current role status from server
async function fetchRoleStatus() { ... }
// Switch to a different role
async function switchRole(roleCode: string) {
await $fetch('/api/user/active-role', {
method: 'PATCH',
body: { roleCode },
})
activeRole.value = roleCode
// Auto-refresh product pages
await Promise.all([
refreshNuxtData('products-list'),
refreshNuxtData('educator-products'),
refreshNuxtData('experimenta-products'),
])
}
return {
activeRole,
roles,
approvedRoles: computed(() => roles.value.filter(r => r.hasRole)),
hasMultipleRoles: computed(() => approvedRoles.value.length > 1),
fetchRoleStatus,
switchRole,
}
}
5.2 Auto-Initialization
Rollen werden automatisch beim Login geladen:
const { loggedIn } = useUserSession()
if (loggedIn.value) {
callOnce('init-roles', () => fetchRoleStatus())
}
6. Server-Side Utilities
6.1 Wichtige Functions
// server/utils/roles.ts
// Get user's approved role codes
await getUserApprovedRoleCodes(userId)
// => ['private', 'educator']
// Get visible product IDs for user
await getVisibleProductIdsForUser(userId)
// => ['uuid-1', 'uuid-2', ...]
// Get visible product IDs for specific role
await getVisibleProductIdsForRole(userId, roleCode)
// => ['uuid-1', 'uuid-2', ...]
// Check if product is visible for user
await isProductVisibleForUser(productId, userId)
// => true/false
// Assign role to user (MVP: always approved)
await assignRoleToUser(userId, 'private', {
adminNotes: 'Auto-assigned',
})
// Auto-assign roles to product by category
await assignRolesToProductByCategory(productId, 'annual-pass')
7. Session Management
7.1 Active Role Session
Die aktive Rolle wird server-side in der Session gespeichert:
// server/utils/role-session.ts
// Get user's active role (with TTL validation)
const activeRole = await getUserActiveRole(event)
// => 'private' | 'educator' | 'company'
// Set user's active role in session + DB
await setUserActiveRole(event, userId, 'educator')
// Validate active role (TTL-based)
const isValid = await validateActiveRole(event, userId)
Session TTL:
- Active role ist 30 Tage gültig
- Nach Ablauf: Fallback auf erste approved role
- Update bei jedem
switchRole()
8. API Endpoints
8.1 Role Management Endpoints
| Endpoint | Method | Beschreibung |
|---|---|---|
/api/user/role-status |
GET | Aktuellen Role-Status abrufen |
/api/user/active-role |
PATCH | Aktive Rolle wechseln |
GET /api/user/role-status
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": true,
"requiresApproval": true
}
],
"roleChangedByAdmin": false
}
PATCH /api/user/active-role
Request:
{
"roleCode": "educator"
}
Validation:
- ✅ User muss eingeloggt sein
- ✅ User muss die Rolle haben (approved status)
- ✅ Rolle muss existieren
9. UI Components
9.1 RoleSwitcher Component
Der RoleSwitcher zeigt die aktive Rolle an und ermöglicht Rollenwechsel:
<!-- app/components/navigation/RoleSwitcher.vue -->
<template>
<DropdownMenu>
<DropdownMenuTrigger>
Du kaufst als: {{ activeRoleDisplay }}
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
v-for="role in approvedRoles"
@click="switchRole(role.code)"
>
{{ role.displayName }}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</template>
Features:
- ✅ Zeigt nur approved roles
- ✅ Markiert aktive Rolle mit Checkmark
- ✅ Disabled für roles die User nicht hat
- ✅ Triggert automatische Product-Refresh
10. Testing
10.1 Test User Setup
// Test User mit multiple roles
const user = await createTestUser({
email: 'test@example.com',
roles: ['private', 'educator']
})
// Setze aktive Rolle
await setUserActiveRole(event, user.id, 'educator')
10.2 Test Scenarios
Scenario 1: Privatperson Login
- ✅ User erhält
privaterole - ✅ Sieht nur Produkte mit
privaterole - ✅ Sieht Navigation: Start, Makerspace, experimenta
Scenario 2: Pädagoge Login
- ✅ User hat
private+educatorroles - ✅ Kann zwischen Rollen wechseln
- ✅ Als Pädagoge: Zusätzliche Produkte sichtbar
- ✅ Als Pädagoge: "Bildung" Tab sichtbar
Scenario 3: Rollenwechsel
- ✅ Produktliste aktualisiert sich automatisch
- ✅ Navigation aktualisiert sich automatisch
- ✅ Session + DB werden aktualisiert
- ✅ Product detail page: 404 redirect wenn nicht sichtbar
11. MVP Limitations
11.1 Was NICHT implementiert ist (Post-MVP)
Approval Workflow:
- ❌ Keine UI für Rollen-Antrag (Pädagogen/Unternehmen)
- ❌ Kein Admin-Panel für Genehmigung
- ❌ Status bleibt immer
approved
Role Management:
- ❌ Keine Self-Service Rollen-Verwaltung
- ❌ Keine Admin-Funktionen zum Hinzufügen/Entfernen von Rollen
- ❌ Keine Rollen-History für Admins
Advanced Features:
- ❌ Keine Organisation-Level Rollen (z.B. alle Lehrer einer Schule)
- ❌ Keine zeitlich begrenzte Rollen
- ❌ Keine Rollen-Hierarchien
11.2 Database prepared for Post-MVP
Trotz MVP Limitations ist die Datenbank bereits vorbereitet:
// user_roles table (prepared but status always 'approved' in MVP)
interface UserRole {
status: 'pending' | 'approved' | 'rejected' | 'revoked'
organizationName?: string // Prepared
requestMessage?: string // Prepared
proofDocument?: string // Prepared
adminNotes?: string // Prepared
statusHistory: StatusHistoryEntry[] // JSONB audit trail
}
12. Best Practices
12.1 Beim Hinzufügen neuer Rollen
- Database: Füge neue Rolle zu
rolestable hinzu - Type: Update
RoleCodetype in schema - Mapping: Update
categoryRoleMappinginserver/utils/roles.ts - UI: Update RoleSwitcher display names
- Navigation: Update
roleVisibilityin AreaTabs.vue
12.2 Beim Hinzufügen neuer Produkte
- Category: Definiere eine klare Kategorie
- Mapping: Füge Kategorie zu
categoryRoleMappinghinzu - Visibility: Produkt wird automatisch Rollen zugeordnet beim Import
12.3 Beim Hinzufügen neuer Navigation-Items
{
id: 'new-tab',
label: 'Neuer Tab',
roleVisibility: 'all' // oder ['educator', 'company']
}
13. Troubleshooting
Problem: User sieht keine Produkte
Prüfen:
- Hat User eine approved Rolle? →
getUserApprovedRoleCodes(userId) - Hat Produkt
product_role_visibilityEinträge? - Matchen Rolle und Produkt-Visibility?
Problem: Menüpunkt erscheint nicht nach Rollenwechsel
Prüfen:
- Ist
roleVisibilitykorrekt gesetzt? - Ist
activeRolekorrekt aktualisiert? → Vue DevTools - Ist computed property
visibleAreasreaktiv?
Problem: Produkt bleibt sichtbar nach Rollenwechsel
Prüfen:
- Wurde
refreshNuxtData()aufgerufen inswitchRole()? - Sind die richtigen keys verwendet? (
products-list, etc.) - Hat API-Endpoint role-based filtering?
14. Weitere Dokumentation
- Architektur:
docs/ARCHITECTURE.md- Database Schema Details - PRD:
docs/PRD.md- Product Requirements - Testing:
docs/TESTING.md- Test Credentials & Scenarios - Cidaas Integration:
docs/CIDAAS_INTEGRATION.md- Auth Flow
15. Changelog
| Version | Datum | Änderung |
|---|---|---|
| 1.0 | Jan 2025 | Initial documentation - MVP implementation |