generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ─── Users ────────────────────────────────────────────────────────── model User { id String @id @default(cuid()) did String @unique // EncryptID DID username String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt trips TripCollaborator[] expenses Expense[] packingItems PackingItem[] } // ─── Trips ────────────────────────────────────────────────────────── model Trip { id String @id @default(cuid()) title String slug String @unique description String? @db.Text rawInput String? @db.Text // Original NL input preserved startDate DateTime? endDate DateTime? budgetTotal Float? budgetCurrency String @default("USD") status TripStatus @default(PLANNING) canvasSlug String? // rspace community slug createdAt DateTime @default(now()) updatedAt DateTime @updatedAt collaborators TripCollaborator[] destinations Destination[] itineraryItems ItineraryItem[] bookings Booking[] expenses Expense[] packingItems PackingItem[] } enum TripStatus { PLANNING BOOKED IN_PROGRESS COMPLETED CANCELLED } model TripCollaborator { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) tripId String trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade) role CollaboratorRole @default(MEMBER) joinedAt DateTime @default(now()) @@unique([userId, tripId]) @@index([tripId]) } enum CollaboratorRole { OWNER EDITOR VIEWER MEMBER } // ─── Destinations ─────────────────────────────────────────────────── model Destination { id String @id @default(cuid()) tripId String trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade) name String country String? lat Float? lng Float? arrivalDate DateTime? departureDate DateTime? notes String? @db.Text sortOrder Int @default(0) canvasShapeId String? // ID of folk-destination on canvas createdAt DateTime @default(now()) itineraryItems ItineraryItem[] bookings Booking[] @@index([tripId]) } // ─── Itinerary ────────────────────────────────────────────────────── model ItineraryItem { id String @id @default(cuid()) tripId String trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade) destinationId String? destination Destination? @relation(fields: [destinationId], references: [id], onDelete: SetNull) title String description String? @db.Text date DateTime? startTime String? // "09:00" format endTime String? // "17:00" format category ItineraryCategory @default(ACTIVITY) sortOrder Int @default(0) canvasShapeId String? createdAt DateTime @default(now()) @@index([tripId]) @@index([destinationId]) } enum ItineraryCategory { FLIGHT TRANSPORT ACCOMMODATION ACTIVITY MEAL FREE_TIME OTHER } // ─── Bookings ─────────────────────────────────────────────────────── model Booking { id String @id @default(cuid()) tripId String trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade) destinationId String? destination Destination? @relation(fields: [destinationId], references: [id], onDelete: SetNull) type BookingType provider String? // "Air Canada", "Booking.com" confirmationNumber String? details String? @db.Text // JSON for flexible data cost Float? currency String @default("USD") startDate DateTime? endDate DateTime? status BookingStatus @default(PLANNED) canvasShapeId String? createdAt DateTime @default(now()) @@index([tripId]) } enum BookingType { FLIGHT HOTEL CAR_RENTAL TRAIN BUS FERRY ACTIVITY RESTAURANT OTHER } enum BookingStatus { PLANNED BOOKED CONFIRMED CANCELLED } // ─── Expenses ─────────────────────────────────────────────────────── model Expense { id String @id @default(cuid()) tripId String trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade) paidById String? paidBy User? @relation(fields: [paidById], references: [id], onDelete: SetNull) description String amount Float currency String @default("USD") category ExpenseCategory @default(OTHER) date DateTime? splitType SplitType @default(EQUAL) createdAt DateTime @default(now()) @@index([tripId]) } enum ExpenseCategory { FLIGHT ACCOMMODATION FOOD TRANSPORT ACTIVITY SHOPPING OTHER } enum SplitType { EQUAL CUSTOM INDIVIDUAL } // ─── Packing ──────────────────────────────────────────────────────── model PackingItem { id String @id @default(cuid()) tripId String trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade) addedById String? addedBy User? @relation(fields: [addedById], references: [id], onDelete: SetNull) name String category String? // "Clothing", "Electronics", "Documents" packed Boolean @default(false) quantity Int @default(1) sortOrder Int @default(0) createdAt DateTime @default(now()) @@index([tripId]) }