export type BookingMode = 'table' | 'seat' export type TicketType = 'vip' | 'supporter' export type BookingStatus = 'pending' | 'confirmed' export const DINNER_EVENT_TITLE = 'DAP JOHOR 60th Anniversary Celebration' export const DINNER_EVENT_DATE_LABEL = 'Saturday, 30 May 2026' export const DINNER_EVENT_TIME_LABEL = '6:30 PM' export const DINNER_EVENT_VENUE = "Yong Peng's Chee Ann Kor" export const TABLE_SEAT_COUNT = 10 export const BOOKING_MODE_OPTIONS = [ { value: 'table', label: `Table (${TABLE_SEAT_COUNT} seats)` }, { value: 'seat', label: 'Seat' } ] satisfies Array<{ value: BookingMode, label: string }> export const BOOKING_TICKET_CATALOG = [ { value: 'vip', label: 'VIP', description: 'RM150 / seat', price: 150 }, { value: 'supporter', label: 'Supporter', description: 'RM60 / seat', price: 60 } ] satisfies Array<{ value: TicketType, label: string, description: string, price: number }> export interface PublicBooking { id: string confirmationToken: string receiptToken: string customerName: string customerPhone: string bookingMode: BookingMode quantity: number seatCount: number ticketType: TicketType unitPrice: number totalPrice: number personInChargeId: string personInChargeName: string personInChargePhoneNumber: string status: BookingStatus createdAt: string confirmedAt: string | null } export interface ReceiptBooking { id: string receiptToken: string customerName: string customerPhone: string bookingMode: BookingMode quantity: number seatCount: number ticketType: TicketType unitPrice: number totalPrice: number status: BookingStatus createdAt: string confirmedAt: string | null } export interface PublicBookingSeat { id: string seatNumber: number seatToken: string recipientName: string | null recipientPhone: string | null sharedAt: string | null createdAt: string updatedAt: string } export interface PublicBookingSeatWithUrl extends PublicBookingSeat { seatUrl: string } export interface PublicBookingReceipt { booking: ReceiptBooking receiptUrl: string seats: PublicBookingSeatWithUrl[] } export interface PublicSeatReceipt { booking: ReceiptBooking seat: PublicBookingSeatWithUrl receiptUrl: string } export interface BookingCapacitySettings { totalSeats: number | null updatedAt: string | null } export interface BookingInventorySummary { totalSeats: number | null soldSeats: number pendingSeats: number leftSeats: number | null } export interface CreateBookingResponse { booking: PublicBooking confirmationUrl: string whatsappUrl: string } export function isBookingMode(value: string | null | undefined): value is BookingMode { return value === 'table' || value === 'seat' } export function isTicketType(value: string | null | undefined): value is TicketType { return value === 'vip' || value === 'supporter' } export function isBookingStatus(value: string | null | undefined): value is BookingStatus { return value === 'pending' || value === 'confirmed' } export function getBookingModeLabel(value: BookingMode) { return value === 'table' ? `Table (${TABLE_SEAT_COUNT} seats each)` : 'Per seat' } export function getBookingStatusLabel(value: BookingStatus) { return value === 'confirmed' ? 'Confirmed' : 'Pending PIC confirmation' } export function getSeatCount(bookingMode: BookingMode, quantity: number) { return bookingMode === 'table' ? quantity * TABLE_SEAT_COUNT : quantity } export function getTicketCatalogItem(ticketType: TicketType) { return BOOKING_TICKET_CATALOG.find((ticket) => ticket.value === ticketType) ?? null } export function formatBookingCurrency(value: number) { return new Intl.NumberFormat('en-MY', { style: 'currency', currency: 'MYR', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(value) } export function getSeatLabel(seatNumber: number) { return `Seat ${seatNumber}` } export function calculateBookingInventorySummary( bookings: Pick[], settings: BookingCapacitySettings ): BookingInventorySummary { const soldSeats = bookings .filter((booking) => booking.status === 'confirmed') .reduce((total, booking) => total + booking.seatCount, 0) const pendingSeats = bookings .filter((booking) => booking.status === 'pending') .reduce((total, booking) => total + booking.seatCount, 0) const leftSeats = settings.totalSeats === null ? null : Math.max(settings.totalSeats - soldSeats, 0) return { totalSeats: settings.totalSeats, soldSeats, pendingSeats, leftSeats } }