Initial commit: Lageplan v1.0 - Next.js 15.5, React 19

This commit is contained in:
Pepe Ziberi
2026-02-21 11:57:44 +01:00
commit adf3dc8c1d
167 changed files with 34265 additions and 0 deletions

414
prisma/schema.prisma Normal file
View File

@@ -0,0 +1,414 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum Role {
SERVER_ADMIN
TENANT_ADMIN
OPERATOR
VIEWER
}
enum IconType {
STANDARD
RETTUNG
GEFAHRSTOFF
FEUER
WASSER
FAHRZEUG
}
enum ItemKind {
SYMBOL
LINE
POLYGON
RECTANGLE
CIRCLE
ARROW
TEXT
}
enum SubscriptionPlan {
FREE
PRO
}
enum DictionaryScope {
GLOBAL
TENANT
}
enum SubscriptionStatus {
ACTIVE
TRIAL
SUSPENDED
EXPIRED
CANCELLED
}
model Tenant {
id String @id @default(uuid())
name String
slug String @unique
description String?
isActive Boolean @default(true)
contactEmail String?
contactPhone String?
address String?
logoUrl String?
logoFileKey String?
// Subscription
plan SubscriptionPlan @default(FREE)
subscriptionStatus SubscriptionStatus @default(ACTIVE)
trialEndsAt DateTime?
subscriptionEndsAt DateTime?
maxUsers Int @default(5)
maxProjects Int @default(10)
notes String?
hiddenIconIds String[] @default([])
journalSuggestions String[] @default([])
// Privacy consent
privacyAccepted Boolean @default(false)
privacyAcceptedAt DateTime?
adminAccessAccepted Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
memberships TenantMembership[]
projects Project[]
hoseTypes HoseType[]
checkTemplates JournalCheckTemplate[]
iconCategories IconCategory[]
iconAssets IconAsset[]
upgradeRequests UpgradeRequest[]
dictionaryEntries DictionaryEntry[]
rapports Rapport[]
@@map("tenants")
}
// ─── System Settings (Key-Value, encrypted for secrets) ──────
model SystemSetting {
id String @id @default(uuid())
key String @unique
value String
isSecret Boolean @default(false)
category String @default("general")
updatedAt DateTime @updatedAt
@@map("system_settings")
}
model User {
id String @id @default(uuid())
email String @unique
password String
name String
role Role @default(OPERATOR)
emailVerified Boolean @default(true)
emailVerificationToken String? @unique
resetToken String? @unique
resetTokenExpiry DateTime?
lastLoginAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
memberships TenantMembership[]
projects Project[]
iconAssets IconAsset[]
upgradeRequests UpgradeRequest[]
rapports Rapport[]
@@map("users")
}
model TenantMembership {
id String @id @default(uuid())
role Role @default(OPERATOR)
createdAt DateTime @default(now())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
tenantId String
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
@@unique([userId, tenantId])
@@map("tenant_memberships")
}
model Project {
id String @id @default(uuid())
einsatzNr String?
title String
location String?
description String?
einsatzleiter String?
journalfuehrer String?
mapCenter Json @default("{\"lng\": 8.5417, \"lat\": 47.3769}")
mapZoom Float @default(15)
isLocked Boolean @default(false)
// Live editing lock (session-based for same-account multi-device)
editingById String?
editingUserName String?
editingSessionId String?
editingStartedAt DateTime?
editingHeartbeat DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
ownerId String?
owner User? @relation(fields: [ownerId], references: [id], onDelete: SetNull)
tenantId String?
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)
features Feature[]
items Item[]
journalEntries JournalEntry[]
journalCheckItems JournalCheckItem[]
journalPendenzen JournalPendenz[]
rapports Rapport[]
@@map("projects")
}
model Feature {
id String @id @default(uuid())
type String
geometry Json
properties Json @default("{}")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
@@map("features")
}
model IconCategory {
id String @id @default(uuid())
name String
description String?
sortOrder Int @default(0)
isGlobal Boolean @default(false)
createdAt DateTime @default(now())
tenantId String?
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)
icons IconAsset[]
@@map("icon_categories")
}
model IconAsset {
id String @id @default(uuid())
name String
fileKey String
mimeType String
width Int?
height Int?
isSystem Boolean @default(false)
isActive Boolean @default(true)
iconType IconType @default(STANDARD)
tags String[] @default([])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
categoryId String?
category IconCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
ownerId String?
owner User? @relation(fields: [ownerId], references: [id], onDelete: SetNull)
tenantId String?
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)
@@map("icon_assets")
}
model HoseType {
id String @id @default(uuid())
name String @unique
diameterMm Int
lengthPerPieceM Int @default(10)
flowRateLpm Float
frictionCoeff Float
description String?
isDefault Boolean @default(false)
isActive Boolean @default(true)
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String?
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)
@@map("hose_types")
}
// ─── Journal ───────────────────────────────────────────────
model JournalEntry {
id String @id @default(uuid())
time DateTime @default(now())
what String
who String?
done Boolean @default(false)
doneAt DateTime?
sortOrder Int @default(0)
isCorrected Boolean @default(false)
correctionOfId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
@@map("journal_entries")
}
model JournalCheckItem {
id String @id @default(uuid())
label String
confirmed Boolean @default(false)
confirmedAt DateTime?
ok Boolean @default(false)
okAt DateTime?
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
@@map("journal_check_items")
}
model JournalPendenz {
id String @id @default(uuid())
what String
who String?
whenHow String?
done Boolean @default(false)
doneAt DateTime?
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
@@map("journal_pendenzen")
}
model JournalCheckTemplate {
id String @id @default(uuid())
label String
sortOrder Int @default(0)
isActive Boolean @default(true)
createdAt DateTime @default(now())
tenantId String?
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)
@@map("journal_check_templates")
}
model Item {
id String @id @default(uuid())
kind ItemKind
geometry Json
style Json @default("{}")
properties Json @default("{}")
isVisible Boolean @default(true)
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
iconId String?
@@map("items")
}
// ─── Upgrade Requests ─────────────────────────────────────
enum UpgradeRequestStatus {
PENDING
APPROVED
REJECTED
}
model UpgradeRequest {
id String @id @default(uuid())
requestedPlan SubscriptionPlan
currentPlan SubscriptionPlan
message String?
status UpgradeRequestStatus @default(PENDING)
adminNote String?
processedAt DateTime?
processedById String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
requestedById String
requestedBy User @relation(fields: [requestedById], references: [id], onDelete: Cascade)
@@map("upgrade_requests")
}
// ─── Dictionary (Global + Tenant word library) ────────────
model DictionaryEntry {
id String @id @default(uuid())
word String
scope DictionaryScope @default(GLOBAL)
createdAt DateTime @default(now())
tenantId String?
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade)
@@unique([word, tenantId])
@@map("dictionary_entries")
}
// ─── Rapport (PDF reports with public token access) ───────
model Rapport {
id String @id @default(uuid())
reportNumber String
token String @unique @default(uuid())
data Json
generatedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
tenantId String?
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade)
createdById String?
createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull)
@@map("rapports")
}