karbe/prisma/schema.prisma
Claude Integration 68f37f554f feat(plugins): content-pages + legal-pages (Phase 4.1 + 4.3)
Plugin content-pages :
- Modèle Prisma ContentPage (slug PK, title, body markdown, category, published)
- lib/content-pages.ts : helpers upsert/get/list/unpublish
- lib/markdown.ts : mini-renderer markdown server-side sans deps externe
  (h1-h3, paragraphes, gras/italique, liens, listes ul/ol, hr, blockquote,
  échappement HTML)
- ContentPageRenderer server component, applique le theme Guyane (font-serif)
- 5 pages seedées : /a-propos, /faq, /comment-ca-marche,
  /pour-comites-entreprise, /devenir-loueur
- Routes publiques + force-dynamic + guard requirePluginOr404

Plugin legal-pages :
- Réutilise le même modèle ContentPage, catégorie 'legal'
- 3 pages seedées : /cgv, /mentions-legales, /politique-de-confidentialite
  (contenu de base, à valider par avocat avant prod réelle)

Admin :
- /admin/content-pages : table par catégorie, statut publié/dépublié
- /admin/content-pages/[slug] : éditeur markdown + toggle publié
- PATCH /api/admin/content-pages/[slug]

Hooks plugin :
- onEnable seed + republish toutes les pages
- onDisable dépublie toute la catégorie sans la supprimer (preserve les edits)
2026-05-31 10:12:13 +00:00

298 lines
7.6 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
}
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?
status CarbetStatus @default(DRAFT)
lastBookedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
owner User @relation("CarbetOwner", fields: [ownerId], references: [id], onDelete: Restrict)
amenities CarbetAmenity[]
media Media[]
availabilities Availability[]
bookings Booking[]
reviews Review[]
subscriptions Subscription[]
@@index([ownerId])
@@index([status])
@@index([river])
@@index([accessType])
}
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 @id
title String
body String
lang String @default("fr")
// 'general' (about, faq, ...) ou 'legal' (cgv, mentions, ...)
category String @default("general")
published Boolean @default(true)
lastEditedBy String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([category])
@@index([published])
}