From 3dc60cdf6a0ccb0d39707e6d4bd95a199e1ae4b4 Mon Sep 17 00:00:00 2001 From: Bastian Masanek Date: Wed, 5 Nov 2025 01:36:27 +0100 Subject: [PATCH] Add Detailed Role Documentation and Update References - Introduced comprehensive documentation for the role system in a new `docs/ROLES.md` file, covering role types, auto-assignment, product visibility, and menu visibility. - Updated `CLAUDE.md` and `docs/PRD.md` to reference the new roles documentation, enhancing clarity on role management and its implications for users. - Ensured all relevant documentation links are consistent and accessible for better user guidance. --- CLAUDE.md | 3 + docs/PRD.md | 2 + docs/ROLES.md | 524 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 529 insertions(+) create mode 100644 docs/ROLES.md diff --git a/CLAUDE.md b/CLAUDE.md index 574fe9d..371aec5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -193,6 +193,8 @@ See `docs/ARCHITECTURE.md` for full schema. Key tables: - Opt-in visibility: No role assignment = invisible to everyone - **Auto-assignment:** New users automatically receive `'private'` role on first login +**📖 Detaillierte Rollen-Dokumentation:** Siehe [`docs/ROLES.md`](./docs/ROLES.md) für vollständige Informationen zum Rollen-System (Auto-Assignment, Produktsichtbarkeit, Menüpunkt-Filterung, Session-Management, API-Endpoints, Code-Patterns). + Use Drizzle ORM for all database operations. Most tables use UUID primary keys (except `roles` which uses enum code as PK). ## Development Commands (Once Set Up) @@ -1209,6 +1211,7 @@ For complete implementation with all utilities (PKCE generator, Cidaas API clien - **PRD:** See `docs/PRD.md` for complete requirements - **Tech Stack:** See `docs/TECH_STACK.md` for technology decisions - **Architecture:** See `docs/ARCHITECTURE.md` for system design and data flows +- **Roles:** See `docs/ROLES.md` for complete role system documentation (auto-assignment, visibility rules, API endpoints, code patterns) - **experimenta Info:** See `docs/EXPERIMENTA_INFO.md` for company information (address, opening hours, legal links, contact details) diff --git a/docs/PRD.md b/docs/PRD.md index baeaa5a..b569eb9 100644 --- a/docs/PRD.md +++ b/docs/PRD.md @@ -75,6 +75,8 @@ Eine spezialisierte E-Commerce-App, die es Besuchern des experimenta Science Cen - Multi-Payment-Provider - Laborkurse +**📖 Detaillierte Dokumentation:** Siehe [`docs/ROLES.md`](./ROLES.md) für vollständige Informationen zum Rollen-System (Auto-Assignment, Produktsichtbarkeit, Menüpunkt-Filterung, API-Endpoints, Code-Beispiele). + --- ## 3. Zielgruppen diff --git a/docs/ROLES.md b/docs/ROLES.md new file mode 100644 index 0000000..33e3aab --- /dev/null +++ b/docs/ROLES.md @@ -0,0 +1,524 @@ +# 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 + +```typescript +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: + +```typescript +// 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 `private` role + +--- + +## 3. Rollen-basierte Produktsichtbarkeit + +### 3.1 Konzept + +Produkte sind NUR sichtbar, wenn: +1. Das Produkt `product_role_visibility` Einträge hat +2. Der User eine genehmigte (`approved`) Rolle hat, die in `product_role_visibility` vorkommt + +**Opt-in Visibility:** +- Produkt ohne `product_role_visibility` Einträge → Unsichtbar für ALLE +- User ohne genehmigte Rollen → Sieht KEINE Produkte + +### 3.2 Database Schema + +```sql +-- 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: + +```typescript +// 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 + +```typescript +// 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 Rollen +- `roleVisibility: ['educator']` → Nur für Pädagogen +- `roleVisibility: ['educator', 'company']` → Für mehrere Rollen + +### 4.2 Implementation (AreaTabs.vue) + +```typescript +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() + +```typescript +// app/composables/useActiveRole.ts +export function useActiveRole() { + const activeRole = useState('activeRole', () => 'private') + const roles = useState('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: + +```typescript +const { loggedIn } = useUserSession() +if (loggedIn.value) { + callOnce('init-roles', () => fetchRoleStatus()) +} +``` + +--- + +## 6. Server-Side Utilities + +### 6.1 Wichtige Functions + +```typescript +// 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: + +```typescript +// 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:** +```json +{ + "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:** +```json +{ + "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: + +```vue + + +``` + +**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 + +```typescript +// 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 `private` role +- ✅ Sieht nur Produkte mit `private` role +- ✅ Sieht Navigation: Start, Makerspace, experimenta + +**Scenario 2: Pädagoge Login** +- ✅ User hat `private` + `educator` roles +- ✅ 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: + +```typescript +// 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 + +1. **Database:** Füge neue Rolle zu `roles` table hinzu +2. **Type:** Update `RoleCode` type in schema +3. **Mapping:** Update `categoryRoleMapping` in `server/utils/roles.ts` +4. **UI:** Update RoleSwitcher display names +5. **Navigation:** Update `roleVisibility` in AreaTabs.vue + +### 12.2 Beim Hinzufügen neuer Produkte + +1. **Category:** Definiere eine klare Kategorie +2. **Mapping:** Füge Kategorie zu `categoryRoleMapping` hinzu +3. **Visibility:** Produkt wird automatisch Rollen zugeordnet beim Import + +### 12.3 Beim Hinzufügen neuer Navigation-Items + +```typescript +{ + id: 'new-tab', + label: 'Neuer Tab', + roleVisibility: 'all' // oder ['educator', 'company'] +} +``` + +--- + +## 13. Troubleshooting + +### Problem: User sieht keine Produkte + +**Prüfen:** +1. Hat User eine approved Rolle? → `getUserApprovedRoleCodes(userId)` +2. Hat Produkt `product_role_visibility` Einträge? +3. Matchen Rolle und Produkt-Visibility? + +### Problem: Menüpunkt erscheint nicht nach Rollenwechsel + +**Prüfen:** +1. Ist `roleVisibility` korrekt gesetzt? +2. Ist `activeRole` korrekt aktualisiert? → Vue DevTools +3. Ist computed property `visibleAreas` reaktiv? + +### Problem: Produkt bleibt sichtbar nach Rollenwechsel + +**Prüfen:** +1. Wurde `refreshNuxtData()` aufgerufen in `switchRole()`? +2. Sind die richtigen keys verwendet? (`products-list`, etc.) +3. 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 |