Fix email template preview UX and add comprehensive shortcodes

1. Remove duplicate close button in preview modal
- Removed extra X button in DialogHeader since Dialog already has close functionality

2. Fix test email to include master layout
- Process shortcodes and render with master template before sending
- Import EmailTemplateRenderer and ShortcodeProcessor in sendTestEmail
- Send complete HTML email with header, footer, and styling applied

3. Add comprehensive table shortcodes for all notification types
- User info: {nama}, {email}
- Order info: {order_id}, {tanggal_pesanan}, {total}, {metode_pembayaran}, {status_pesanan}, {invoice_url}
- Product info: {produk}, {kategori_produk}, {harga_produk}, {deskripsi_produk}
- Access info: {link_akses}, {username_akses}, {password_akses}, {kadaluarsa_akses}
- Consulting: {tanggal_konsultasi}, {jam_konsultasi}, {durasi_konsultasi}, {jenis_konsultasi}, {topik_konsultasi}
- Event: {judul_event}, {tanggal_event}, {jam_event}, {link_event}, {lokasi_event}, {kapasitas_event}
- Bootcamp: {judul_bootcamp}, {progres_bootcamp}, {modul_selesai}, {modul_selanjutnya}, {link_progress}
- Company: {nama_perusahaan}, {website_perusahaan}, {email_support}, {telepon_support}
- Payment: {bank_tujuan}, {nomor_rekening}, {atas_nama}, {jumlah_pembayaran}, {batas_pembayaran}

4. Improve shortcode UI display
- Scrollable shortcode list with better organization
- Show available shortcodes summary
- Categorize shortcodes by type (User, Orders, Products, etc.)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
dwindown
2025-12-22 21:13:09 +07:00
parent 4bb6e8d08c
commit 0668ed22a7
3 changed files with 99 additions and 17 deletions

View File

