Database Models (Prisma Schema)
The database uses PostgreSQL with Prisma ORM. Schema is in /prisma/schema.prisma.
Enums
User & Employment
prisma
enum UserRole {
job_seeker // Formal job seeker
service_worker // Informal service provider
client // Needs services
hybrid // Both worker and client
}
enum CompanySize {
startup | small | medium | large | enterprise
}
enum JobType {
full_time | part_time | contract | internship | remote
}
enum ExperienceLevel {
entry | mid | senior | executive
}
enum ApplicationStatus {
pending | reviewed | shortlisted | rejected | hired
}
enum SwipeAction {
like | dislike | superlike
}Services
prisma
enum BookingStatus {
pending | accepted | declined | in_progress | completed | cancelled | disputed
}
enum YeboTier {
bronze | silver | gold | platinum
}Billing
prisma
enum SubscriptionTier {
free | pro | business | enterprise
}
enum SubscriptionStatus {
active | trialing | past_due | canceled | unpaid | paused
}
enum SubscriberType {
user | employer
}
enum VoucherType {
percentage | fixed_amount | free_trial | credits
}
enum BillingInterval {
monthly | yearly
}Messaging
prisma
enum MessageStatus {
sent | delivered | read
}
enum MessageType {
text | image | system
}
enum ConversationType {
job_inquiry // About a job application
booking_inquiry // About a service booking/quote
}Core Models
User
prisma
model User {
id String @id @default(cuid())
name String
email String?
password String
phone String @unique
location String
bio String?
profilePicture String?
profileVideo String?
personalityMatch String[] @default([])
culturePreferences String[] @default([])
isActive Boolean @default(true)
lastLogin DateTime?
refreshToken String?
showInterviewScores Boolean @default(true) // AI score privacy
role UserRole @default(job_seeker)
latitude Float?
longitude Float?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
education Education[]
experience Experience[]
swipeHistory SwipeHistory[]
savedJobs SavedJob[]
applications Application[]
serviceWorkerProfile ServiceWorkerProfile?
credits UserCredits?
stripeCustomer StripeCustomer?
// ... more relations
}Employer
prisma
model Employer {
id String @id @default(cuid())
companyName String
email String @unique
password String
industry String
companySize CompanySize
description String
phone String
whatsappNumber String?
logo String?
website String?
location String
isVerified Boolean @default(false)
jobsPosted Int @default(0)
activeJobs Int @default(0)
joinedDate DateTime @default(now())
refreshToken String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
jobs Job[]
applications Application[]
stripeCustomer StripeCustomer?
}Job
prisma
model Job {
id String @id @default(cuid())
title String
company String
location String
salary Decimal @db.Decimal(12, 2)
currency String @default("USD")
description String
requirements String[] @default([])
benefits String[] @default([])
jobType JobType
experienceLevel ExperienceLevel
applicationsCount Int @default(0)
savesCount Int @default(0)
sharesCount Int @default(0)
videoContent String?
isActive Boolean @default(true)
postedDate DateTime @default(now())
expiryDate DateTime
hashtags String[] @default([])
trending Boolean @default(false)
boostLevel Int @default(0)
cultureFit String[] @default([])
personalityMatch String[] @default([])
whatsappContact String?
email String
employerId String
// AI Interview settings
aiInterviewEnabled Boolean @default(false)
autoInviteOnApply Boolean @default(false)
interviewQuestions Int @default(10)
interviewTimeLimit Int @default(15)
employer Employer @relation(fields: [employerId], references: [id], onDelete: Cascade)
applications Application[]
swipeHistory SwipeHistory[]
savedBy SavedJob[]
}Application
prisma
model Application {
id String @id @default(cuid())
userId String
jobId String
employerId String
status ApplicationStatus @default(pending)
coverLetter String?
cvUrl String?
appliedDate DateTime @default(now())
// AI Interview fields
interviewSessionId String?
interviewStatus String? // pending, in_progress, completed, expired
interviewScore Int? // 0-100 composite score
interviewGrade String? // A+, A, B+, B, C, D, F
interviewCompletedAt DateTime?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
job Job @relation(fields: [jobId], references: [id], onDelete: Cascade)
employer Employer @relation(fields: [employerId], references: [id], onDelete: Cascade)
@@unique([userId, jobId])
}Services Models
ServiceWorkerProfile
prisma
model ServiceWorkerProfile {
id String @id @default(cuid())
userId String @unique
headline String
bio String?
skills String[] @default([])
yearsExperience Int @default(0)
// Pricing
priceMin Decimal? @db.Decimal(10, 2)
priceMax Decimal? @db.Decimal(10, 2)
priceNote String?
currency String @default("SZL")
negotiable Boolean @default(true)
// Location & Availability
latitude Float?
longitude Float?
locationName String
serviceRadius Int @default(25)
isAvailable Boolean @default(true)
availableDays String[] @default(["monday", "tuesday", "wednesday", "thursday", "friday"])
availableFrom String? @default("08:00")
availableTo String? @default("17:00")
// Media
introVideo String?
portfolioVideos String[] @default([])
portfolioImages String[] @default([])
certificateUrls String[] @default([])
// Verification
isVerified Boolean @default(false)
idVerified Boolean @default(false)
phoneVerified Boolean @default(true)
// Stats
jobsCompleted Int @default(0)
averageRating Decimal @default(0) @db.Decimal(2, 1)
reviewCount Int @default(0)
responseRate Int @default(100)
responseTimeMin Int @default(60)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
categories WorkerCategory[]
bookings ServiceBooking[] @relation("WorkerBookings")
quotes Quote[]
reviews Review[] @relation("WorkerReviews")
}ServiceCategory
prisma
model ServiceCategory {
id String @id @default(cuid())
name String @unique
slug String @unique
parentId String?
description String?
icon String?
aiGenerated Boolean @default(true)
usageCount Int @default(0)
isActive Boolean @default(true)
parent ServiceCategory? @relation("CategoryHierarchy", fields: [parentId], references: [id])
children ServiceCategory[] @relation("CategoryHierarchy")
workerCategories WorkerCategory[]
}ServiceBooking
prisma
model ServiceBooking {
id String @id @default(cuid())
clientId String
workerId String
listingId String?
requestId String?
categoryId String?
description String
categoryTags String[] @default([])
scheduledDate DateTime
scheduledTime String?
estimatedDuration String?
latitude Float
longitude Float
locationName String
address String
agreedPrice Decimal @db.Decimal(10, 2)
currency String @default("SZL")
priceNotes String?
status BookingStatus @default(pending)
acceptedAt DateTime?
startedAt DateTime?
completedAt DateTime?
cancelledAt DateTime?
clientPhone String
workerPhone String
notes String?
isOnPlatform Boolean @default(true)
sourceNote String?
paymentStatus String @default("unpaid")
paymentMethod String?
platformFee Decimal? @db.Decimal(10, 2)
client User @relation("ClientBookings", fields: [clientId], references: [id])
worker ServiceWorkerProfile @relation("WorkerBookings", fields: [workerId], references: [id])
review Review?
}Messaging Models
Conversation
prisma
model Conversation {
id String @id @default(cuid())
type ConversationType
applicationId String?
bookingId String?
quoteId String?
isActive Boolean @default(true)
lastMessageAt DateTime?
lastMessageText String?
createdAt DateTime @default(now())
participants ConversationParticipant[]
messages Message[]
}Message
prisma
model Message {
id String @id @default(cuid())
conversationId String
senderId String
type MessageType @default(text)
content String
images String[] @default([])
status MessageStatus @default(sent)
deliveredAt DateTime?
readAt DateTime?
isDeleted Boolean @default(false)
deletedAt DateTime?
replyToId String?
createdAt DateTime @default(now())
replyTo Message? @relation("MessageReplies", fields: [replyToId], references: [id])
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
sender User @relation("SentMessages", fields: [senderId], references: [id])
}Billing Models
UserCredits
prisma
model UserCredits {
id String @id @default(cuid())
userId String @unique
balance Int @default(0)
frozenBalance Int @default(0) // In escrow
totalPurchased Int @default(0)
totalSpent Int @default(0)
totalEarned Int @default(0) // Workers
totalBonuses Int @default(0)
user User @relation(fields: [userId], references: [id])
transactions CreditTransaction[]
}Subscription
prisma
model Subscription {
id String @id @default(cuid())
stripeSubscriptionId String? @unique
stripeCustomerId String
tier SubscriptionTier @default(free)
status SubscriptionStatus @default(active)
priceId String?
currentPeriodStart DateTime?
currentPeriodEnd DateTime?
billingInterval BillingInterval?
trialStart DateTime?
trialEnd DateTime?
cancelAtPeriodEnd Boolean @default(false)
canceledAt DateTime?
stripeCustomer StripeCustomer @relation(fields: [stripeCustomerId], references: [id])
}Experience Lab Models
ExperienceTrack
prisma
model ExperienceTrack {
id String @id @default(cuid())
name String
slug String @unique
description String
category String // "customer_service", "data_entry"
difficulty String // "beginner", "intermediate", "advanced"
estimatedHours Float
iconUrl String?
isActive Boolean @default(true)
isFree Boolean @default(true)
tasks ExperienceTask[]
enrollments TrackEnrollment[]
certificates ExperienceCertificate[]
}ExperienceTask
prisma
model ExperienceTask {
id String @id @default(cuid())
trackId String
orderIndex Int
title String
description String
instructions String // markdown
taskType String // "multiple_choice", "text_response", "scenario"
taskData Json // Questions, options, rubric
estimatedMinutes Int
passingScore Int @default(70)
maxAttempts Int @default(3)
xpReward Int @default(50)
track ExperienceTrack @relation(fields: [trackId], references: [id])
submissions TaskSubmission[]
@@unique([trackId, orderIndex])
}ExperienceCertificate
prisma
model ExperienceCertificate {
id String @id @default(cuid())
userId String
trackId String
verificationCode String @unique
overallScore Int
pdfUrl String?
issuedAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
track ExperienceTrack @relation(fields: [trackId], references: [id])
@@unique([userId, trackId])
}Database Indexes
Key indexes for performance:
prisma
@@index([phone]) // User phone lookup
@@index([email]) // Employer email lookup
@@index([latitude, longitude]) // GPS queries
@@index([isActive, postedDate(sort: Desc)]) // Job feed
@@index([employerId, status]) // Application filtering
@@index([conversationId, createdAt(sort: Desc)]) // Message pagination