feat/improve-emails #8
@@ -72,50 +72,6 @@ function wrapLayout(content: string) {
|
||||
</html>`;
|
||||
}
|
||||
|
||||
function headerSection() {
|
||||
return `
|
||||
<tr>
|
||||
<td style="background-color:#0a1628;padding:32px 32px 28px;text-align:center;">
|
||||
<span style="font-size:24px;font-weight:700;color:#10b981;letter-spacing:-0.3px;">Playzer</span>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
function divider() {
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding:0 32px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="height:1px;background-color:#e5e7eb;line-height:1px;font-size:1px;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
function dateTimeBlock(date: string, startTime: string, endTime: string): string {
|
||||
const friendlyDate = formatFriendlyDate(date);
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding:0 32px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#f0fdf4;border-radius:10px;border:1px solid #bbf7d0;">
|
||||
<tr>
|
||||
<td style="padding:20px 24px;text-align:center;">
|
||||
<p style="margin:0;font-size:16px;font-weight:700;color:#065f46;line-height:1.4;">
|
||||
${friendlyDate}
|
||||
</p>
|
||||
<p style="margin:8px 0 0;font-size:22px;font-weight:800;color:#059669;letter-spacing:-0.3px;">
|
||||
${startTime} — ${endTime}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
export function bookingConfirmationHtml(data: BookingEmailData): string {
|
||||
const formattedPrice = formatBookingPrice(data.price ?? 0);
|
||||
const friendlyDate = formatFriendlyDate(data.date);
|
||||
@@ -229,50 +185,99 @@ export function bookingConfirmationHtml(data: BookingEmailData): string {
|
||||
}
|
||||
|
||||
export function bookingCancelledHtml(data: BookingEmailData): string {
|
||||
const friendlyDate = formatFriendlyDate(data.date);
|
||||
|
||||
const content = `
|
||||
${headerSection()}
|
||||
<tr>
|
||||
<td style="padding:32px 32px 8px;">
|
||||
<h1 style="margin:0;font-size:22px;font-weight:700;color:#dc2626;text-align:center;">
|
||||
Reserva cancelada
|
||||
</h1>
|
||||
<p style="margin:8px 0 0;font-size:14px;color:#6b7280;text-align:center;">
|
||||
${data.complexName}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="padding:20px 32px 12px;">
|
||||
${dateTimeBlock(data.date, data.startTime, data.endTime)}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
${divider()}
|
||||
|
||||
<tr>
|
||||
<td style="padding:24px 32px;">
|
||||
<p style="margin:0;font-size:14px;color:#374151;line-height:1.6;">
|
||||
La reserva <strong style="font-family:monospace;font-weight:700;letter-spacing:2px;">${data.bookingCode}</strong>
|
||||
fue cancelada. Si tenés dudas, contactate con el complejo.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="padding:0 32px 24px;">
|
||||
<td style="padding:32px 32px 24px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||
${detailRow('Cancha', `${data.courtName} — ${data.sportName}`)}
|
||||
${detailRow('Cliente', data.customerName)}
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;background-color:#fef2f2;border-radius:999px;padding:4px 12px;">
|
||||
<tr>
|
||||
<td style="font-size:11px;font-weight:700;letter-spacing:0.14em;color:#dc2626;text-transform:uppercase;line-height:1.25rem;">
|
||||
✗ Reserva cancelada
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h1 style="margin:12px 0 0;font-size:28px;font-weight:700;color:#111827;letter-spacing:-0.025em;">
|
||||
${data.complexName}
|
||||
</h1>
|
||||
</td>
|
||||
<td valign="top" align="right" style="white-space:nowrap;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td valign="middle" style="padding-right:8px;">
|
||||
<img src="${APP_BASE_URL}/playzer-favicon-512-transparent.png" alt="Playzer" width="32" height="32" style="display:block;" />
|
||||
</td>
|
||||
<td valign="middle">
|
||||
<span style="font-size:18px;font-weight:700;color:#475569;letter-spacing:-0.025em;">Playzer</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="background-color:#f9fafb;padding:20px 32px;text-align:center;">
|
||||
<p style="margin:0;font-size:12px;color:#9ca3af;">
|
||||
Playzer — Reserva de canchas online
|
||||
</p>
|
||||
<td style="padding:0 32px 24px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#f0fdf4;border-radius:24px;border:1px solid rgba(5,150,105,0.3);">
|
||||
<tr>
|
||||
<td style="padding:20px 24px;">
|
||||
<p style="margin:0;font-size:13px;font-weight:600;color:#15803d;">
|
||||
Tu turno
|
||||
</p>
|
||||
<p style="margin:12px 0 0;font-size:28px;font-weight:900;color:#111827;line-height:1.1;letter-spacing:-0.025em;">
|
||||
${friendlyDate}
|
||||
</p>
|
||||
<p style="margin:8px 0 0;font-size:24px;font-weight:900;color:#059669;line-height:1;letter-spacing:-0.025em;">
|
||||
${data.startTime} — ${data.endTime}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="padding:0 32px 24px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#fef2f2;border-radius:24px;border:1px solid rgba(220,38,38,0.3);">
|
||||
<tr>
|
||||
<td style="padding:20px 24px;">
|
||||
<p style="margin:0;font-size:14px;color:#374151;line-height:1.6;">
|
||||
La reserva <strong style="font-family:monospace;font-weight:700;letter-spacing:2px;">${data.bookingCode}</strong>
|
||||
fue cancelada. Si tenés dudas, contactate con el complejo.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="padding:0 32px 32px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border:1px solid #e5e7eb;border-radius:22px;overflow:hidden;">
|
||||
<tr>
|
||||
<td width="50%" style="padding:16px;border-right:1px solid #e5e7eb;background-color:#ffffff;">
|
||||
<p style="margin:0;font-size:13px;font-weight:500;color:#6b7280;">
|
||||
Cancha
|
||||
</p>
|
||||
<p style="margin:8px 0 0;font-size:14px;font-weight:600;color:#111827;">
|
||||
${data.courtName} — ${data.sportName}
|
||||
</p>
|
||||
</td>
|
||||
<td width="50%" style="padding:16px;background-color:#ffffff;">
|
||||
<p style="margin:0;font-size:13px;font-weight:500;color:#6b7280;">
|
||||
Cliente
|
||||
</p>
|
||||
<p style="margin:8px 0 0;font-size:14px;font-weight:600;color:#111827;">
|
||||
${data.customerName}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
@@ -280,70 +285,101 @@ export function bookingCancelledHtml(data: BookingEmailData): string {
|
||||
}
|
||||
|
||||
export function bookingNoShowHtml(data: BookingEmailData): string {
|
||||
const friendlyDate = formatFriendlyDate(data.date);
|
||||
|
||||
const content = `
|
||||
${headerSection()}
|
||||
<tr>
|
||||
<td style="padding:32px 32px 8px;">
|
||||
<h1 style="margin:0;font-size:22px;font-weight:700;color:#d97706;text-align:center;">
|
||||
Reserva no concretada
|
||||
</h1>
|
||||
<p style="margin:8px 0 0;font-size:14px;color:#6b7280;text-align:center;">
|
||||
${data.complexName}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="padding:20px 32px 12px;">
|
||||
${dateTimeBlock(data.date, data.startTime, data.endTime)}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
${divider()}
|
||||
|
||||
<tr>
|
||||
<td style="padding:24px 32px;">
|
||||
<p style="margin:0;font-size:14px;color:#374151;line-height:1.6;">
|
||||
La reserva <strong style="font-family:monospace;font-weight:700;letter-spacing:2px;">${data.bookingCode}</strong>
|
||||
fue registrada como no concretada por falta de asistencia.
|
||||
</p>
|
||||
<td style="padding:32px 32px 24px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;background-color:#fffbeb;border-radius:999px;padding:4px 12px;">
|
||||
<tr>
|
||||
<td style="font-size:11px;font-weight:700;letter-spacing:0.14em;color:#d97706;text-transform:uppercase;line-height:1.25rem;">
|
||||
Reserva no concretada
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h1 style="margin:12px 0 0;font-size:28px;font-weight:700;color:#111827;letter-spacing:-0.025em;">
|
||||
${data.complexName}
|
||||
</h1>
|
||||
</td>
|
||||
<td valign="top" align="right" style="white-space:nowrap;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td valign="middle" style="padding-right:8px;">
|
||||
<img src="${APP_BASE_URL}/playzer-favicon-512-transparent.png" alt="Playzer" width="32" height="32" style="display:block;" />
|
||||
</td>
|
||||
<td valign="middle">
|
||||
<span style="font-size:18px;font-weight:700;color:#475569;letter-spacing:-0.025em;">Playzer</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="padding:0 32px 24px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||
${detailRow('Cancha', `${data.courtName} — ${data.sportName}`)}
|
||||
${detailRow('Cliente', data.customerName)}
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#f0fdf4;border-radius:24px;border:1px solid rgba(5,150,105,0.3);">
|
||||
<tr>
|
||||
<td style="padding:20px 24px;">
|
||||
<p style="margin:0;font-size:13px;font-weight:600;color:#15803d;">
|
||||
Tu turno
|
||||
</p>
|
||||
<p style="margin:12px 0 0;font-size:28px;font-weight:900;color:#111827;line-height:1.1;letter-spacing:-0.025em;">
|
||||
${friendlyDate}
|
||||
</p>
|
||||
<p style="margin:8px 0 0;font-size:24px;font-weight:900;color:#059669;line-height:1;letter-spacing:-0.025em;">
|
||||
${data.startTime} — ${data.endTime}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="background-color:#f9fafb;padding:20px 32px;text-align:center;">
|
||||
<p style="margin:0;font-size:12px;color:#9ca3af;">
|
||||
Playzer — Reserva de canchas online
|
||||
</p>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
return wrapLayout(content);
|
||||
}
|
||||
|
||||
function detailRow(label: string, value: string): string {
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding:6px 0;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||
<td style="padding:0 32px 24px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#fffbeb;border-radius:24px;border:1px solid rgba(217,119,6,0.3);">
|
||||
<tr>
|
||||
<td style="width:100px;font-size:13px;color:#6b7280;vertical-align:top;padding:2px 0;">
|
||||
${label}
|
||||
<td style="padding:20px 24px;">
|
||||
<p style="margin:0;font-size:14px;color:#374151;line-height:1.6;">
|
||||
La reserva <strong style="font-family:monospace;font-weight:700;letter-spacing:2px;">${data.bookingCode}</strong>
|
||||
fue registrada como no concretada por falta de asistencia.
|
||||
</p>
|
||||
</td>
|
||||
<td style="font-size:13px;font-weight:600;color:#111827;vertical-align:top;padding:2px 0;">
|
||||
${value}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="padding:0 32px 32px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border:1px solid #e5e7eb;border-radius:22px;overflow:hidden;">
|
||||
<tr>
|
||||
<td width="50%" style="padding:16px;border-right:1px solid #e5e7eb;background-color:#ffffff;">
|
||||
<p style="margin:0;font-size:13px;font-weight:500;color:#6b7280;">
|
||||
Cancha
|
||||
</p>
|
||||
<p style="margin:8px 0 0;font-size:14px;font-weight:600;color:#111827;">
|
||||
${data.courtName} — ${data.sportName}
|
||||
</p>
|
||||
</td>
|
||||
<td width="50%" style="padding:16px;background-color:#ffffff;">
|
||||
<p style="margin:0;font-size:13px;font-weight:500;color:#6b7280;">
|
||||
Cliente
|
||||
</p>
|
||||
<p style="margin:8px 0 0;font-size:14px;font-weight:600;color:#111827;">
|
||||
${data.customerName}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
return wrapLayout(content);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user