Documentation is currently in beta. Report issues →
DatabaseSchema Reference

Schema Reference

Complete Prisma schema for Y3NKO.

Users & Authentication

User

model User {
  id            String    @id @default(cuid())
  email         String    @unique
  phone         String?   @unique
  emailVerified DateTime?
  phoneVerified DateTime?
  passwordHash  String?
  role          UserRole  @default(USER)
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
 
  profile       Profile?
  bookings      Booking[]
  favorites     Favorite[]
 
  @@index([email])
  @@index([phone])
}
 
enum UserRole {
  USER
  HOST
  ADMIN
  SUPER_ADMIN
}

Profile

model Profile {
  id          String   @id @default(cuid())
  userId      String   @unique
  firstName   String
  lastName    String
  displayName String?
  avatarUrl   String?
  bio         String?
  nationality String?
  language    String   @default("en")
  currency    Currency @default(GHS)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
 
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
 
enum Currency {
  GHS
  USD
  EUR
  GBP
}

Listings

Listing

model Listing {
  id          String        @id @default(cuid())
  type        ListingType
  status      ListingStatus @default(DRAFT)
 
  // Basic Info
  title       String
  slug        String        @unique
  description String        @db.Text
  highlights  String[]
 
  // Location
  region      String
  city        String
  area        String?
  address     String
  latitude    Float?
  longitude   Float?
 
  // Pricing
  priceGHS    Decimal       @db.Decimal(10, 2)
  priceUSD    Decimal?      @db.Decimal(10, 2)
  priceUnit   PriceUnit     @default(PER_NIGHT)
 
  // Accommodation-specific
  propertyType    PropertyType?
  bedrooms        Int?
  bathrooms       Int?
  maxGuests       Int?
  checkInTime     String?
  checkOutTime    String?
 
  // Activity-specific
  duration        Int?
  difficulty      Difficulty?
  minParticipants Int?
  maxParticipants Int?
 
  // Meta
  featured        Boolean   @default(false)
  viewCount       Int       @default(0)
  bookingCount    Int       @default(0)
 
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt
  publishedAt DateTime?
 
  // Host
  hostId      String?
 
  images      ListingImage[]
  amenities   ListingAmenity[]
  bookings    Booking[]
  favorites   Favorite[]
  availability Availability[]
 
  @@index([type, status])
  @@index([region, city])
  @@index([slug])
  @@index([featured])
  @@index([hostId])
}
 
enum ListingType {
  ACCOMMODATION
  ACTIVITY
  EXPERIENCE
  RESTAURANT
}
 
enum ListingStatus {
  DRAFT
  PENDING_REVIEW
  PUBLISHED
  ARCHIVED
}
 
enum PropertyType {
  HOTEL
  GUEST_HOUSE
  APARTMENT
  VILLA
  HOSTEL
  RESORT
  BOUTIQUE_HOTEL
  AIRBNB_STYLE
}
 
enum PriceUnit {
  PER_NIGHT
  PER_PERSON
  PER_GROUP
  FIXED
}
 
enum Difficulty {
  EASY
  MODERATE
  CHALLENGING
}

ListingImage

model ListingImage {
  id        String   @id @default(cuid())
  listingId String
  url       String
  publicId  String   // Cloudinary public ID
  alt       String?
  caption   String?
  order     Int      @default(0)
  isPrimary Boolean  @default(false)
  createdAt DateTime @default(now())
 
  listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
 
  @@index([listingId])
}

Amenities

model Amenity {
  id       String @id @default(cuid())
  name     String @unique
  icon     String
  category String
 
  listings ListingAmenity[]
}
 
model ListingAmenity {
  listingId String
  amenityId String
 
  listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
  amenity Amenity @relation(fields: [amenityId], references: [id], onDelete: Cascade)
 
  @@id([listingId, amenityId])
}

Availability

model Availability {
  id        String   @id @default(cuid())
  listingId String
  date      DateTime @db.Date
  available Boolean  @default(true)
  priceGHS  Decimal? @db.Decimal(10, 2)
  note      String?
 
  listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
 
  @@unique([listingId, date])
  @@index([listingId, date])
}

Bookings & Payments

Booking

model Booking {
  id          String        @id @default(cuid())
  reference   String        @unique
  userId      String
  listingId   String
  status      BookingStatus @default(PENDING)
 
  // Dates
  checkIn     DateTime      @db.Date
  checkOut    DateTime      @db.Date
  nights      Int
 
  // Guests
  adults      Int           @default(1)
  children    Int           @default(0)
 
  // Pricing (snapshot at booking time)
  pricePerNight Decimal     @db.Decimal(10, 2)
  subtotal      Decimal     @db.Decimal(10, 2)
  serviceFee    Decimal     @db.Decimal(10, 2)
  taxes         Decimal     @db.Decimal(10, 2)
  total         Decimal     @db.Decimal(10, 2)
  currency      Currency    @default(GHS)
 
  // Guest Info
  guestName   String
  guestEmail  String
  guestPhone  String
  specialRequests String?   @db.Text
 
  createdAt   DateTime      @default(now())
  updatedAt   DateTime      @updatedAt
  confirmedAt DateTime?
  cancelledAt DateTime?
  cancelReason String?
 
  user     User      @relation(fields: [userId], references: [id])
  listing  Listing   @relation(fields: [listingId], references: [id])
  payments Payment[]
 
  @@index([userId])
  @@index([listingId])
  @@index([reference])
  @@index([status])
  @@index([checkIn, checkOut])
}
 
enum BookingStatus {
  PENDING
  CONFIRMED
  CANCELLED
  COMPLETED
  NO_SHOW
}

Payment

model Payment {
  id            String        @id @default(cuid())
  bookingId     String
  status        PaymentStatus @default(PENDING)
 
  amount        Decimal       @db.Decimal(10, 2)
  currency      Currency      @default(GHS)
  method        PaymentMethod?
 
  paystackRef   String?       @unique
  paystackAuth  String?
 
  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt
  paidAt        DateTime?
  failedAt      DateTime?
  failureReason String?
 
  booking Booking @relation(fields: [bookingId], references: [id])
 
  @@index([bookingId])
  @@index([paystackRef])
}
 
enum PaymentStatus {
  PENDING
  PROCESSING
  COMPLETED
  FAILED
  REFUNDED
}
 
enum PaymentMethod {
  CARD
  MOBILE_MONEY
  BANK_TRANSFER
}

User Interactions

Favorite

model Favorite {
  userId    String
  listingId String
  createdAt DateTime @default(now())
 
  user    User    @relation(fields: [userId], references: [id], onDelete: Cascade)
  listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
 
  @@id([userId, listingId])
}

Content

Destination

model Destination {
  id          String   @id @default(cuid())
  name        String
  slug        String   @unique
  region      String
  description String   @db.Text
  imageUrl    String
  featured    Boolean  @default(false)
  order       Int      @default(0)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
 
  @@index([slug])
  @@index([featured])
}

Ghana Regions

const GHANA_REGIONS = [
  "Greater Accra",
  "Ashanti",
  "Western",
  "Central",
  "Eastern",
  "Volta",
  "Northern",
  "Upper East",
  "Upper West",
  "Brong-Ahafo",
  "Oti",
  "Bono East",
  "Ahafo",
  "Western North",
  "North East",
  "Savannah",
]