/** * Transactional email — service SHELL. * * Each method names a real lifecycle email but currently only logs and no-ops. * The lifecycle (status transitions + the daily cron) drives these calls; when * a provider is wired up later, only the bodies here change — every call site * stays the same. Methods are async so the future real implementation (an HTTP * send) is a drop-in without touching callers. */ export type EmailLogger = { info: (message: string) => void } export class EmailService { constructor(private readonly logger: EmailLogger) {} private async send(method: string, bookingId: string): Promise { this.logger.info(`[email:shell] ${method} → booking ${bookingId} (no-op)`) } /** Enquiry submitted — acknowledge receipt to the client. */ sendEnquiryReceivedEmail(bookingId: string) { return this.send('sendEnquiryReceivedEmail', bookingId) } /** Admin declined the enquiry. */ sendEnquiryDeclinedEmail(bookingId: string) { return this.send('sendEnquiryDeclinedEmail', bookingId) } /** Admin approved the enquiry — dates are now soft-reserved. */ sendReservationConfirmedEmail(bookingId: string) { return this.send('sendReservationConfirmedEmail', bookingId) } /** Day 0: contract (legally binding) + deposit request. Cron R1. */ sendContractAndDepositEmail(bookingId: string) { return this.send('sendContractAndDepositEmail', bookingId) } /** Day 7: deposit still unpaid — reminder. Cron R2. */ sendDepositReminderEmail(bookingId: string) { return this.send('sendDepositReminderEmail', bookingId) } /** Deposit recorded — reservation is now guaranteed. */ sendBookingConfirmedEmail(bookingId: string) { return this.send('sendBookingConfirmedEmail', bookingId) } /** Booking cancelled at a non-enquiry stage. */ sendCancellationEmail(bookingId: string) { return this.send('sendCancellationEmail', bookingId) } }