generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ─── Multi-Tenancy ──────────────────────────────────────────── model Tenant { id String @id @default(uuid()) @db.Uuid name String slug String @unique createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") hostnames TenantHostname[] users User[] boards Board[] tags Tag[] categories Category[] boardTemplates BoardTemplate[] theme TenantTheme? @@map("tenants") } model TenantHostname { id String @id @default(uuid()) @db.Uuid hostname String @unique tenantId String @map("tenant_id") @db.Uuid isPrimary Boolean @default(false) @map("is_primary") createdAt DateTime @default(now()) @map("created_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) @@map("tenant_hostnames") } // ─── Users & Auth ───────────────────────────────────────────── enum GlobalRole { SITE_ADMIN USER } enum TenantRole { ADMIN MEMBER } model User { id String @id @default(uuid()) @db.Uuid tenantId String @map("tenant_id") @db.Uuid email String name String passwordHash String @map("password_hash") globalRole GlobalRole @default(USER) @map("global_role") tenantRole TenantRole @default(MEMBER) @map("tenant_role") avatarUrl String? @map("avatar_url") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) sessions Session[] boardMembers BoardMember[] cardAssignees CardAssignee[] comments Comment[] activities Activity[] notifications Notification[] @@unique([tenantId, email]) @@map("users") } model Session { id String @id @default(uuid()) @db.Uuid userId String @map("user_id") @db.Uuid expiresAt DateTime @map("expires_at") createdAt DateTime @default(now()) @map("created_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("sessions") } // ─── Boards ─────────────────────────────────────────────────── enum BoardVisibility { PUBLIC PRIVATE } enum BoardMemberRole { OWNER EDITOR VIEWER } model Board { id String @id @default(uuid()) @db.Uuid tenantId String @map("tenant_id") @db.Uuid title String description String? visibility BoardVisibility @default(PRIVATE) background String? categoryId String? @map("category_id") @db.Uuid archived Boolean @default(false) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull) columns Column[] members BoardMember[] activities Activity[] @@map("boards") } model BoardMember { id String @id @default(uuid()) @db.Uuid boardId String @map("board_id") @db.Uuid userId String @map("user_id") @db.Uuid role BoardMemberRole @default(EDITOR) board Board @relation(fields: [boardId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([boardId, userId]) @@map("board_members") } // ─── Columns ────────────────────────────────────────────────── model Column { id String @id @default(uuid()) @db.Uuid boardId String @map("board_id") @db.Uuid title String position String // Fractional index (lexicographic) board Board @relation(fields: [boardId], references: [id], onDelete: Cascade) cards Card[] @@map("columns") } // ─── Cards ──────────────────────────────────────────────────── model Card { id String @id @default(uuid()) @db.Uuid columnId String @map("column_id") @db.Uuid title String description String? position String // Fractional index (lexicographic) dueDate DateTime? @map("due_date") archived Boolean @default(false) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") column Column @relation(fields: [columnId], references: [id], onDelete: Cascade) assignees CardAssignee[] labels CardLabel[] checklists Checklist[] comments Comment[] attachments Attachment[] activities Activity[] @@map("cards") } model CardAssignee { id String @id @default(uuid()) @db.Uuid cardId String @map("card_id") @db.Uuid userId String @map("user_id") @db.Uuid card Card @relation(fields: [cardId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([cardId, userId]) @@map("card_assignees") } // ─── Tags & Labels ──────────────────────────────────────────── model Tag { id String @id @default(uuid()) @db.Uuid tenantId String @map("tenant_id") @db.Uuid name String color String // hex color tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) cards CardLabel[] @@unique([tenantId, name]) @@map("tags") } model CardLabel { id String @id @default(uuid()) @db.Uuid cardId String @map("card_id") @db.Uuid tagId String @map("tag_id") @db.Uuid card Card @relation(fields: [cardId], references: [id], onDelete: Cascade) tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade) @@unique([cardId, tagId]) @@map("card_labels") } // ─── Categories ─────────────────────────────────────────────── model Category { id String @id @default(uuid()) @db.Uuid tenantId String @map("tenant_id") @db.Uuid name String tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) boards Board[] @@unique([tenantId, name]) @@map("categories") } // ─── Checklists ─────────────────────────────────────────────── model Checklist { id String @id @default(uuid()) @db.Uuid cardId String @map("card_id") @db.Uuid title String position String card Card @relation(fields: [cardId], references: [id], onDelete: Cascade) items ChecklistItem[] @@map("checklists") } model ChecklistItem { id String @id @default(uuid()) @db.Uuid checklistId String @map("checklist_id") @db.Uuid title String completed Boolean @default(false) position String checklist Checklist @relation(fields: [checklistId], references: [id], onDelete: Cascade) @@map("checklist_items") } // ─── Comments ───────────────────────────────────────────────── model Comment { id String @id @default(uuid()) @db.Uuid cardId String @map("card_id") @db.Uuid userId String @map("user_id") @db.Uuid content String createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") card Card @relation(fields: [cardId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("comments") } // ─── Attachments ────────────────────────────────────────────── model Attachment { id String @id @default(uuid()) @db.Uuid cardId String @map("card_id") @db.Uuid filename String filepath String mimetype String size Int createdAt DateTime @default(now()) @map("created_at") card Card @relation(fields: [cardId], references: [id], onDelete: Cascade) @@map("attachments") } // ─── Activity Log ───────────────────────────────────────────── model Activity { id String @id @default(uuid()) @db.Uuid boardId String @map("board_id") @db.Uuid cardId String? @map("card_id") @db.Uuid userId String @map("user_id") @db.Uuid action String // e.g. "card.created", "card.moved", "comment.added" metadata Json? // additional context createdAt DateTime @default(now()) @map("created_at") board Board @relation(fields: [boardId], references: [id], onDelete: Cascade) card Card? @relation(fields: [cardId], references: [id], onDelete: SetNull) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("activities") } // ─── Notifications ──────────────────────────────────────────── model Notification { id String @id @default(uuid()) @db.Uuid userId String @map("user_id") @db.Uuid type String // e.g. "assigned", "mentioned", "due_soon" title String body String? link String? read Boolean @default(false) createdAt DateTime @default(now()) @map("created_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("notifications") } // ─── Board Templates ────────────────────────────────────────── model BoardTemplate { id String @id @default(uuid()) @db.Uuid tenantId String? @map("tenant_id") @db.Uuid // null = system template name String description String? template Json // JSON structure with columns/cards tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) @@map("board_templates") } // ─── Tenant Branding & Theming ─────────────────────────────── model TenantTheme { id String @id @default(uuid()) @db.Uuid tenantId String @unique @map("tenant_id") @db.Uuid // Light mode lightPageBg String? @map("light_page_bg") lightPageBgImage String? @map("light_page_bg_image") lightNavBg String? @map("light_nav_bg") lightNavBgImage String? @map("light_nav_bg_image") lightColumnBg String? @map("light_column_bg") lightColumnBorder String? @map("light_column_border") lightColumnText String? @map("light_column_text") lightColumnOpacity Float? @map("light_column_opacity") lightCardBg String? @map("light_card_bg") lightCardBorder String? @map("light_card_border") lightCardText String? @map("light_card_text") lightCardOpacity Float? @map("light_card_opacity") lightCardRadius String? @map("light_card_radius") lightPrimary String? @map("light_primary") lightPrimaryHover String? @map("light_primary_hover") lightDanger String? @map("light_danger") lightDangerHover String? @map("light_danger_hover") lightSuccess String? @map("light_success") lightWarning String? @map("light_warning") // Dark mode darkPageBg String? @map("dark_page_bg") darkPageBgImage String? @map("dark_page_bg_image") darkNavBg String? @map("dark_nav_bg") darkNavBgImage String? @map("dark_nav_bg_image") darkColumnBg String? @map("dark_column_bg") darkColumnBorder String? @map("dark_column_border") darkColumnText String? @map("dark_column_text") darkColumnOpacity Float? @map("dark_column_opacity") darkCardBg String? @map("dark_card_bg") darkCardBorder String? @map("dark_card_border") darkCardText String? @map("dark_card_text") darkCardOpacity Float? @map("dark_card_opacity") darkCardRadius String? @map("dark_card_radius") darkPrimary String? @map("dark_primary") darkPrimaryHover String? @map("dark_primary_hover") darkDanger String? @map("dark_danger") darkDangerHover String? @map("dark_danger_hover") darkSuccess String? @map("dark_success") darkWarning String? @map("dark_warning") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) @@map("tenant_themes") }