// 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" } datasource db { provider = "postgresql" } // --- Enums --- enum MemberRole { OWNER EDITOR MEMBER_PLAN_ONLY MEMBER_PHOTO_ONLY VIEWER_EXTERNAL } enum ExpenseCategory { LODGING DINING TRANSPORT OTHER } enum TriggerType { AUTO_BY_TIME MANUAL_BY_USER } enum PrivacyLevel { PUBLIC_IN_TOUR PRIVATE_OWNER } // --- Models --- model User { id Int @id @default(autoincrement()) email String @unique passwordHash String name String? avatar String? createdAt DateTime @default(now()) isAdmin Boolean @default(false) createdTours Tour[] @relation("TourCreator") tourMemberships TourMember[] uploadedPhotos Photo[] } model Tour { id Int @id @default(autoincrement()) title String startDate DateTime? endDate DateTime? createdAt DateTime @default(now()) creatorId Int creator User @relation("TourCreator", fields: [creatorId], references: [id]) members TourMember[] legs Leg[] tasks Task[] photos Photo[] } model TourMember { tourId Int userId Int role MemberRole @default(MEMBER_PLAN_ONLY) tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@id([tourId, userId]) } model Leg { id Int @id @default(autoincrement()) tourId Int sequenceNumber Int notes String? @db.Text tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade) places Place[] expenses Expense[] tasks Task[] } model Place { id Int @id @default(autoincrement()) legId Int name String address String? // Sử dụng Float cho Lat/Lng để dễ thao tác ở Frontend. // Nếu bạn muốn dùng PostGIS chuyên sâu, có thể dùng Unsupported("geography(Point, 4326)") latitude Float longitude Float sequenceInLeg Int arrivalTime DateTime? departureTime DateTime? leg Leg @relation(fields: [legId], references: [id], onDelete: Cascade) expenses Expense[] photos Photo[] } model Expense { id Int @id @default(autoincrement()) legId Int placeId Int? category ExpenseCategory amount Decimal @db.Decimal(15, 2) currency String @default("VND") description String? @db.Text leg Leg @relation(fields: [legId], references: [id], onDelete: Cascade) place Place? @relation(fields: [placeId], references: [id], onDelete: Cascade) } model Task { id Int @id @default(autoincrement()) tourId Int legId Int? title String plannedTimestamp DateTime isCompleted Boolean @default(false) completedAt DateTime? triggerType TriggerType @default(MANUAL_BY_USER) tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade) leg Leg? @relation(fields: [legId], references: [id]) } model Photo { id Int @id @default(autoincrement()) tourId Int placeId Int? uploaderId Int imageUrl String capturedAt DateTime @default(now()) privacyLevel PrivacyLevel @default(PUBLIC_IN_TOUR) tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade) place Place? @relation(fields: [placeId], references: [id], onDelete: SetNull) uploader User @relation(fields: [uploaderId], references: [id]) }