generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ===================================================================== // Core Reference Data // ===================================================================== model State { id String @id @default(cuid()) name String @unique abbr String @unique @db.VarChar(2) lat Float lng Float regulation Regulation? equipmentRequirements EquipmentRequirement[] contact Contact? seasonalRestrictions SeasonalRestriction[] truckStops TruckStop[] bridges Bridge[] weighStations WeighStation[] alerts Alert[] @@map("states") } model Regulation { id String @id @default(cuid()) stateId String @unique state State @relation(fields: [stateId], references: [id], onDelete: Cascade) permitWidth String permitHeight String permitLength String permitWeight String escortWidth String escortHeight String escortLength String escortWeight String travelRestrictions String holidays String agency String url String notes String @db.Text @@map("regulations") } model EquipmentRequirement { id String @id @default(cuid()) stateId String state State @relation(fields: [stateId], references: [id], onDelete: Cascade) type String // "escort" or "carrier" certification String @default("") vehicle String @default("") signs String @default("") lights String @default("") heightPole String @default("") flags String @default("") safetyGear String @default("") @db.Text communication String @default("") @@map("equipment_requirements") } model Contact { id String @id @default(cuid()) stateId String @unique state State @relation(fields: [stateId], references: [id], onDelete: Cascade) permitPhone String policePhone String email String hours String portalUrl String @@map("contacts") } model SeasonalRestriction { id String @id @default(cuid()) stateId String state State @relation(fields: [stateId], references: [id], onDelete: Cascade) name String type String // "spring_weight", "winter_closure", "harvest", "holiday_blackout" startMonth Int endMonth Int description String @db.Text @@map("seasonal_restrictions") } // ===================================================================== // Geospatial Entities // ===================================================================== model TruckStop { id String @id @default(cuid()) stateId String state State @relation(fields: [stateId], references: [id], onDelete: Cascade) name String lat Float lng Float address String hasOversizeParking Boolean @default(false) entranceHeight String @default("") entranceWidth String @default("") lotSqFt Int? facilities Json @default("[]") // ["fuel","food","showers","restrooms"] satelliteUrl String @default("") phone String @default("") contributions Contribution[] @@map("truck_stops") } model Bridge { id String @id @default(cuid()) stateId String state State @relation(fields: [stateId], references: [id], onDelete: Cascade) name String lat Float lng Float route String heightClearance Float // in feet widthClearance Float? // in feet, null = unrestricted weightLimit Float? // in lbs, null = unrestricted lastVerified DateTime? @@map("bridges") } model WeighStation { id String @id @default(cuid()) stateId String state State @relation(fields: [stateId], references: [id], onDelete: Cascade) name String lat Float lng Float direction String // "NB", "SB", "EB", "WB" route String prePass Boolean @default(false) hours String @default("") currentStatus String @default("unknown") // "open", "closed", "unknown" lastStatusUpdate DateTime? lastStatusUserId String? contributions Contribution[] @@map("weigh_stations") } // ===================================================================== // Users & Auth // ===================================================================== model User { id String @id @default(cuid()) email String @unique passwordHash String name String role String @default("driver") // "driver", "carrier", "escort", "admin" tier String @default("free") // "free", "subscriber", "premium" createdAt DateTime @default(now()) updatedAt DateTime @updatedAt escortProfile EscortProfile? loads Load[] orders Order[] documents Document[] contributions Contribution[] @@map("users") } model EscortProfile { id String @id @default(cuid()) userId String @unique user User @relation(fields: [userId], references: [id], onDelete: Cascade) lat Float lng Float radiusMiles Int @default(100) certifications Json @default("[]") // ["TX","OK","CA"] vehicleType String @default("") availability String @default("available") // "available", "on_job", "unavailable" rating Float @default(0) ratingCount Int @default(0) phone String @default("") bio String @default("") @db.Text @@map("escort_profiles") } // ===================================================================== // Transactional Data // ===================================================================== model Load { id String @id @default(cuid()) posterId String poster User @relation(fields: [posterId], references: [id], onDelete: Cascade) origin String destination String pickupDate DateTime width String height String length String weight String description String @default("") @db.Text escortsNeeded Int @default(1) status String @default("open") // "open", "assigned", "in_transit", "delivered", "cancelled" createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("loads") } model Order { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) // Load details (embedded, since orders may not reference a load board listing) origin String destination String pickupDate DateTime width String @default("") height String @default("") length String @default("") weight String @default("") loadType String @default("") escortsNeeded Int @default(1) status String @default("pending") // "pending", "confirmed", "in_progress", "completed", "cancelled" notes String @default("") @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("orders") } model Document { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) type String // "permit", "insurance", "certification", "license", "other" filename String filepath String mimeType String @default("") sizeBytes Int @default(0) expiresAt DateTime? createdAt DateTime @default(now()) @@map("documents") } model Contribution { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) entityType String // "truck_stop", "weigh_station", "bridge", "regulation" entityId String type String // "info", "flag", "confirm" content String @db.Text createdAt DateTime @default(now()) // Optional relations (polymorphic — only one will be set) truckStop TruckStop? @relation(fields: [truckStopId], references: [id]) truckStopId String? weighStation WeighStation? @relation(fields: [weighStationId], references: [id]) weighStationId String? @@map("contributions") } // ===================================================================== // Alerts // ===================================================================== model Alert { id String @id @default(cuid()) stateId String state State @relation(fields: [stateId], references: [id], onDelete: Cascade) type String // "construction", "closure", "weather", "wind" route String description String @db.Text severity String @default("info") // "info", "warning", "critical" startsAt DateTime endsAt DateTime? createdAt DateTime @default(now()) @@map("alerts") }