docs: Experten Profile wieder in Scope — eigenständiger Menüpunkt, RBAC expertprofile, vollständiges Schema

This commit is contained in:
Thomas Reitz 2026-03-15 19:24:19 +01:00
parent 37899d0399
commit 6368596973

View file

@ -67,30 +67,78 @@ Folgende Module kannst du 1:1 oder minimal angepasst übernehmen:
| MVP-Feature | Grund |
|---|---|
| `ExpertProfile` (Skills, Erfahrung, Sprachen) | IT-Berater-Profil für MVP — kein Teil von INSIGHT |
| `ExpertProject` (Projekthistorie) | IT-Berater-Profil für MVP — kein Teil von INSIGHT |
| `ExpertCertification` (Zertifikate) | IT-Berater-Profil für MVP — kein Teil von INSIGHT |
| `ExpertAttachment` (PDF/Word Export) | IT-Berater-Profil für MVP — kein Teil von INSIGHT |
| `ProfileAccessGroup` (Profilzugriff) | IT-Berater-spezifisch — kein Teil von INSIGHT |
| `MasterData` (Departments, Locations, etc.) | MVP-spezifisch — kein Teil von INSIGHT |
| Tab "Experten Profil" im Profil | IT-Berater-Feature — kein Teil von INSIGHT |
| `ProfileAccessGroup` (Gruppen-basierter Profilzugriff) | Wird durch RBAC `moduleRoles.expertprofile` ersetzt |
| `MasterData` (Departments, Locations, CostCenters, etc.) | MVP-spezifisch — kein Teil von INSIGHT |
| `AdminProfileAccessPage`, `AdminMasterDataPage` | MVP-spezifisch — kein Teil von INSIGHT |
**Folgende MVP-Features werden übernommen (inkl. Profil-Seite):**
**Folgende MVP-Features werden übernommen:**
| Feature | Details |
|---|---|
| **Profil-Seite** | Name, Avatar, Kontakt, Adresse, Organisation (4 Felder) |
| **Profil-Seite** | Tabs: Profil \| Experten Profil \| Passwort ändern \| 2FA \| Integrationen |
| **"O365 übernehmen"** Button | Einmaliger manueller Import aller Felder aus M365 |
| **Profil-Sync beim Login** | Automatisch: Name, Jobitel, Department, Company aus M365 aktualisieren |
| **Profil-Sync beim Login** | Automatisch: Name, Jobtitel, Department, Company aus M365 |
| **Passwort ändern** | Eigenes Passwort mit Verifikation des aktuellen Passworts |
| **2FA Tab** | TOTP aktivieren/deaktivieren (nur für lokale Auth-User) |
| **Integrationen Tab** | Microsoft 365: verbinden, Status anzeigen, trennen |
| **Experten Profil Tab** (eigenes) | User bearbeitet sein eigenes Experten Profil |
| **ExpertProfile Datenmodell** | Skills, Sprachen, Erfahrung, Projekthistorie, Zertifikate, Anlagen |
| **PDF / Word Export** | Eigenes Profil exportieren |
> **Hintergrund Profil-Sync:** Wenn ein User sich via MS SSO einloggt, werden Name, JobTitle,
> Department, CompanyName, OfficeLocation automatisch aus dem M365-Token aktualisiert.
> Der "O365 übernehmen"-Button ermöglicht zusätzlich den manuellen Sync (Graph API `/me`).
> Das setzt voraus, dass MS SSO konfiguriert ist — ohne SSO nur lokales Profil.
---
## Experten Profile — Eigenständiger Menüpunkt
**"Experten Profile" ist ein eigener Menüpunkt in der Hauptnavigation** (nicht nur ein persönlicher Tab).
### Was er leistet:
| Aktion | Wer darf das |
|---|---|
| Eigenes Experten Profil bearbeiten | Jeder eingeloggte User (im Tab "Experten Profil" seiner Profil-Seite) |
| Alle Profile auflisten und **ansehen** | `moduleRoles.expertprofile: "VIEWER"` oder höher |
| Profile anderer User **bearbeiten** | `moduleRoles.expertprofile: "EDITOR"` oder höher |
| Profile **exportieren** (PDF/Word) | `moduleRoles.expertprofile: "EDITOR"` oder höher |
| Alles + Profil-Zuweisung verwalten | `moduleRoles.expertprofile: "ADMIN"` oder `platformRole: PLATFORM_ADMIN` |
### RBAC für Experten Profile:
```
moduleRoles.expertprofile:
"ADMIN" → vollständiger Zugang, kann alle Profile bearbeiten + exportieren
"EDITOR" → kann alle Profile bearbeiten + exportieren
"VIEWER" → kann alle Profile nur ansehen (kein Export, kein Bearbeiten)
(nicht gesetzt) → nur eigenes Profil über persönliche Profil-Seite
```
### UI-Konzept:
```
Navigation Sidebar:
└── 📋 Experten Profile (sichtbar für User mit expertprofile-Rolle)
├── Liste aller User mit Profilvorschau
├── Klick auf User → vollständiges Profil ansehen
├── [Bearbeiten]-Button (nur für EDITOR/ADMIN)
└── [PDF Export] / [Word Export] (nur für EDITOR/ADMIN)
```
### MVP-Referenzdateien für Experten Profile:
| MVP-Datei | Für INSIGHT nutzen |
|---|---|
| `packages/core-service/src/core/expert-profile/expert-profile.service.ts` | Experten Profil CRUD |
| `packages/core-service/src/core/expert-profile/expert-profile.controller.ts` | Endpoints |
| `packages/core-service/src/core/expert-profile/profile-export.service.ts` | PDF/Word Export |
| `packages/core-service/prisma/core.schema.prisma` (ExpertProfile-Modelle) | Datenmodell |
| `packages/frontend/src/profile/ProfilePage.tsx` | Eigenes Profil + Experten-Profil-Tab |
**Anpassung gegenüber MVP:** Im MVP war der Profilzugriff über `ProfileAccessGroup` geregelt
(Gruppen mit `canView`, `canEdit`, `canExport`). In INSIGHT wird das durch
`moduleRoles.expertprofile` ersetzt — einfacher, konsistent mit dem restlichen RBAC-System.
---
@ -155,6 +203,7 @@ model User {
authProvider AuthProvider[]
integrations UserIntegration[]
auditLogs AuditLog[]
expertProfile ExpertProfile?
@@map("users")
}
@ -242,6 +291,106 @@ model AuditLog {
@@index([createdAt])
@@map("audit_logs")
}
// --------------------------------------------------------
// ExpertProfile - Experten-Profil (1:1 mit User)
// --------------------------------------------------------
model ExpertProfile {
id String @id @default(uuid()) @db.Uuid
userId String @unique @map("user_id") @db.Uuid
skills String[] @default([])
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
experiences ExpertExperience[]
languages ExpertLanguage[]
projects ExpertProject[]
certifications ExpertCertification[]
attachments ExpertAttachment[]
@@map("expert_profiles")
}
model ExpertExperience {
id String @id @default(uuid()) @db.Uuid
expertProfileId String @map("expert_profile_id") @db.Uuid
area String @db.VarChar(200)
years Int
level String? @db.VarChar(50)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
@@map("expert_experiences")
}
model ExpertLanguage {
id String @id @default(uuid()) @db.Uuid
expertProfileId String @map("expert_profile_id") @db.Uuid
language String @db.VarChar(100)
level String @db.VarChar(20) // Muttersprache, C2, C1, B2, B1, A2, A1
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
@@map("expert_languages")
}
model ExpertProject {
id String @id @default(uuid()) @db.Uuid
expertProfileId String @map("expert_profile_id") @db.Uuid
fromMonth Int @map("from_month")
fromYear Int @map("from_year")
toMonth Int? @map("to_month")
toYear Int? @map("to_year")
isCurrent Boolean @default(false) @map("is_current")
role String @db.VarChar(200)
tasks String? @db.Text
company String? @db.VarChar(200)
companySize String? @map("company_size") @db.VarChar(20)
industry String? @db.VarChar(200)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
@@index([expertProfileId, fromYear, fromMonth])
@@map("expert_projects")
}
model ExpertCertification {
id String @id @default(uuid()) @db.Uuid
expertProfileId String @map("expert_profile_id") @db.Uuid
title String @db.VarChar(300)
issuingBody String @map("issuing_body") @db.VarChar(300)
website String? @db.VarChar(500)
issueYear Int @map("issue_year")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
@@map("expert_certifications")
}
model ExpertAttachment {
id String @id @default(uuid()) @db.Uuid
expertProfileId String @map("expert_profile_id") @db.Uuid
filename String @db.VarChar(255)
mimetype String @db.VarChar(100)
size Int
data String @db.Text // Base64
createdAt DateTime @default(now()) @map("created_at")
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
@@map("expert_attachments")
}
```
---
@ -250,15 +399,26 @@ model AuditLog {
```
User.platformRole:
PLATFORM_ADMIN → Zugang zum kompletten Admin-Bereich
PLATFORM_ADMIN → Zugang zum kompletten Admin-Bereich + alle Experten Profile
USER → Normaler Nutzer
User.moduleRoles (JSON):
User.moduleRoles (JSON-Objekt mit Rollen pro Modul):
Experten Profile:
{ "expertprofile": "ADMIN" } → Alle Profile ansehen + bearbeiten + exportieren
{ "expertprofile": "EDITOR" } → Alle Profile ansehen + bearbeiten + exportieren
{ "expertprofile": "VIEWER" } → Alle Profile nur ansehen (kein Export, kein Bearbeiten)
(nicht gesetzt) → Nur eigenes Profil über persönliche Profil-Seite
CRM (Phase 3):
{ "crm": "ADMIN" } → Admin im CRM-Modul
{ "crm": "MANAGER" } → Manager im CRM-Modul
{ "crm": "USER" } → Normaler CRM-Nutzer
{ "crm": "VIEWER" } → Nur lesen
{} → Kein Zugang zu CRM
(nicht gesetzt) → Kein Zugang zu CRM
Kombiniert möglich:
{ "expertprofile": "EDITOR", "crm": "MANAGER" }
```
**Im JWT-Token** werden beide Felder mitgegeben:
@ -267,7 +427,7 @@ User.moduleRoles (JSON):
"sub": "uuid",
"email": "user@firma.de",
"platformRole": "USER",
"moduleRoles": { "crm": "MANAGER" },
"moduleRoles": { "expertprofile": "EDITOR", "crm": "MANAGER" },
"jti": "uuid",
"iat": 1234567890,
"exp": 1234568790
@ -322,7 +482,13 @@ npx prisma migrate dev --name init
5. `core/auth/sso/` — EntraIdService, SsoController (aus MVP 1:1 übernehmen)
6. `core/users/` — UsersService, UsersController (aus MVP, `moduleRoles` statt `tenantRole`)
7. `core/settings/` — SettingsController (aus MVP, External Links + Branding + Company + SSL + AI-Config)
8. `core/modules/` — NEU: ModuleRegistryService (Module aus DB laden, User-Rollen-Zuweisung)
8. `core/integrations/` — IntegrationsService, IntegrationsController (aus MVP, M365 OAuth2 Tokens)
9. `core/expert-profile/` — ExpertProfileService, ExpertProfileController, ProfileExportService (aus MVP)
- Eigenes Profil bearbeiten: alle authentifizierten User
- Fremde Profile lesen: `moduleRoles.expertprofile` = VIEWER/EDITOR/ADMIN
- Fremde Profile bearbeiten: `moduleRoles.expertprofile` = EDITOR/ADMIN
- Export: `moduleRoles.expertprofile` = EDITOR/ADMIN
10. `core/modules/` — NEU: ModuleRegistryService (Module aus DB laden, User-Rollen-Zuweisung)
**4. JWT RS256 Keys generieren:**
```bash
@ -372,21 +538,40 @@ npm install react-hot-toast @heroicons/react
4. `shell/App.tsx` — Routing-Struktur (ohne CRM-Routen — die kommen Phase 3)
5. `shell/AppLayout.tsx` — Haupt-Layout (Sidebar + Topbar + Outlet)
6. `shell/DashboardPage.tsx` — Leeres Dashboard mit Widget-Platzhaltern
7. `profile/ProfilePage.tsx` — Eigenes Profil (aus MVP)
8. `admin/AdminLayout.tsx` — Admin-Navigation (aus MVP, CRM-Settings entfernen)
9. `admin/AdminUsersPage.tsx` — User-Verwaltung + Rollen-Zuweisung (aus MVP erweitern)
10. `admin/AdminSsoPage.tsx` — SSO-Konfiguration (aus MVP 1:1)
11. `admin/AdminCustomizePage.tsx` — Branding (aus MVP 1:1)
12. `admin/AdminExternalLinksPage.tsx` — Externe Links (aus MVP 1:1)
13. `admin/AdminCompanyPage.tsx` — Firmendaten (aus MVP 1:1)
14. `admin/AdminSslPage.tsx` — SSL/Domain (aus MVP 1:1)
15. `admin/AdminAiSettingsPage.tsx` — KI-Einstellungen (aus MVP 1:1)
16. `admin/AdminModulesPage.tsx` — NEU: Modul-Registry (welche Module aktiv)
7. `profile/ProfilePage.tsx` — Eigenes Profil mit Tabs: Profil | Experten Profil | Passwort | 2FA | Integrationen (aus MVP)
8. `expert-profiles/ExpertProfilesPage.tsx`**NEU**: Menüpunkt "Experten Profile" (Liste aller User-Profile, permission-gesteuert)
9. `expert-profiles/ExpertProfileDetailPage.tsx`**NEU**: Einzelnes Profil ansehen + bearbeiten (EDITOR/ADMIN) + exportieren
10. `admin/AdminLayout.tsx` — Admin-Navigation (aus MVP)
11. `admin/AdminUsersPage.tsx` — User-Verwaltung + Rollen-Zuweisung (aus MVP erweitern)
12. `admin/AdminSsoPage.tsx` — SSO-Konfiguration (aus MVP 1:1)
13. `admin/AdminCustomizePage.tsx` — Branding (aus MVP 1:1)
14. `admin/AdminExternalLinksPage.tsx` — Externe Links (aus MVP 1:1)
15. `admin/AdminCompanyPage.tsx` — Firmendaten (aus MVP 1:1)
16. `admin/AdminSslPage.tsx` — SSL/Domain (aus MVP 1:1)
17. `admin/AdminAiSettingsPage.tsx` — KI-Einstellungen (aus MVP 1:1)
18. `admin/AdminModulesPage.tsx`**NEU**: Modul-Registry (welche Module aktiv, Rollen-Konfiguration)
**Navigation Sidebar — Hauptmenü:**
```
🏠 Dashboard
📋 Experten Profile ← nur sichtbar wenn moduleRoles.expertprofile gesetzt
── Admin-Bereich ── ← nur sichtbar für PLATFORM_ADMIN
⚙️ Benutzer
⚙️ SSO / Sicherheit
⚙️ Erscheinungsbild
⚙️ Externe Links
⚙️ Firmendaten
⚙️ SSL / Domain
⚙️ KI-Einstellungen
⚙️ Module
```
**Wichtig für Admin-User-Management (NEU gegenüber MVP):**
- User-Liste zeigt: Name, E-Mail, platformRole, moduleRoles
- PLATFORM_ADMIN kann platformRole ändern
- PLATFORM_ADMIN kann moduleRoles pro Modul zuweisen (Dropdown: ADMIN/MANAGER/USER/VIEWER/Kein Zugang)
- PLATFORM_ADMIN kann moduleRoles pro Modul zuweisen:
- expertprofile: ADMIN / EDITOR / VIEWER / (kein Zugang)
- crm: ADMIN / MANAGER / USER / VIEWER / (kein Zugang) ← Phase 3, schon vorbereiten
---