# 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<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:

```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
<!-- 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

```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 |
