import nodemailer from 'nodemailer'; import { prisma } from './db'; import { getSettings } from './settings'; export async function sendTicketEmail(bookingId: number) { try { // 1. Fetch active settings const settings = await getSettings(); const { brandName, logoImageUrl, logoIcon, primaryColor, smtpHost, smtpPort, smtpUser, smtpPassword, smtpSenderName, smtpSenderEmail, csPhone, csWhatsapp, csEmail, } = settings; // 2. Guard: Verify if SMTP is configured if (!smtpHost || !smtpUser || !smtpPassword) { console.warn(`[SMTP Email] SMTP is not fully configured (host: "${smtpHost}", user: "${smtpUser}"). Skipping email notification.`); return false; } // 3. Fetch Booking with related Schedule and Route const booking = await prisma.booking.findUnique({ where: { id: bookingId }, include: { seats: true, schedule: { include: { route: true, }, }, }, }); if (!booking) { console.error(`[SMTP Email] Booking with ID ${bookingId} not found.`); return false; } if (!booking.passengerEmail) { console.warn(`[SMTP Email] Passenger email is missing for booking ${booking.bookingCode}.`); return false; } // 4. Set up nodemailer transporter const port = Number(smtpPort) || 587; const transporter = nodemailer.createTransport({ host: smtpHost, port: port, secure: port === 465, // true for 465, false for other ports auth: { user: smtpUser, pass: smtpPassword, }, }); // 5. Formats const formatCurrency = (val: number) => { return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0, }).format(val); }; const formatDate = (date: Date) => { return date.toLocaleDateString('id-ID', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', }); }; const formatTime = (date: Date) => { return date.toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit', }); }; const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'; const ticketUrl = `${appUrl}/ticket/${booking.bookingCode}`; const brandColorHex = primaryColor || '#6366f1'; // 6. Build HTML template with brand color matching theme settings const htmlContent = ` E-Tiket Perjalanan Anda - ${booking.bookingCode}

${brandName}

Halo ${booking.passengerName},

Pembayaran Anda untuk pemesanan tiket perjalanan telah berhasil dikonfirmasi. Berikut adalah rincian e-ticket resmi Anda:

Kode Booking Perjalanan
${booking.bookingCode}
Nama Penumpang
${booking.passengerName}
Kursi Terpilih
${booking.seats.map(s => `${s.seatNumber}`).join('')}
Jenis Armada
${booking.schedule.vehicleType}
Total Pembayaran
${formatCurrency(Number(booking.totalPrice))}
Keberangkatan
${booking.schedule.route.departureCity}
${formatTime(booking.schedule.departureTime)}
${formatDate(booking.schedule.departureTime)}
Tujuan
${booking.schedule.route.arrivalCity}
${formatTime(booking.schedule.arrivalTime)}
${formatDate(booking.schedule.arrivalTime)}

Silakan bawa Kode Booking atau tunjukkan boarding pass digital Anda saat keberangkatan di lokasi loket resmi kami.

`; // 7. Send the email const senderName = smtpSenderName || brandName; const senderEmail = smtpSenderEmail || smtpUser; await transporter.sendMail({ from: `"${senderName}" <${senderEmail}>`, to: booking.passengerEmail, subject: `[E-Tiket Lunas] Boarding Pass Perjalanan Anda - ${booking.bookingCode}`, html: htmlContent, }); console.log(`[SMTP Email] Email receipt successfully sent to ${booking.passengerEmail} for booking ${booking.bookingCode}.`); return true; } catch (error) { console.error('[SMTP Email] Error sending email receipt:', error); return false; } }