mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-24 23:56:40 +02:00
Full-stack implementation of the Expert Profile tab with 6 sections: - Skills (tag/chip UI with add/remove) - Experience (area, years, optional level) - Languages (language + proficiency level) - Project History (modal form with dates, role, tasks, company details) - Certifications (modal form with title, issuer, website, year) - Attachments (file upload/download as Base64, max 10MB) Backend: 15 API endpoints, 8 DTOs, full CRUD service with ownership verification. Frontend: Reusable Modal component (React Portal), ExpertProfileTab orchestrator, 8 section components. Database: 6 new tables (expert_profiles, expert_experiences, expert_languages, expert_projects, expert_certifications, expert_attachments). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
327 lines
12 KiB
Text
327 lines
12 KiB
Text
// ============================================================
|
|
// INSIGHT MVP - Core Schema (platform_core Datenbank)
|
|
// ============================================================
|
|
// Zentrale Plattform-Tabellen: Users, Tenants, Auth, Modules
|
|
// Kein raw SQL - nur Prisma!
|
|
// ============================================================
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
directUrl = env("DATABASE_URL_DIRECT")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// User - Plattform-Benutzer
|
|
// --------------------------------------------------------
|
|
model User {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
email String @unique @db.VarChar(255)
|
|
firstName String @map("first_name") @db.VarChar(100)
|
|
lastName String @map("last_name") @db.VarChar(100)
|
|
avatar String? @db.Text // Profilbild als Base64 Data-URL
|
|
|
|
// Kontaktdaten
|
|
phone String? @map("phone") @db.VarChar(30)
|
|
mobile String? @map("mobile") @db.VarChar(30)
|
|
|
|
// Adresse
|
|
street String? @map("street") @db.VarChar(200)
|
|
postalCode String? @map("postal_code") @db.VarChar(10)
|
|
city String? @map("city") @db.VarChar(100)
|
|
|
|
role String @default("USER") @db.VarChar(50) // PLATFORM_ADMIN, TENANT_ADMIN, USER
|
|
isActive Boolean @default(true) @map("is_active")
|
|
|
|
// 2FA
|
|
twoFactorEnabled Boolean @default(false) @map("two_factor_enabled")
|
|
|
|
// Login-Tracking
|
|
lastLogin DateTime? @map("last_login")
|
|
failedLoginAttempts Int @default(0) @map("failed_login_attempts")
|
|
lastFailedLogin DateTime? @map("last_failed_login")
|
|
|
|
// Timestamps
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
// Relationen
|
|
authProvider AuthProvider[]
|
|
tenantMemberships TenantMembership[]
|
|
auditLogs AuditLog[]
|
|
expertProfile ExpertProfile?
|
|
|
|
@@map("users")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// AuthProvider - Authentifizierungs-Provider pro User
|
|
// --------------------------------------------------------
|
|
// Unterstuetzt mehrere Auth-Methoden pro User:
|
|
// LOCAL (Passwort), MS_SSO (spaeter), M2M (Machine-to-Machine)
|
|
model AuthProvider {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String @map("user_id") @db.Uuid
|
|
provider String @db.VarChar(50) // LOCAL, MS_SSO, M2M
|
|
providerId String? @map("provider_id") @db.VarChar(255) // Externe ID (z.B. MS Object ID)
|
|
passwordHash String? @map("password_hash") @db.VarChar(255)
|
|
totpSecret String? @map("totp_secret") @db.VarChar(255) // Verschluesselt
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
// Relationen
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, provider])
|
|
@@map("auth_providers")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// Tenant - Mandant
|
|
// --------------------------------------------------------
|
|
model Tenant {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
name String @db.VarChar(200)
|
|
slug String @unique @db.VarChar(50) // URL-freundlich, fuer DB-Name
|
|
isActive Boolean @default(true) @map("is_active")
|
|
|
|
// Mandant-Einstellungen (JSON)
|
|
settings Json @default("{}")
|
|
|
|
// Timestamps
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
// Relationen
|
|
members TenantMembership[]
|
|
modules TenantModule[]
|
|
|
|
@@map("tenants")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// TenantMembership - User-Tenant-Zuordnung (M:N)
|
|
// --------------------------------------------------------
|
|
model TenantMembership {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String @map("user_id") @db.Uuid
|
|
tenantId String @map("tenant_id") @db.Uuid
|
|
tenantRole String @default("MEMBER") @map("tenant_role") @db.VarChar(50) // ADMIN, MEMBER, VIEWER
|
|
isActive Boolean @default(true) @map("is_active")
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
// Relationen
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, tenantId])
|
|
@@map("tenant_memberships")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// Module - Verfuegbare Plattform-Module
|
|
// --------------------------------------------------------
|
|
model Module {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
key String @unique @db.VarChar(50) // z.B. "crm", "project", "docs"
|
|
name String @db.VarChar(100)
|
|
description String? @db.Text
|
|
version String @default("1.0.0") @db.VarChar(20)
|
|
isActive Boolean @default(true) @map("is_active")
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
// Relationen
|
|
tenantModules TenantModule[]
|
|
|
|
@@map("modules")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// TenantModule - Welcher Tenant welche Module nutzt
|
|
// --------------------------------------------------------
|
|
model TenantModule {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
tenantId String @map("tenant_id") @db.Uuid
|
|
moduleId String @map("module_id") @db.Uuid
|
|
isActive Boolean @default(true) @map("is_active")
|
|
|
|
// Modul-spezifische Konfiguration pro Tenant
|
|
config Json @default("{}")
|
|
|
|
activatedAt DateTime @default(now()) @map("activated_at")
|
|
|
|
// Relationen
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
module Module @relation(fields: [moduleId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([tenantId, moduleId])
|
|
@@map("tenant_modules")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// AuditLog - Plattform-weites Audit-Log
|
|
// --------------------------------------------------------
|
|
model AuditLog {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String? @map("user_id") @db.Uuid
|
|
action String @db.VarChar(100) // z.B. "user.login", "tenant.create"
|
|
entity String @db.VarChar(100) // z.B. "User", "Tenant"
|
|
entityId String? @map("entity_id") @db.VarChar(255)
|
|
details Json? // Zusaetzliche Informationen
|
|
ipAddress String? @map("ip_address") @db.VarChar(45)
|
|
userAgent String? @map("user_agent") @db.Text
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
// Relationen
|
|
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
|
|
|
@@index([userId])
|
|
@@index([action])
|
|
@@index([entity, entityId])
|
|
@@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 als Tag-Array
|
|
skills String[] @default([])
|
|
|
|
// Timestamps
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
// Relationen
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
experiences ExpertExperience[]
|
|
languages ExpertLanguage[]
|
|
projects ExpertProject[]
|
|
certifications ExpertCertification[]
|
|
attachments ExpertAttachment[]
|
|
|
|
@@map("expert_profiles")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// ExpertExperience - Erfahrung / Expertise-Bereiche
|
|
// --------------------------------------------------------
|
|
model ExpertExperience {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
expertProfileId String @map("expert_profile_id") @db.Uuid
|
|
area String @db.VarChar(200) // z.B. "IT Infrastruktur"
|
|
years Int // Jahre Erfahrung
|
|
level String? @db.VarChar(50) // Experte, Fortgeschritten, Grundkenntnisse
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
// Relationen
|
|
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("expert_experiences")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// ExpertLanguage - Sprachen
|
|
// --------------------------------------------------------
|
|
model ExpertLanguage {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
expertProfileId String @map("expert_profile_id") @db.Uuid
|
|
language String @db.VarChar(100) // z.B. "Deutsch"
|
|
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")
|
|
|
|
// Relationen
|
|
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("expert_languages")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// ExpertProject - Projekthistorie
|
|
// --------------------------------------------------------
|
|
model ExpertProject {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
expertProfileId String @map("expert_profile_id") @db.Uuid
|
|
|
|
// Zeitraum
|
|
fromMonth Int @map("from_month") // 1-12
|
|
fromYear Int @map("from_year") // z.B. 2023
|
|
toMonth Int? @map("to_month") // null wenn isCurrent
|
|
toYear Int? @map("to_year")
|
|
isCurrent Boolean @default(false) @map("is_current")
|
|
|
|
// Details
|
|
role String @db.VarChar(200) // Taetigkeit
|
|
tasks String? @db.Text // Aufgaben (max 1500 Zeichen im DTO)
|
|
company String? @db.VarChar(200) // Firma
|
|
companySize String? @map("company_size") @db.VarChar(20) // "1-10", "11-50", etc.
|
|
industry String? @db.VarChar(200) // Branche
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
// Relationen
|
|
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([expertProfileId, fromYear, fromMonth])
|
|
@@map("expert_projects")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// ExpertCertification - Zertifizierungen
|
|
// --------------------------------------------------------
|
|
model ExpertCertification {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
expertProfileId String @map("expert_profile_id") @db.Uuid
|
|
|
|
title String @db.VarChar(300) // Titel
|
|
issuingBody String @map("issuing_body") @db.VarChar(300) // Zertifizierungsstelle
|
|
website String? @db.VarChar(500) // URL
|
|
issueYear Int @map("issue_year") // Ausstellungsjahr
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
// Relationen
|
|
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("expert_certifications")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// ExpertAttachment - Profilanlagen (Dateien als Base64)
|
|
// --------------------------------------------------------
|
|
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 // Groesse in Bytes
|
|
data String @db.Text // Base64-Daten
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
// Relationen
|
|
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("expert_attachments")
|
|
}
|