Migration : ContentPage.id devient PK composite (slug, lang) au lieu de slug seul, pour stocker une version FR et une version EN du même slug. Index sur slug seul pour les lookups. Schema Prisma : @@id([slug, lang]). Helpers : - getContentPage(slug, lang) avec fallback FR si la version dans la langue demandée n'existe pas - listContentPages(category?, lang?) accepte un filtre lang - upsertContentPage : utilise le composite key Pages publiques (a-propos, faq, comment-ca-marche, pour-comites-entreprise, devenir-loueur, cgv, mentions-legales, politique-de-confidentialite) : ajoutent un appel à getLocale() et le passent à getContentPage. Seeds : - src/lib/plugins/seeds/content-pages-en.ts : 8 pages traduites en anglais - hook onEnable du plugin i18n-fr-en : seed EN pages au toggle on. Désactiver i18n n'efface pas les EN pages (elles dorment, fallback FR reprend). Résultat : quand l'utilisateur switche vers EN, /a-propos, /faq, /cgv, etc. basculent en anglais. Le contenu hors-DB (composants UI) bascule déjà via les dictionnaires de la PR i18n-fr-en initiale.
328 lines
8.3 KiB
Text
328 lines
8.3 KiB
Text
generator client {
|
|
provider = "prisma-client"
|
|
output = "../src/generated/prisma"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
}
|
|
|
|
enum UserRole {
|
|
OWNER
|
|
CE_MANAGER
|
|
CE_MEMBER
|
|
TOURIST
|
|
ADMIN
|
|
}
|
|
|
|
enum CarbetStatus {
|
|
DRAFT
|
|
PUBLISHED
|
|
ARCHIVED
|
|
}
|
|
|
|
enum MediaType {
|
|
PHOTO
|
|
VIDEO
|
|
}
|
|
|
|
enum AvailabilityScope {
|
|
PUBLIC
|
|
CE_ONLY
|
|
}
|
|
|
|
enum AvailabilityBlockReason {
|
|
NONE
|
|
CE_BLOCKED
|
|
WEEKEND_BLOCKED
|
|
}
|
|
|
|
enum BookingStatus {
|
|
PENDING
|
|
CONFIRMED
|
|
CANCELLED
|
|
COMPLETED
|
|
}
|
|
|
|
enum PaymentStatus {
|
|
PENDING
|
|
AUTHORIZED
|
|
SUCCEEDED
|
|
FAILED
|
|
REFUNDED
|
|
}
|
|
|
|
enum SubscriptionStatus {
|
|
TRIAL
|
|
ACTIVE
|
|
PAST_DUE
|
|
CANCELED
|
|
}
|
|
|
|
enum AccessType {
|
|
ROAD_AND_RIVER
|
|
RIVER_ONLY
|
|
}
|
|
|
|
enum TransportMode {
|
|
OWNER_PROVIDES
|
|
SELF_ARRANGE
|
|
PARTNER_PROVIDER
|
|
}
|
|
|
|
model Organization {
|
|
id String @id @default(cuid())
|
|
name String
|
|
slug String @unique
|
|
description String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
members User[]
|
|
|
|
@@index([name])
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
email String @unique
|
|
passwordHash String
|
|
firstName String
|
|
lastName String
|
|
phone String?
|
|
role UserRole
|
|
organizationId String?
|
|
avatarUrl String?
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
organization Organization? @relation(fields: [organizationId], references: [id], onDelete: SetNull)
|
|
carbets Carbet[] @relation("CarbetOwner")
|
|
bookings Booking[] @relation("BookingTenant")
|
|
reviews Review[] @relation("ReviewAuthor")
|
|
subscriptions Subscription[]
|
|
|
|
@@index([organizationId])
|
|
@@index([role])
|
|
}
|
|
|
|
model Carbet {
|
|
id String @id @default(cuid())
|
|
ownerId String
|
|
title String
|
|
slug String @unique
|
|
description String
|
|
river String
|
|
latitude Decimal @db.Decimal(9, 6)
|
|
longitude Decimal @db.Decimal(9, 6)
|
|
embarkPoint String
|
|
// Pirogue : obligatoire pour RIVER_ONLY, optionnelle pour ROAD_AND_RIVER
|
|
// (estimation pour ceux qui veulent quand même venir en pirogue).
|
|
pirogueDurationMin Int?
|
|
accessType AccessType @default(ROAD_AND_RIVER)
|
|
// Détails d'accès route pour ROAD_AND_RIVER (GPS, distance, type de piste).
|
|
roadAccessNote String?
|
|
capacity Int
|
|
// Contraintes séjour (plugin min-stay). null = pas de contrainte.
|
|
minStayNights Int?
|
|
maxStayNights Int?
|
|
minCapacity Int?
|
|
// Contraintes saisonnières (plugin seasonality). JSON libre, schéma type :
|
|
// { closedInLowWater: bool, closedSeasons: ["WET"|"DRY"|"LOW_WATER"][], note: string }
|
|
seasonalConstraints Json?
|
|
// Plugin pirogue-providers : qui organise le transport ?
|
|
transportMode TransportMode?
|
|
pirogueProviderId String?
|
|
status CarbetStatus @default(DRAFT)
|
|
lastBookedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
owner User @relation("CarbetOwner", fields: [ownerId], references: [id], onDelete: Restrict)
|
|
pirogueProvider PirogueProvider? @relation(fields: [pirogueProviderId], references: [id], onDelete: SetNull)
|
|
amenities CarbetAmenity[]
|
|
media Media[]
|
|
availabilities Availability[]
|
|
bookings Booking[]
|
|
reviews Review[]
|
|
subscriptions Subscription[]
|
|
|
|
@@index([ownerId])
|
|
@@index([status])
|
|
@@index([river])
|
|
@@index([accessType])
|
|
@@index([pirogueProviderId])
|
|
}
|
|
|
|
model PirogueProvider {
|
|
id String @id @default(cuid())
|
|
name String
|
|
contactEmail String?
|
|
contactPhone String?
|
|
rivers String[] @default([])
|
|
pricingNote String?
|
|
description String?
|
|
active Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
carbets Carbet[]
|
|
|
|
@@index([active])
|
|
}
|
|
|
|
model Amenity {
|
|
id String @id @default(cuid())
|
|
key String @unique
|
|
label String
|
|
description String?
|
|
createdAt DateTime @default(now())
|
|
|
|
carbets CarbetAmenity[]
|
|
}
|
|
|
|
model CarbetAmenity {
|
|
carbetId String
|
|
amenityId String
|
|
|
|
carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Cascade)
|
|
amenity Amenity @relation(fields: [amenityId], references: [id], onDelete: Cascade)
|
|
|
|
@@id([carbetId, amenityId])
|
|
@@index([amenityId])
|
|
}
|
|
|
|
model Media {
|
|
id String @id @default(cuid())
|
|
carbetId String
|
|
type MediaType
|
|
s3Key String
|
|
s3Url String
|
|
sortOrder Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
|
|
carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([carbetId, sortOrder])
|
|
}
|
|
|
|
model Availability {
|
|
id String @id @default(cuid())
|
|
carbetId String
|
|
startDate DateTime
|
|
endDate DateTime
|
|
scope AvailabilityScope @default(PUBLIC)
|
|
blockReason AvailabilityBlockReason @default(NONE)
|
|
isAvailable Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([carbetId])
|
|
@@index([scope, blockReason])
|
|
@@index([startDate, endDate])
|
|
}
|
|
|
|
model Booking {
|
|
id String @id @default(cuid())
|
|
carbetId String
|
|
tenantId String
|
|
startDate DateTime
|
|
endDate DateTime
|
|
guestCount Int
|
|
status BookingStatus @default(PENDING)
|
|
amount Decimal @db.Decimal(10, 2)
|
|
currency String @default("EUR")
|
|
paymentStatus PaymentStatus @default(PENDING)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Restrict)
|
|
tenant User @relation("BookingTenant", fields: [tenantId], references: [id], onDelete: Restrict)
|
|
|
|
review Review?
|
|
|
|
@@index([carbetId])
|
|
@@index([tenantId])
|
|
@@index([status, paymentStatus])
|
|
@@index([startDate, endDate])
|
|
}
|
|
|
|
model Subscription {
|
|
id String @id @default(cuid())
|
|
ownerId String
|
|
carbetId String
|
|
provider String
|
|
providerSubId String? @unique
|
|
status SubscriptionStatus @default(TRIAL)
|
|
startedAt DateTime @default(now())
|
|
renewedAt DateTime?
|
|
canceledAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
owner User @relation(fields: [ownerId], references: [id], onDelete: Restrict)
|
|
carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([ownerId])
|
|
@@index([carbetId])
|
|
@@index([status])
|
|
}
|
|
|
|
model Review {
|
|
id String @id @default(cuid())
|
|
bookingId String @unique
|
|
carbetId String
|
|
authorId String
|
|
rating Int
|
|
comment String?
|
|
hostResponse String?
|
|
hostRespondedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
|
|
carbet Carbet @relation(fields: [carbetId], references: [id], onDelete: Restrict)
|
|
author User @relation("ReviewAuthor", fields: [authorId], references: [id], onDelete: Restrict)
|
|
|
|
@@index([carbetId])
|
|
@@index([authorId])
|
|
}
|
|
|
|
model Plugin {
|
|
key String @id
|
|
name String
|
|
description String
|
|
category String
|
|
version String @default("0.1.0")
|
|
enabled Boolean @default(false)
|
|
config Json @default("{}")
|
|
migrationsApplied String[] @default([])
|
|
installedAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
lastEnabledAt DateTime?
|
|
lastDisabledAt DateTime?
|
|
|
|
@@index([category])
|
|
@@index([enabled])
|
|
}
|
|
|
|
model ContentPage {
|
|
slug String
|
|
lang String @default("fr")
|
|
title String
|
|
body String
|
|
// 'general' (about, faq, ...) ou 'legal' (cgv, mentions, ...)
|
|
category String @default("general")
|
|
published Boolean @default(true)
|
|
lastEditedBy String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@id([slug, lang])
|
|
@@index([slug])
|
|
@@index([category])
|
|
@@index([published])
|
|
}
|