Files
clinera-site/prisma/schema.prisma
T

379 lines
12 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Clinic {
id String @id @default(cuid())
name String
slug String @unique
email String @unique
passwordHash String?
phone String?
clinicType String @default("Medical Clinic")
wahaSessionId String?
wahaStatus String @default("disconnected")
wahaLastChecked DateTime?
timezone String @default("Asia/Riyadh")
quietHoursStart Int @default(22)
quietHoursEnd Int @default(8)
plan String @default("free")
planExpiresAt DateTime?
trialEndsAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sessions AppSession[]
appointments Appointment[]
auditLogs AuditLog[]
cronLogs CronLog[]
leads Lead[]
patientNotes PatientNote[]
messages MessageLog[]
oauthAccounts OAuthAccount[]
patientProfiles PatientProfile[]
reminders ReminderTemplate[]
reminderTrees ReminderTree[]
treatments Treatment[]
treeExperiments TreeExperiment[]
campaigns Campaign[]
staff Staff[]
}
model OAuthAccount {
id String @id @default(cuid())
clinicId String
provider String
providerId String
email String?
name String?
avatarUrl String?
accessToken String?
refreshToken String?
expiresAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
@@unique([provider, providerId])
@@index([clinicId])
@@index([provider, email])
}
model AppSession {
id String @id @default(cuid())
clinicId String
token String @unique
userAgent String?
ipAddress String?
expiresAt DateTime
createdAt DateTime @default(now())
lastSeenAt DateTime @default(now())
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
@@index([clinicId])
@@index([token])
@@index([expiresAt])
}
model AuditLog {
id String @id @default(cuid())
clinicId String
action String
actor String?
resource String?
metadata String?
ipAddress String?
userAgent String?
createdAt DateTime @default(now())
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
@@index([clinicId, createdAt])
@@index([action, createdAt])
}
model Treatment {
id String @id @default(cuid())
clinicId String
name String
duration Int @default(30)
price Float?
prepInstructions String?
aftercareInstructions String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
appointments Appointment[]
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
}
model Lead {
id String @id @default(cuid())
clinicId String
name String
phone String
email String?
source String @default("manual")
status String @default("new")
treatmentInterest String?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
appointments Appointment[]
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
patientNotes PatientNote[]
patientProfiles PatientProfile[]
}
model Appointment {
id String @id @default(cuid())
clinicId String
leadId String
treatmentId String
dateTime DateTime
status String @default("scheduled")
confirmToken String?
confirmedAt DateTime?
arrivedAt DateTime?
completedAt DateTime?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
treatment Treatment @relation(fields: [treatmentId], references: [id], onDelete: Cascade)
lead Lead @relation(fields: [leadId], references: [id], onDelete: Cascade)
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
messages MessageLog[]
scheduledReminders ScheduledReminder[]
userJourneys UserJourney[]
}
model ReminderTemplate {
id String @id @default(cuid())
clinicId String?
trigger String
offsetMinutes Int
message String
isActive Boolean @default(true)
sortOrder Int @default(0)
clinic Clinic? @relation(fields: [clinicId], references: [id], onDelete: Cascade)
scheduledReminders ScheduledReminder[]
}
model ScheduledReminder {
id String @id @default(cuid())
appointmentId String
templateId String
scheduledFor DateTime
status String @default("pending")
sentAt DateTime?
failureReason String?
retryCount Int @default(0)
nextRetryAt DateTime?
template ReminderTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
appointment Appointment @relation(fields: [appointmentId], references: [id], onDelete: Cascade)
@@index([status, scheduledFor])
}
model MessageLog {
id String @id @default(cuid())
clinicId String
appointmentId String?
phone String
direction String @default("outgoing")
message String
status String @default("queued")
wahaMessageId String?
sentAt DateTime?
deliveredAt DateTime?
readAt DateTime?
createdAt DateTime @default(now())
appointment Appointment? @relation(fields: [appointmentId], references: [id])
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
@@index([wahaMessageId])
}
model CronLog {
id String @id @default(cuid())
clinicId String?
type String
sent Int @default(0)
skipped Int @default(0)
failed Int @default(0)
retried Int @default(0)
durationMs Int @default(0)
error String?
ranAt DateTime @default(now())
clinic Clinic? @relation(fields: [clinicId], references: [id])
@@index([type, ranAt])
}
model ReminderTree {
id String @id @default(cuid())
clinicId String
name String
description String?
triggerType String
isActive Boolean @default(true)
version Int @default(1)
metadata String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
variantFor TreeExperiment[] @relation("VariantTree")
controlFor TreeExperiment[] @relation("ControlTree")
nodes TreeNode[]
userJourneys UserJourney[]
@@index([clinicId, isActive])
}
model TreeNode {
id String @id @default(cuid())
treeId String
parentId String?
name String
nodeType String
content String?
offsetMinutes Int @default(0)
conditions String?
metadata String?
sortOrder Int @default(0)
createdAt DateTime @default(now())
nextNodes NodeExecution[] @relation("NextNode")
executions NodeExecution[]
parent TreeNode? @relation("TreeNodeParent", fields: [parentId], references: [id], onDelete: Cascade)
children TreeNode[] @relation("TreeNodeParent")
tree ReminderTree @relation(fields: [treeId], references: [id], onDelete: Cascade)
userJourneys UserJourney[]
@@index([treeId, sortOrder])
@@index([parentId])
}
model UserJourney {
id String @id @default(cuid())
appointmentId String
treeId String
currentNodeId String?
status String @default("active")
journeyData String?
startedAt DateTime @default(now())
lastActiveAt DateTime @default(now())
completedAt DateTime?
executions NodeExecution[]
currentNode TreeNode? @relation(fields: [currentNodeId], references: [id])
tree ReminderTree @relation(fields: [treeId], references: [id], onDelete: Cascade)
appointment Appointment @relation(fields: [appointmentId], references: [id], onDelete: Cascade)
@@index([appointmentId, status])
@@index([currentNodeId, lastActiveAt])
}
model NodeExecution {
id String @id @default(cuid())
journeyId String
nodeId String
status String @default("pending")
executedAt DateTime?
response String?
metadata String?
nextNodeId String?
nextNode TreeNode? @relation("NextNode", fields: [nextNodeId], references: [id])
node TreeNode @relation(fields: [nodeId], references: [id], onDelete: Cascade)
journey UserJourney @relation(fields: [journeyId], references: [id], onDelete: Cascade)
@@index([journeyId, executedAt])
}
model PatientProfile {
id String @id @default(cuid())
leadId String
clinicId String
responseStyle String @default("standard")
preferredTime String?
engagementScore Float @default(50.0)
noShowRisk Float @default(50.0)
conversationTone String @default("neutral")
languagePreference String @default("auto")
lastInteractionAt DateTime?
profileData String?
updatedAt DateTime @default(now()) @updatedAt
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
lead Lead @relation(fields: [leadId], references: [id], onDelete: Cascade)
@@unique([leadId, clinicId])
@@index([leadId])
}
model TreeExperiment {
id String @id @default(cuid())
clinicId String
name String
description String?
controlTreeId String
variantTreeId String
trafficSplit Float @default(50.0)
status String @default("draft")
startDate DateTime?
endDate DateTime?
metrics String?
results String?
createdAt DateTime @default(now())
variantTree ReminderTree @relation("VariantTree", fields: [variantTreeId], references: [id], onDelete: Cascade)
controlTree ReminderTree @relation("ControlTree", fields: [controlTreeId], references: [id], onDelete: Cascade)
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
@@index([clinicId, status])
}
model PatientNote {
id String @id @default(cuid())
leadId String
clinicId String
type String @default("note")
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lead Lead @relation(fields: [leadId], references: [id], onDelete: Cascade)
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
@@index([leadId])
}
model Campaign {
id String @id @default(cuid())
clinicId String
name String
message String
targetType String
targetValue String?
sentCount Int @default(0)
status String @default("sent")
createdAt DateTime @default(now())
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
@@index([clinicId, createdAt])
}
model Staff {
id String @id @default(cuid())
clinicId String
name String
email String
passwordHash String
role String @default("receptionist")
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)
@@unique([clinicId, email])
@@index([clinicId])
}