229 lines
8.2 KiB
Plaintext
229 lines
8.2 KiB
Plaintext
// VA-P28f: public booking and availability for HiveGrid's own sales team.
|
|
//
|
|
// These records never carry tenantId. Public requests first resolve a published
|
|
// page through a narrow SECURITY DEFINER function and then bind app.user_id to
|
|
// the page owner. Provider actions are performed by bounded worker contracts.
|
|
|
|
enum AdminAvailabilityDay {
|
|
monday
|
|
tuesday
|
|
wednesday
|
|
thursday
|
|
friday
|
|
saturday
|
|
sunday
|
|
}
|
|
|
|
enum AdminAvailabilityOverrideKind {
|
|
unavailable
|
|
custom_window
|
|
}
|
|
|
|
enum AdminBookingStatus {
|
|
reserved
|
|
confirmed
|
|
cancelled
|
|
rescheduled
|
|
completed
|
|
no_show
|
|
provider_sync_failed
|
|
}
|
|
|
|
enum AdminBookingAssignmentKind {
|
|
round_robin
|
|
manual
|
|
}
|
|
|
|
enum AdminBookingReminderStatus {
|
|
pending
|
|
processing
|
|
sent
|
|
skipped
|
|
failed
|
|
cancelled
|
|
}
|
|
|
|
model AdminMeetingType {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
archivedAt DateTime?
|
|
createdByAdminUserId String
|
|
name String
|
|
slug String @unique
|
|
description String?
|
|
durationMinutes Int
|
|
bufferBeforeMinutes Int @default(0)
|
|
bufferAfterMinutes Int @default(0)
|
|
slotIncrementMinutes Int @default(15)
|
|
minimumNoticeMinutes Int @default(240)
|
|
bookingHorizonDays Int @default(60)
|
|
hostAdminUserIds String[] @default([])
|
|
intakeQuestions Json @default("[]")
|
|
reminderOffsetsMinutes Int[] @default([])
|
|
active Boolean @default(true)
|
|
|
|
pages AdminBookingPage[]
|
|
bookings AdminBooking[]
|
|
|
|
@@index([active, archivedAt, name], name: "admin_meeting_type_active_idx")
|
|
@@index([createdByAdminUserId, archivedAt], name: "admin_meeting_type_creator_idx")
|
|
}
|
|
|
|
model AdminAvailabilityRule {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
archivedAt DateTime?
|
|
adminUserId String
|
|
dayOfWeek AdminAvailabilityDay
|
|
startMinutes Int
|
|
endMinutes Int
|
|
timeZone String
|
|
|
|
@@index([adminUserId, dayOfWeek, archivedAt], name: "admin_availability_rule_owner_day_idx")
|
|
}
|
|
|
|
model AdminAvailabilityOverride {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
archivedAt DateTime?
|
|
adminUserId String
|
|
date DateTime @db.Date
|
|
kind AdminAvailabilityOverrideKind
|
|
startMinutes Int?
|
|
endMinutes Int?
|
|
timeZone String
|
|
reason String?
|
|
|
|
@@index([adminUserId, date, archivedAt], name: "admin_availability_override_owner_date_idx")
|
|
}
|
|
|
|
model AdminBookingPage {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
archivedAt DateTime?
|
|
createdByAdminUserId String
|
|
meetingTypeId String
|
|
slug String @unique
|
|
title String
|
|
description String?
|
|
published Boolean @default(false)
|
|
tokenVersion Int @default(1)
|
|
allowedEmailDomains String[] @default([])
|
|
maxBookingsPerEmailPerDay Int @default(3)
|
|
|
|
meetingType AdminMeetingType @relation(fields: [meetingTypeId], references: [id], onDelete: Restrict)
|
|
bookings AdminBooking[]
|
|
|
|
@@index([createdByAdminUserId, published, archivedAt], name: "admin_booking_page_creator_status_idx")
|
|
@@index([meetingTypeId, published, archivedAt], name: "admin_booking_page_type_status_idx")
|
|
}
|
|
|
|
model AdminBooking {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
adminUserId String
|
|
bookingPageId String
|
|
meetingTypeId String
|
|
calendarConnectionId String?
|
|
contactId String
|
|
dealId String?
|
|
startsAt DateTime
|
|
endsAt DateTime
|
|
bookerTimeZone String
|
|
status AdminBookingStatus @default(reserved)
|
|
idempotencyKey String @unique
|
|
slotReservationKey String @unique
|
|
providerEventId String?
|
|
providerEventVersion String?
|
|
providerSyncedAt DateTime?
|
|
intakeAnswers Json @default("{}")
|
|
manageTokenVersion Int @default(1)
|
|
manageTokenExpiresAt DateTime
|
|
lastManageNonceHash String?
|
|
confirmedAt DateTime?
|
|
cancelledAt DateTime?
|
|
cancellationReason String?
|
|
rescheduledFromId String? @unique
|
|
|
|
bookingPage AdminBookingPage @relation(fields: [bookingPageId], references: [id], onDelete: Restrict)
|
|
meetingType AdminMeetingType @relation(fields: [meetingTypeId], references: [id], onDelete: Restrict)
|
|
calendarConnection AdminCalendarConnection? @relation(fields: [calendarConnectionId], references: [id], onDelete: SetNull)
|
|
contact AdminContact @relation(fields: [contactId], references: [id], onDelete: Restrict)
|
|
deal AdminDeal? @relation(fields: [dealId], references: [id], onDelete: SetNull)
|
|
rescheduledFrom AdminBooking? @relation("AdminBookingReschedule", fields: [rescheduledFromId], references: [id], onDelete: SetNull)
|
|
rescheduledTo AdminBooking? @relation("AdminBookingReschedule")
|
|
participants AdminBookingParticipant[]
|
|
assignment AdminBookingAssignment?
|
|
reminders AdminBookingReminder[]
|
|
activities AdminActivity[]
|
|
|
|
@@index([adminUserId, startsAt], name: "admin_booking_owner_start_idx")
|
|
@@index([status, startsAt], name: "admin_booking_status_start_idx")
|
|
@@index([contactId, startsAt(sort: Desc)], name: "admin_booking_contact_start_idx")
|
|
@@index([dealId, startsAt(sort: Desc)], name: "admin_booking_deal_start_idx")
|
|
@@index([bookingPageId, startsAt], name: "admin_booking_page_start_idx")
|
|
}
|
|
|
|
model AdminBookingParticipant {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
adminUserId String
|
|
bookingId String
|
|
email String
|
|
name String
|
|
locale String?
|
|
timeZone String?
|
|
isOrganizer Boolean @default(false)
|
|
|
|
booking AdminBooking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([bookingId, email], name: "admin_booking_participant_email_key")
|
|
@@index([adminUserId, createdAt(sort: Desc)], name: "admin_booking_participant_owner_idx")
|
|
}
|
|
|
|
model AdminBookingAssignment {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
adminUserId String
|
|
bookingId String @unique
|
|
kind AdminBookingAssignmentKind
|
|
selectedHostAdminUserId String
|
|
previousHostAdminUserId String?
|
|
overriddenByAdminUserId String?
|
|
eligibleHostAdminUserIds String[]
|
|
excludedHosts Json @default("[]")
|
|
loadSnapshot Json
|
|
selectionReason String
|
|
|
|
booking AdminBooking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([adminUserId, createdAt(sort: Desc)], name: "admin_booking_assignment_owner_idx")
|
|
@@index([selectedHostAdminUserId, createdAt(sort: Desc)], name: "admin_booking_assignment_host_idx")
|
|
}
|
|
|
|
model AdminBookingReminder {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
adminUserId String
|
|
bookingId String
|
|
offsetMinutes Int
|
|
scheduledFor DateTime
|
|
status AdminBookingReminderStatus @default(pending)
|
|
idempotencyKey String @unique
|
|
attemptCount Int @default(0)
|
|
sentAt DateTime?
|
|
lastErrorCode String?
|
|
|
|
booking AdminBooking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([bookingId, offsetMinutes], name: "admin_booking_reminder_offset_key")
|
|
@@index([status, scheduledFor], name: "admin_booking_reminder_due_idx")
|
|
@@index([adminUserId, scheduledFor], name: "admin_booking_reminder_owner_idx")
|
|
}
|