type BookingEmailData = {
bookingCode: string;
complexSlug?: string;
complexName: string;
date: string;
startTime: string;
endTime: string;
courtName: string;
sportName: string;
customerName: string;
price?: number;
};
const APP_BASE_URL = process.env.APP_BASE_URL ?? 'http://localhost:5173';
function formatBookingPrice(price: number): string {
if (price === 0) {
return 'Sin cargo';
}
return new Intl.NumberFormat('es-AR', {
style: 'currency',
currency: 'ARS',
maximumFractionDigits: Number.isInteger(price) ? 0 : 2,
}).format(price);
}
function formatFriendlyDate(isoDate: string): string {
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDate);
if (!match) return isoDate;
const date = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
const weekday = new Intl.DateTimeFormat('es-AR', { weekday: 'long' }).format(date);
const day = date.getDate();
const month = new Intl.DateTimeFormat('es-AR', { month: 'long' }).format(date);
return `${weekday.charAt(0).toUpperCase() + weekday.slice(1)}, ${day} de ${month}`;
}
const BASE_STYLES = `
body {
margin: 0;
padding: 0;
background-color: #edf7f4;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
* {
box-sizing: border-box;
}
`;
export function wrapLayout(content: string) {
return `
Playzer
`;
}
export function bookingConfirmationHtml(data: BookingEmailData): string {
const formattedPrice = formatBookingPrice(data.price ?? 0);
const friendlyDate = formatFriendlyDate(data.date);
const content = `
${data.complexName}
|
|
Playzer
|
|
|
|
Tu turno
${friendlyDate}
${data.startTime} — ${data.endTime}
|
|
Precio del turno
${formattedPrice}
|
|
|
|
|
Cancha
${data.courtName} — ${data.sportName}
|
Código de reserva
${data.bookingCode}
|
|
${
data.complexSlug
? `
|
|
`
: ''
}`;
return wrapLayout(content);
}
export function bookingCancelledHtml(data: BookingEmailData): string {
const friendlyDate = formatFriendlyDate(data.date);
const content = `
${data.complexName}
|
|
Playzer
|
|
|
|
Tu turno
${friendlyDate}
${data.startTime} — ${data.endTime}
|
|
|
La reserva ${data.bookingCode}
fue cancelada. Si tenés dudas, contactate con el complejo.
|
|
|
Cancha
${data.courtName} — ${data.sportName}
|
Cliente
${data.customerName}
|
|
`;
return wrapLayout(content);
}
type BookingRescheduledEmailData = BookingEmailData & {
previousCourtName: string;
previousStartTime: string;
previousEndTime: string;
};
export function bookingRescheduledHtml(data: BookingRescheduledEmailData): string {
const friendlyDate = formatFriendlyDate(data.date);
const content = `
${data.complexName}
|
|
Playzer
|
|
|
|
Nuevo turno
${friendlyDate}
${data.startTime} — ${data.endTime}
|
|
|
Cancha anterior
${data.previousCourtName} — ${data.sportName}
|
Cancha nueva
${data.courtName} — ${data.sportName}
|
|
|
Horario anterior
${data.previousStartTime} — ${data.previousEndTime}
|
Horario nuevo
${data.startTime} — ${data.endTime}
|
|
|
Cliente
${data.customerName}
|
Código de reserva
${data.bookingCode}
|
|
${
data.complexSlug
? `
|
|
`
: ''
}`;
return wrapLayout(content);
}
export function bookingNoShowHtml(data: BookingEmailData): string {
const friendlyDate = formatFriendlyDate(data.date);
const content = `
${data.complexName}
|
|
Playzer
|
|
|
|
Tu turno
${friendlyDate}
${data.startTime} — ${data.endTime}
|
|
|
La reserva ${data.bookingCode}
fue registrada como no concretada por falta de asistencia.
|
|
|
Cancha
${data.courtName} — ${data.sportName}
|
Cliente
${data.customerName}
|
|
`;
return wrapLayout(content);
}