Add WhatsApp API integration for automated receipt delivery Enforce country codes for all phone number inputs (defaults to +60)
111 lines
3.7 KiB
TypeScript
111 lines
3.7 KiB
TypeScript
import type { BookingCapacitySettings, BookingMode, PublicBooking, TicketType } from '~~/shared/booking'
|
|
|
|
import {
|
|
formatBookingCurrency,
|
|
getTicketCatalogItem,
|
|
isBookingMode,
|
|
isTicketType
|
|
} from '~~/shared/booking'
|
|
import { hasValidFullName, isValidPhoneNumber, normalizeFullName, normalizePhoneNumber } from '~~/shared/auth'
|
|
|
|
import { assertBadRequest } from './http'
|
|
|
|
export function parseCreateBookingInput(body: {
|
|
customerName?: string
|
|
customerPhone?: string
|
|
bookingMode?: BookingMode | string | null
|
|
quantity?: number
|
|
ticketType?: TicketType
|
|
personInChargeId?: string
|
|
}) {
|
|
const customerName = normalizeFullName(body.customerName || '')
|
|
const customerPhone = normalizePhoneNumber(body.customerPhone || '')
|
|
const bookingMode = typeof body.bookingMode === 'string' ? body.bookingMode.trim().toLowerCase() : body.bookingMode
|
|
const ticketType = body.ticketType
|
|
const quantity = Number(body.quantity)
|
|
const personInChargeId = (body.personInChargeId || '').trim()
|
|
|
|
assertBadRequest(hasValidFullName(customerName), 'Guest or organizer name must be at least 2 characters')
|
|
assertBadRequest(isValidPhoneNumber(customerPhone), 'Phone number must include a country code, e.g. +60123456789')
|
|
assertBadRequest(isBookingMode(bookingMode), 'Booking mode is invalid')
|
|
assertBadRequest(Number.isInteger(quantity) && quantity >= 1, 'Quantity must be a whole number of at least 1')
|
|
assertBadRequest(isTicketType(ticketType), 'Ticket category is invalid')
|
|
assertBadRequest(personInChargeId, 'Person in charge is required')
|
|
|
|
return {
|
|
customerName,
|
|
customerPhone,
|
|
bookingMode,
|
|
quantity,
|
|
ticketType,
|
|
personInChargeId
|
|
}
|
|
}
|
|
|
|
export function buildBookingMessage(booking: PublicBooking, confirmationUrl: string) {
|
|
const ticket = getTicketCatalogItem(booking.ticketType)
|
|
const ticketLabel = ticket?.label || booking.ticketType.toUpperCase()
|
|
|
|
return [
|
|
"I'd like to book tickets for the DAP Johor 60th Anniversary Celebration.",
|
|
'',
|
|
`Name: ${booking.customerName}`,
|
|
`Phone Number: ${booking.customerPhone}`,
|
|
`Seats: ${booking.seatCount}`,
|
|
`Ticket Category: ${ticketLabel}`,
|
|
`Total Price: ${formatBookingCurrency(booking.totalPrice)}`,
|
|
'',
|
|
'PIC confirmation link:',
|
|
confirmationUrl
|
|
].join('\n')
|
|
}
|
|
|
|
export function parseSeatShareInput(body: {
|
|
shared?: boolean
|
|
recipientName?: string | null
|
|
recipientPhone?: string | null
|
|
}) {
|
|
const shared = body.shared
|
|
const recipientName = normalizeFullName(body.recipientName || '')
|
|
const recipientPhone = normalizePhoneNumber(body.recipientPhone || '')
|
|
|
|
assertBadRequest(typeof shared === 'boolean', 'Shared flag is required')
|
|
|
|
if (shared) {
|
|
assertBadRequest(!recipientName || hasValidFullName(recipientName), 'Recipient name must be at least 2 characters')
|
|
assertBadRequest(!recipientPhone || isValidPhoneNumber(recipientPhone), 'Recipient phone number must include a country code, e.g. +60123456789')
|
|
}
|
|
|
|
return {
|
|
shared,
|
|
recipientName: recipientName || null,
|
|
recipientPhone: recipientPhone || null
|
|
}
|
|
}
|
|
|
|
export function parseBookingCapacityInput(body: {
|
|
totalSeats?: number | string | null
|
|
}): Pick<BookingCapacitySettings, 'totalSeats'> {
|
|
const totalSeats = parseOptionalInteger(body.totalSeats)
|
|
|
|
assertBadRequest(totalSeats === null || totalSeats >= 0, 'Total seats must be 0 or greater')
|
|
|
|
return {
|
|
totalSeats
|
|
}
|
|
}
|
|
|
|
function parseOptionalInteger(value: number | string | null | undefined) {
|
|
if (value === null || value === undefined || value === '') {
|
|
return null
|
|
}
|
|
|
|
const parsed = typeof value === 'number'
|
|
? value
|
|
: Number.parseInt(String(value), 10)
|
|
|
|
assertBadRequest(Number.isInteger(parsed), 'Capacity values must be whole numbers')
|
|
|
|
return parsed
|
|
}
|