@@ -88,13 +88,6 @@ export function EmailTemplatePreview({
<DialogDescription> <DialogDescription>
Preview template email dengan master styling Preview template email dengan master styling
</DialogDescription> </DialogDescription>
<Button
variant="ghost"
size="icon"
onClick={onClose}
>
<X className="w-4 h-4" />
</Button>
</DialogHeader> </DialogHeader>
<div className="space-y-4"> <div className="space-y-4">
@@ -188,18 +181,51 @@ export function EmailTemplatePreview({
{/* Shortcodes Used */} {/* Shortcodes Used */}
<div className="p-3 bg-blue-50 border border-blue-200 rounded"> <div className="p-3 bg-blue-50 border border-blue-200 rounded">
<h4 className="font-semibold text-sm mb-2">Shortcodes Used in This Template:</h4> <h4 className="font-semibold text-sm mb-2">Shortcodes Used in This Template:</h4>
<div className="grid grid-cols-2 gap-2 text-sm"> <div className="grid grid-cols-2 md:grid-cols-3 gap-2 text-sm max-h-32 overflow-y-auto">
{['{nama}', '{email}', '{order_id}', '{tanggal_pesanan}', '{total}', '{metode_pembayaran}', {[
'{produk}', '{link_akses}', '{tanggal_konsultasi}', '{jam_konsultasi}', '{link_meet}', // User information
'{judul_event}', '{tanggal_event}', '{jam_event}', '{link_event}'].filter(shortcode => '{nama}', '{email}',
(template.email_subject && template.email_subject.includes(shortcode)) || // Order information
(template.email_body_html && template.email_body_html.includes(shortcode)) '{order_id}', '{tanggal_pesanan}', '{total}', '{metode_pembayaran}', '{status_pesanan}', '{invoice_url}',
// Product information
'{produk}', '{kategori_produk}', '{harga_produk}', '{deskripsi_produk}',
// Access information
'{link_akses}', '{username_akses}', '{password_akses}', '{kadaluarsa_akses}',
// Consulting information
'{tanggal_konsultasi}', '{jam_konsultasi}', '{durasi_konsultasi}', '{link_meet}',
'{jenis_konsultasi}', '{topik_konsultasi}',
// Event information
'{judul_event}', '{tanggal_event}', '{jam_event}', '{link_event}', '{lokasi_event}', '{kapasitas_event}',
// Bootcamp/Course information
'{judul_bootcamp}', '{progres_bootcamp}', '{modul_selesai}', '{modul_selanjutnya}', '{link_progress}',
// Company information
'{nama_perusahaan}', '{website_perusahaan}', '{email_support}', '{telepon_support}',
// Payment information
'{bank_tujuan}', '{nomor_rekening}', '{atas_nama}', '{jumlah_pembayaran}', '{batas_pembayaran}'
].filter(shortcode =>
(template.email_subject && template.email_subject.includes(shortcode)) ||
(template.email_body_html && template.email_body_html.includes(shortcode))
).map(shortcode => ( ).map(shortcode => (
<code key={shortcode} className="bg-blue-100 px-2 py-1 rounded text-xs"> <code key={shortcode} className="bg-blue-100 px-2 py-1 rounded text-xs">
{shortcode} {shortcode}
</code> </code>
))} ))}
</div> </div>
{((template.email_subject && template.email_subject.includes('{')) ||
(template.email_body_html && template.email_body_html.includes('{'))) && (
<div className="mt-3 pt-3 border-t border-blue-200">
<p className="text-xs text-blue-700">
<strong>All Available Shortcodes:</strong> User ({nama}, {email}), Orders ({order_id}, {tanggal_pesanan}, {total}, {metode_pembayaran}, {status_pesanan}, {invoice_url}),
Products ({produk}, {kategori_produk}, {harga_produk}),
Access ({link_akses}, {username_akses}, {password_akses}),
Consulting ({tanggal_konsultasi}, {jam_konsultasi}, {link_meet}),
Events ({judul_event}, {tanggal_event}, {link_event}),
Bootcamp ({judul_bootcamp}, {progres_bootcamp}, {modul_selesai}),
Payment ({bank_tujuan}, {nomor_rekening}, {jumlah_pembayaran}),
Company ({nama_perusahaan}, {email_support})
</p>
</div>
)}
</div> </div>
{/* Template Actions */} {/* Template Actions */}

View File

@@ -461,6 +461,18 @@ export function NotifikasiTab() {
throw new Error('Konfigurasi email provider belum lengkap'); throw new Error('Konfigurasi email provider belum lengkap');
} }
// Import EmailTemplateRenderer and ShortcodeProcessor
const { EmailTemplateRenderer, ShortcodeProcessor } = await import('@/lib/email-templates/master-template');
// Process shortcodes and render with master template
const processedSubject = ShortcodeProcessor.process(template.email_subject || '');
const processedContent = ShortcodeProcessor.process(template.email_body_html || '');
const fullHtml = EmailTemplateRenderer.render({
subject: processedSubject,
content: processedContent,
brandName: 'ACCESS HUB'
});
// Send test email using send-email-v2 // Send test email using send-email-v2
const { data, error } = await supabase.functions.invoke('send-email-v2', { const { data, error } = await supabase.functions.invoke('send-email-v2', {
body: { body: {
@@ -468,8 +480,8 @@ export function NotifikasiTab() {
api_token: emailData.api_token, api_token: emailData.api_token,
from_name: emailData.from_name, from_name: emailData.from_name,
from_email: emailData.from_email, from_email: emailData.from_email,
subject: template.email_subject, subject: processedSubject,
html_body: template.email_body_html, html_body: fullHtml,
}, },
}); });

View File

@@ -347,21 +347,65 @@ export const EmailComponents = {
// Shortcode processor // Shortcode processor
export class ShortcodeProcessor { export class ShortcodeProcessor {
private static readonly DEFAULT_DATA = { private static readonly DEFAULT_DATA = {
// User information
nama: 'John Doe', nama: 'John Doe',
email: 'john@example.com', email: 'john@example.com',
// Order information
order_id: 'ORD-123456', order_id: 'ORD-123456',
tanggal_pesanan: '22 Desember 2025', tanggal_pesanan: '22 Desember 2025',
total: 'Rp 1.500.000', total: 'Rp 1.500.000',
metode_pembayaran: 'Transfer Bank', metode_pembayaran: 'Transfer Bank',
produk: 'Product Name', status_pesanan: 'Diproses',
invoice_url: 'https://example.com/invoice/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://example.com/access', link_akses: 'https://example.com/access',
username_akses: 'john.doe',
password_akses: 'Temp123!',
kadaluarsa_akses: '22 Desember 2026',
// Consulting information
tanggal_konsultasi: '22 Desember 2025', tanggal_konsultasi: '22 Desember 2025',
jam_konsultasi: '14:00', jam_konsultasi: '14:00',
durasi_konsultasi: '60 menit',
link_meet: 'https://meet.google.com/example', 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', judul_event: 'Workshop Digital Marketing',
tanggal_event: '25 Desember 2025', tanggal_event: '25 Desember 2025',
jam_event: '19:00', jam_event: '19:00',
link_event: 'https://event.example.com' link_event: 'https://event.example.com',
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://example.com/progress',
// Company information
nama_perusahaan: 'ACCESS HUB',
website_perusahaan: 'https://accesshub.example.com',
email_support: 'support@accesshub.example.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'
}; };
static process(content: string, customData: Record<string, string> = {}): string { static process(content: string, customData: Record<string, string> = {}): string {