-
-
-
{
- setTemplates(templates.map(t =>
- t.id === template.id ? { ...t, email_subject: e.target.value } : t
- ));
- }}
- placeholder="Subjek email..."
- className="border-2"
- />
+
+
+
+
+ {
+ setTemplates(templates.map(t =>
+ t.id === template.id ? { ...t, email_subject: e.target.value } : t
+ ));
+ }}
+ placeholder="Subjek email..."
+ className="border-2"
+ />
+
+
+
+
+ {
+ setTemplates(templates.map(t =>
+ t.id === template.id ? { ...t, email_body_html: html } : t
+ ));
+ }}
+ placeholder="Tulis isi email..."
+ />
+
+
+
+
+ {
+ setTemplates(templates.map(t =>
+ t.id === template.id ? { ...t, webhook_url: e.target.value } : t
+ ));
+ }}
+ placeholder="https://n8n.example.com/webhook/..."
+ className="border-2"
+ />
+
-
-
- {
- setTemplates(templates.map(t =>
- t.id === template.id ? { ...t, email_body_html: html } : t
- ));
- }}
- placeholder="Tulis isi email..."
- />
-
-
-
-
-
{
- setTemplates(templates.map(t =>
- t.id === template.id ? { ...t, webhook_url: e.target.value } : t
- ));
- }}
- placeholder="https://n8n.example.com/webhook/..."
- className="border-2"
+ {/* Email Preview */}
+
+
Email Preview
+
@@ -272,12 +335,14 @@ export function NotifikasiTab() {
)}
-
+
+
+
diff --git a/src/lib/email-templates/master-template.ts b/src/lib/email-templates/master-template.ts
new file mode 100644
index 0000000..28c139d
--- /dev/null
+++ b/src/lib/email-templates/master-template.ts
@@ -0,0 +1,382 @@
+interface EmailTemplateData {
+ subject: string;
+ content: string;
+ brandName?: string;
+ brandLogo?: string;
+}
+
+export class EmailTemplateRenderer {
+ private static readonly MASTER_TEMPLATE = `
+
+
+
+
+
+
{{subject}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+ {{brandName}}
+
+ |
+
+
+
+ NOTIF #{{timestamp}}
+
+ |
+
+
+ |
+
+
+
+
+ |
+
+
+
+ {{content}}
+
+
+ |
+
+
+
+
+ |
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ `;
+
+ static render(data: EmailTemplateData): string {
+ let html = this.MASTER_TEMPLATE;
+
+ // Replace placeholders
+ html = html.replace(/{{subject}}/g, data.subject || 'Notification');
+ html = html.replace(/{{brandName}}/g, data.brandName || 'ACCESS HUB');
+ html = html.replace(/{{brandLogo}}/g, data.brandLogo || '');
+ html = html.replace(/{{timestamp}}/g, Date.now().toString().slice(-6));
+ html = html.replace(/{{content}}/g, data.content);
+
+ return html;
+ }
+}
+
+// Reusable Email Components
+export const EmailComponents = {
+ // Buttons
+ button: (text: string, url: string, fullwidth = false) =>
+ `
+ ${text}
+
`,
+
+ // Alert boxes
+ alert: (type: 'success' | 'danger' | 'info', content: string) =>
+ `
+ ${content}
+
`,
+
+ // Code blocks
+ codeBlock: (code: string, language = '') =>
+ `
${code}
`,
+
+ // OTP boxes
+ otpBox: (code: string) =>
+ `
${code}
`,
+
+ // Info card
+ infoCard: (title: string, items: Array<{label: string; value: string}>) => {
+ const rows = items.map(item =>
+ `
+ | ${item.label} |
+ ${item.value} |
+
`
+ ).join('');
+
+ return `
+
${title}
+
+
+
+ | Parameter |
+ Value |
+
+
+
+ ${rows}
+
+
+ `;
+ },
+
+ // Divider
+ divider: () => '
',
+
+ // Spacing
+ spacing: (size: 'small' | 'medium' | 'large' = 'medium') => {
+ const sizes = { small: '15px', medium: '25px', large: '40px' };
+ return `
`;
+ }
+};
+
+// Shortcode processor
+export class ShortcodeProcessor {
+ private static readonly DEFAULT_DATA = {
+ nama: 'John Doe',
+ email: 'john@example.com',
+ order_id: 'ORD-123456',
+ tanggal_pesanan: '22 Desember 2025',
+ total: 'Rp 1.500.000',
+ metode_pembayaran: 'Transfer Bank',
+ produk: 'Product Name',
+ link_akses: 'https://example.com/access',
+ tanggal_konsultasi: '22 Desember 2025',
+ jam_konsultasi: '14:00',
+ link_meet: 'https://meet.google.com/example',
+ judul_event: 'Workshop Digital Marketing',
+ tanggal_event: '25 Desember 2025',
+ jam_event: '19:00',
+ link_event: 'https://event.example.com'
+ };
+
+ static process(content: string, customData: Record
= {}): 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 {
+ return this.DEFAULT_DATA;
+ }
+}
\ No newline at end of file