export type BookingMode = 'table' | 'pax' export type TicketType = 'vip' | 'supporter' export type BookingStatus = 'pending' | 'confirmed' export const BOOKING_MODE_OPTIONS = [ { value: 'table', label: 'Table (10 pax)' }, { value: 'pax', label: 'Person' } ] satisfies Array<{ value: BookingMode, label: string }> export const BOOKING_TICKET_CATALOG = [ { value: 'vip', label: 'VIP', description: 'RM150 / pax', price: 150 }, { value: 'supporter', label: 'Supporter', description: 'RM60 / pax', price: 60 } ] satisfies Array<{ value: TicketType, label: string, description: string, price: number }> export interface PublicBooking { id: string confirmationToken: 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 BookingCapacitySettings { totalTables: number | null updatedAt: string | null } export interface BookingInventorySummary { totalTables: number | null totalCapacitySeats: number | null soldTables: number pendingTables: number soldSeats: number pendingSeats: number soldCapacitySeats: number pendingCapacitySeats: number leftTables: number | null leftSeats: number | null leftCapacitySeats: 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 === 'pax' } 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 (10 pax each)' : 'Per person' } export function getBookingStatusLabel(value: BookingStatus) { return value === 'confirmed' ? 'Confirmed' : 'Pending PIC confirmation' } export function getSeatCount(bookingMode: BookingMode, quantity: number) { return bookingMode === 'table' ? quantity * 10 : 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 calculateBookingInventorySummary( bookings: Pick[], settings: BookingCapacitySettings ): BookingInventorySummary { const soldTables = bookings .filter((booking) => booking.status === 'confirmed' && booking.bookingMode === 'table') .reduce((total, booking) => total + booking.quantity, 0) const pendingTables = bookings .filter((booking) => booking.status === 'pending' && booking.bookingMode === 'table') .reduce((total, booking) => total + booking.quantity, 0) const soldSeats = bookings .filter((booking) => booking.status === 'confirmed' && booking.bookingMode === 'pax') .reduce((total, booking) => total + booking.seatCount, 0) const pendingSeats = bookings .filter((booking) => booking.status === 'pending' && booking.bookingMode === 'pax') .reduce((total, booking) => total + booking.seatCount, 0) const totalCapacitySeats = settings.totalTables === null ? null : settings.totalTables * 10 const soldCapacitySeats = (soldTables * 10) + soldSeats const pendingCapacitySeats = (pendingTables * 10) + pendingSeats const leftCapacitySeats = totalCapacitySeats === null ? null : Math.max(totalCapacitySeats - soldCapacitySeats, 0) return { totalTables: settings.totalTables, totalCapacitySeats, soldTables, pendingTables, soldSeats, pendingSeats, soldCapacitySeats, pendingCapacitySeats, leftTables: leftCapacitySeats === null ? null : Math.floor(leftCapacitySeats / 10), leftSeats: leftCapacitySeats === null ? null : leftCapacitySeats % 10, leftCapacitySeats } }