473 lines
13 KiB
Plaintext
473 lines
13 KiB
Plaintext
// This is your Prisma schema file,
|
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
binaryTargets = ["native", "linux-musl-arm64-openssl-3.0.x"]
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// ENUMS
|
|
// ─────────────────────────────────────────────
|
|
|
|
enum Role {
|
|
ADMIN
|
|
PRODUCER
|
|
SUPERVISOR
|
|
ARTIST
|
|
CLIENT
|
|
}
|
|
|
|
enum ProjectStatus {
|
|
ACTIVE
|
|
ON_HOLD
|
|
COMPLETED
|
|
ARCHIVED
|
|
}
|
|
|
|
enum ShotStatus {
|
|
WAITING
|
|
IN_PROGRESS
|
|
IN_REVIEW
|
|
REVISIONS
|
|
COMPLETE
|
|
}
|
|
|
|
enum ShotPriority {
|
|
LOW
|
|
NORMAL
|
|
HIGH
|
|
URGENT
|
|
}
|
|
|
|
enum ApprovalStatus {
|
|
PENDING_REVIEW
|
|
APPROVED
|
|
REJECTED
|
|
NEEDS_CHANGES
|
|
}
|
|
|
|
enum ReviewStatus {
|
|
PENDING
|
|
INTERNAL_APPROVED
|
|
CLIENT_APPROVED
|
|
NEEDS_CHANGES
|
|
FINAL_APPROVED
|
|
}
|
|
|
|
enum NotificationType {
|
|
VERSION_UPLOADED
|
|
FEEDBACK_ADDED
|
|
SHOT_APPROVED
|
|
SHOT_REJECTED
|
|
COMMENT_REPLY
|
|
MENTION
|
|
REVISION_REQUESTED
|
|
TASK_ASSIGNED
|
|
TASK_OVERDUE
|
|
TASK_APPROVED
|
|
TASK_CHANGES_REQUESTED
|
|
TASK_READY_FOR_REVIEW
|
|
}
|
|
|
|
enum TaskStatus {
|
|
TODO
|
|
IN_PROGRESS
|
|
INTERNAL_REVIEW
|
|
CLIENT_REVIEW
|
|
CHANGES
|
|
DONE
|
|
}
|
|
|
|
enum TaskType {
|
|
TRACK
|
|
ROTO
|
|
KEY
|
|
COMP
|
|
FX
|
|
LIGHTING
|
|
RENDER
|
|
ANIMATION
|
|
MODEL
|
|
TEXTURE
|
|
RIG
|
|
LOOKDEV
|
|
GENERAL
|
|
}
|
|
|
|
enum ProjectType {
|
|
STANDARD
|
|
EPISODIC
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// AUTH MODELS (NextAuth v5 compatible)
|
|
// ─────────────────────────────────────────────
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
name String?
|
|
email String @unique
|
|
emailVerified DateTime?
|
|
image String?
|
|
passwordHash String?
|
|
role Role @default(ARTIST)
|
|
isActive Boolean @default(true)
|
|
mustChangePassword Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// NextAuth relations
|
|
accounts Account[]
|
|
sessions Session[]
|
|
|
|
// App relations
|
|
producedProjects Project[] @relation("ProducerProjects")
|
|
supervisedProjects Project[] @relation("SupervisorProjects")
|
|
assignedShots Shot[] @relation("ArtistShots")
|
|
uploadedVersions Version[]
|
|
comments Comment[]
|
|
commentReplies CommentReply[]
|
|
annotations Annotation[]
|
|
approvals Approval[]
|
|
notifications Notification[]
|
|
clientAccess ClientAccess[]
|
|
assignedTasks Task[] @relation("TaskArtist")
|
|
createdTasks Task[] @relation("TaskCreator")
|
|
sharedVersions Version[] @relation("VersionSharedBy")
|
|
ledAssets Asset[] @relation("AssetLead")
|
|
|
|
@@map("users")
|
|
}
|
|
|
|
model Account {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
type String
|
|
provider String
|
|
providerAccountId String
|
|
refresh_token String? @db.Text
|
|
access_token String? @db.Text
|
|
expires_at Int?
|
|
token_type String?
|
|
scope String?
|
|
id_token String? @db.Text
|
|
session_state String?
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([provider, providerAccountId])
|
|
@@map("accounts")
|
|
}
|
|
|
|
model Session {
|
|
id String @id @default(cuid())
|
|
sessionToken String @unique
|
|
userId String
|
|
expires DateTime
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("sessions")
|
|
}
|
|
|
|
model VerificationToken {
|
|
identifier String
|
|
token String @unique
|
|
expires DateTime
|
|
|
|
@@unique([identifier, token])
|
|
@@map("verification_tokens")
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// CORE DOMAIN MODELS
|
|
// ─────────────────────────────────────────────
|
|
|
|
model Client {
|
|
id String @id @default(cuid())
|
|
company String
|
|
contactPerson String
|
|
email String @unique
|
|
phone String?
|
|
notes String? @db.Text
|
|
logoUrl String?
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
projects Project[]
|
|
clientAccess ClientAccess[]
|
|
|
|
@@map("clients")
|
|
}
|
|
|
|
/// Links a USER with CLIENT role to the client record they represent
|
|
model ClientAccess {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
clientId String
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
client Client @relation(fields: [clientId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, clientId])
|
|
@@map("client_access")
|
|
}
|
|
|
|
model Project {
|
|
id String @id @default(cuid())
|
|
name String
|
|
code String @unique
|
|
showId String @default("")
|
|
projectType ProjectType @default(STANDARD)
|
|
description String? @db.Text
|
|
status ProjectStatus @default(ACTIVE)
|
|
dueDate DateTime?
|
|
startDate DateTime?
|
|
clientId String?
|
|
producerId String?
|
|
supervisorId String?
|
|
slackWebhook String?
|
|
slackChannel String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
client Client? @relation(fields: [clientId], references: [id])
|
|
producer User? @relation("ProducerProjects", fields: [producerId], references: [id])
|
|
supervisor User? @relation("SupervisorProjects", fields: [supervisorId], references: [id])
|
|
shots Shot[]
|
|
assets Asset[]
|
|
tasks Task[]
|
|
reviewSessions ReviewSession[]
|
|
|
|
@@map("projects")
|
|
}
|
|
|
|
model Shot {
|
|
id String @id @default(cuid())
|
|
shotCode String
|
|
scene String @default("")
|
|
episode String?
|
|
shotNumber Int @default(0)
|
|
sequence String?
|
|
description String? @db.Text
|
|
status ShotStatus @default(WAITING)
|
|
priority ShotPriority @default(NORMAL)
|
|
artistId String?
|
|
projectId String
|
|
frameStart Int?
|
|
frameEnd Int?
|
|
fps Float @default(24)
|
|
dueDate DateTime?
|
|
thumbnailUrl String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
artist User? @relation("ArtistShots", fields: [artistId], references: [id])
|
|
versions Version[]
|
|
tasks Task[]
|
|
|
|
@@unique([projectId, shotCode])
|
|
@@map("shots")
|
|
}
|
|
|
|
model Version {
|
|
id String @id @default(cuid())
|
|
versionNumber Int
|
|
shotId String?
|
|
taskId String?
|
|
artistId String?
|
|
fileUrl String
|
|
fileName String
|
|
fileSize BigInt?
|
|
mimeType String?
|
|
thumbnailUrl String?
|
|
posterUrl String?
|
|
proxyUrl String?
|
|
fps Float @default(24)
|
|
duration Float?
|
|
frameCount Int?
|
|
width Int?
|
|
height Int?
|
|
notes String? @db.Text
|
|
approvalStatus ApprovalStatus @default(PENDING_REVIEW)
|
|
reviewStatus ReviewStatus @default(PENDING)
|
|
isLatest Boolean @default(true)
|
|
isClientVisible Boolean @default(false)
|
|
sharedAt DateTime?
|
|
sharedById String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
shot Shot? @relation(fields: [shotId], references: [id], onDelete: Cascade)
|
|
task Task? @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
|
artist User? @relation(fields: [artistId], references: [id])
|
|
sharedBy User? @relation("VersionSharedBy", fields: [sharedById], references: [id])
|
|
comments Comment[]
|
|
annotations Annotation[]
|
|
approvals Approval[]
|
|
|
|
@@map("versions")
|
|
}
|
|
|
|
model Comment {
|
|
id String @id @default(cuid())
|
|
versionId String
|
|
authorId String?
|
|
frameNumber Int
|
|
timestamp Float
|
|
text String @db.Text
|
|
isResolved Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
version Version @relation(fields: [versionId], references: [id], onDelete: Cascade)
|
|
author User? @relation(fields: [authorId], references: [id], onDelete: SetNull)
|
|
replies CommentReply[]
|
|
annotations Annotation[]
|
|
|
|
@@map("comments")
|
|
}
|
|
|
|
model CommentReply {
|
|
id String @id @default(cuid())
|
|
commentId String
|
|
authorId String?
|
|
text String @db.Text
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
comment Comment @relation(fields: [commentId], references: [id], onDelete: Cascade)
|
|
author User? @relation(fields: [authorId], references: [id], onDelete: SetNull)
|
|
|
|
@@map("comment_replies")
|
|
}
|
|
|
|
/// Stores canvas drawing data as JSON (normalized 0-1 coordinates)
|
|
model Annotation {
|
|
id String @id @default(cuid())
|
|
versionId String
|
|
commentId String?
|
|
authorId String?
|
|
frameNumber Int
|
|
drawingData Json // Array of AnnotationShape objects
|
|
color String @default("#ef4444")
|
|
isVisible Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
|
|
version Version @relation(fields: [versionId], references: [id], onDelete: Cascade)
|
|
comment Comment? @relation(fields: [commentId], references: [id], onDelete: SetNull)
|
|
author User? @relation(fields: [authorId], references: [id], onDelete: SetNull)
|
|
|
|
@@map("annotations")
|
|
}
|
|
|
|
model Approval {
|
|
id String @id @default(cuid())
|
|
versionId String
|
|
userId String?
|
|
status ApprovalStatus
|
|
notes String? @db.Text
|
|
createdAt DateTime @default(now())
|
|
|
|
version Version @relation(fields: [versionId], references: [id], onDelete: Cascade)
|
|
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
|
|
|
@@map("approvals")
|
|
}
|
|
|
|
model Notification {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
type NotificationType
|
|
title String
|
|
message String @db.Text
|
|
data Json? // Extra context (versionId, shotCode, etc.)
|
|
isRead Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("notifications")
|
|
}
|
|
|
|
model Asset {
|
|
id String @id @default(cuid())
|
|
projectId String
|
|
assetCode String
|
|
name String
|
|
description String? @db.Text
|
|
status ShotStatus @default(WAITING)
|
|
priority ShotPriority @default(NORMAL)
|
|
leadId String?
|
|
dueDate DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
lead User? @relation("AssetLead", fields: [leadId], references: [id])
|
|
tasks Task[]
|
|
|
|
@@unique([projectId, assetCode])
|
|
@@map("assets")
|
|
}
|
|
|
|
model Task {
|
|
id String @id @default(cuid())
|
|
title String
|
|
description String? @db.Text
|
|
type TaskType @default(GENERAL)
|
|
status TaskStatus @default(TODO)
|
|
priority ShotPriority @default(NORMAL)
|
|
dueDate DateTime?
|
|
estimatedHours Float?
|
|
sortOrder Int @default(0)
|
|
shotId String?
|
|
assetId String?
|
|
assignedArtistId String?
|
|
createdById String?
|
|
projectId String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
scheduledStartDate DateTime?
|
|
scheduledEndDate DateTime?
|
|
scheduleNotes String? @db.Text
|
|
|
|
shot Shot? @relation(fields: [shotId], references: [id], onDelete: Cascade)
|
|
asset Asset? @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
|
assignedArtist User? @relation("TaskArtist", fields: [assignedArtistId], references: [id])
|
|
createdBy User? @relation("TaskCreator", fields: [createdById], references: [id], onDelete: SetNull)
|
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
versions Version[]
|
|
|
|
@@index([scheduledStartDate])
|
|
@@index([scheduledEndDate])
|
|
@@map("tasks")
|
|
}
|
|
|
|
/// Secure tokenized review link for clients
|
|
model ReviewSession {
|
|
id String @id @default(cuid())
|
|
projectId String
|
|
token String @unique @default(cuid())
|
|
label String?
|
|
email String?
|
|
expiresAt DateTime
|
|
isActive Boolean @default(true)
|
|
accessCount Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
|
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("review_sessions")
|
|
}
|