Add EmailComponents and ShortcodeProcessor to shared email template renderer
- Add EmailComponents utility functions (button, alert, otpBox, etc.) - Add ShortcodeProcessor class with DEFAULT_DATA - Now matches src/lib/email-templates/master-template.ts exactly - Edge functions can now use helpful components like EmailComponents.otpBox()
This commit is contained in:
@@ -250,3 +250,143 @@ export class EmailTemplateRenderer {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reusable Email Components
|
||||||
|
export const EmailComponents = {
|
||||||
|
// Buttons
|
||||||
|
button: (text: string, url: string, fullwidth = false) =>
|
||||||
|
`<p style="margin-top: 20px; text-align: ${fullwidth ? 'center' : 'left'};">
|
||||||
|
<a href="${url}" class="${fullwidth ? 'btn-full' : 'btn'}">${text}</a>
|
||||||
|
</p>`,
|
||||||
|
|
||||||
|
// Alert boxes
|
||||||
|
alert: (type: 'success' | 'danger' | 'info', content: string) =>
|
||||||
|
`<blockquote class="alert-${type}">
|
||||||
|
${content}
|
||||||
|
</blockquote>`,
|
||||||
|
|
||||||
|
// Code blocks
|
||||||
|
codeBlock: (code: string, language = '') =>
|
||||||
|
`<pre><code>${code}</code></pre>`,
|
||||||
|
|
||||||
|
// OTP boxes
|
||||||
|
otpBox: (code: string) =>
|
||||||
|
`<div class="otp-box">${code}</div>`,
|
||||||
|
|
||||||
|
// Info card
|
||||||
|
infoCard: (title: string, items: Array<{label: string; value: string}>) => {
|
||||||
|
const rows = items.map(item =>
|
||||||
|
`<tr>
|
||||||
|
<td>${item.label}</td>
|
||||||
|
<td>${item.value}</td>
|
||||||
|
</tr>`
|
||||||
|
).join('');
|
||||||
|
|
||||||
|
return `
|
||||||
|
<h2>${title}</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Parameter</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${rows}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Divider
|
||||||
|
divider: () => '<hr style="border: 1px solid #000; margin: 30px 0;">',
|
||||||
|
|
||||||
|
// Spacing
|
||||||
|
spacing: (size: 'small' | 'medium' | 'large' = 'medium') => {
|
||||||
|
const sizes = { small: '15px', medium: '25px', large: '40px' };
|
||||||
|
return `<div style="height: ${sizes[size]};"></div>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shortcode processor
|
||||||
|
export class ShortcodeProcessor {
|
||||||
|
private static readonly DEFAULT_DATA = {
|
||||||
|
// User information
|
||||||
|
nama: 'John Doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
|
||||||
|
// Order information
|
||||||
|
order_id: 'ORD-123456',
|
||||||
|
tanggal_pesanan: '22 Desember 2025',
|
||||||
|
total: 'Rp 1.500.000',
|
||||||
|
metode_pembayaran: 'Transfer Bank',
|
||||||
|
status_pesanan: 'Diproses',
|
||||||
|
invoice_url: 'https://with.dwindi.com/orders/ORD-123456',
|
||||||
|
|
||||||
|
// Product information
|
||||||
|
produk: 'Digital Marketing Masterclass',
|
||||||
|
kategori_produk: 'Digital Marketing',
|
||||||
|
harga_produk: 'Rp 1.500.000',
|
||||||
|
deskripsi_produk: 'Kelas lengkap digital marketing dari pemula hingga mahir',
|
||||||
|
|
||||||
|
// Access information
|
||||||
|
link_akses: 'https://with.dwindi.com/access',
|
||||||
|
username_akses: 'john.doe',
|
||||||
|
password_akses: 'Temp123!',
|
||||||
|
kadaluarsa_akses: '22 Desember 2026',
|
||||||
|
|
||||||
|
// Consulting information
|
||||||
|
tanggal_konsultasi: '22 Desember 2025',
|
||||||
|
jam_konsultasi: '14:00',
|
||||||
|
durasi_konsultasi: '60 menit',
|
||||||
|
link_meet: 'https://meet.google.com/example',
|
||||||
|
jenis_konsultasi: 'Digital Marketing Strategy',
|
||||||
|
topik_konsultasi: 'Social Media Marketing for Beginners',
|
||||||
|
|
||||||
|
// Event information
|
||||||
|
judul_event: 'Workshop Digital Marketing',
|
||||||
|
tanggal_event: '25 Desember 2025',
|
||||||
|
jam_event: '19:00',
|
||||||
|
link_event: 'https://with.dwindi.com/events',
|
||||||
|
lokasi_event: 'Zoom Online',
|
||||||
|
kapasitas_event: '100 peserta',
|
||||||
|
|
||||||
|
// Bootcamp/Course information
|
||||||
|
judul_bootcamp: 'Digital Marketing Bootcamp',
|
||||||
|
progres_bootcamp: '75%',
|
||||||
|
modul_selesai: '15 dari 20 modul',
|
||||||
|
modul_selanjutnya: 'Final Assessment',
|
||||||
|
link_progress: 'https://with.dwindi.com/bootcamp/progress',
|
||||||
|
|
||||||
|
// Company information
|
||||||
|
nama_perusahaan: 'ACCESS HUB',
|
||||||
|
website_perusahaan: 'https://with.dwindi.com',
|
||||||
|
email_support: 'support@with.dwindi.com',
|
||||||
|
telepon_support: '+62 812-3456-7890',
|
||||||
|
|
||||||
|
// Payment information
|
||||||
|
bank_tujuan: 'BCA',
|
||||||
|
nomor_rekening: '123-456-7890',
|
||||||
|
atas_nama: 'PT Access Hub Indonesia',
|
||||||
|
jumlah_pembayaran: 'Rp 1.500.000',
|
||||||
|
batas_pembayaran: '22 Desember 2025 23:59',
|
||||||
|
payment_link: 'https://with.dwindi.com/checkout',
|
||||||
|
thank_you_page: 'https://with.dwindi.com/orders/{order_id}'
|
||||||
|
};
|
||||||
|
|
||||||
|
static process(content: string, customData: Record<string, string> = {}): string {
|
||||||
|
const data = { ...this.DEFAULT_DATA, ...customData };
|
||||||
|
|
||||||
|
let processed = content;
|
||||||
|
for (const [key, value] of Object.entries(data)) {
|
||||||
|
const regex = new RegExp(`\\{${key}\\}`, 'g');
|
||||||
|
processed = processed.replace(regex, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDummyData(): Record<string, string> {
|
||||||
|
return this.DEFAULT_DATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user