Initial commit

This commit is contained in:
twotalesanimation
2026-05-19 22:20:29 +02:00
commit 0fbe856dce
173 changed files with 38316 additions and 0 deletions
+472
View File
@@ -0,0 +1,472 @@
// 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")
}