diff --git a/EMAIL-TEMPLATE-SYSTEM.md b/EMAIL-TEMPLATE-SYSTEM.md new file mode 100644 index 0000000..0bc0629 --- /dev/null +++ b/EMAIL-TEMPLATE-SYSTEM.md @@ -0,0 +1,358 @@ +# Unified Email Template System + +## Overview + +All emails now use a **single master template** for consistent branding and design. The master template wraps content-only HTML from database templates. + +## Architecture + +``` +Database Template (content only) + ↓ +Process Shortcodes ({nama}, {platform_name}, etc.) + ↓ +EmailTemplateRenderer.render() - wraps with master template + ↓ +Complete HTML Email sent via provider +``` + +## Master Template + +**Location:** `supabase/shared/email-template-renderer.ts` + +**Features:** +- Brutalist design (black borders, hard shadows) +- Responsive layout (600px max width) +- `.tiptap-content` wrapper for auto-styling +- Header with brand name + notification ID +- Footer with unsubscribe links +- All CSS included (no external dependencies) + +**CSS Classes for Content:** +- `.tiptap-content h1, h2, h3` - Headings +- `.tiptap-content p` - Paragraphs +- `.tiptap-content a` - Links (underlined, bold) +- `.tiptap-content ul, ol` - Lists +- `.tiptap-content table` - Tables with brutalist borders +- `.btn` - Buttons with hard shadow +- `.otp-box` - OTP codes with dashed border +- `.alert-success, .alert-danger, .alert-info` - Colored alert boxes + +## Database Templates + +### Format + +**CORRECT** (content-only): +```html +

Payment Successful!

+

Hello {nama}, your payment has been confirmed.

+ + + + + + + + + + + + + + +
ParameterValue
Order ID{order_id}
+``` + +**WRONG** (full HTML): +```html + + +... + +

Payment Successful!

+ ... + + +``` + +### Why Content-Only? + +The master template provides: +- Email client compatibility (resets, Outlook fixes) +- Consistent header/footer +- Responsive wrapper +- Brutalist styling + +Your content just needs the **body HTML** - no ``, ``, or `` tags. + +## Usage in Edge Functions + +### Auth OTP (`send-auth-otp`) + +```typescript +import { EmailTemplateRenderer } from "../shared/email-template-renderer.ts"; + +// Fetch template from database +const template = await supabase + .from("notification_templates") + .select("*") + .eq("key", "auth_email_verification") + .single(); + +// Process shortcodes +let htmlContent = template.email_body_html; +Object.entries(templateVars).forEach(([key, value]) => { + htmlContent = htmlContent.replace(new RegExp(`{${key}}`, 'g'), value); +}); + +// Wrap with master template +const htmlBody = EmailTemplateRenderer.render({ + subject: template.email_subject, + content: htmlContent, + brandName: settings.platform_name || 'ACCESS HUB', +}); + +// Send via send-email-v2 +await fetch(`${supabaseUrl}/functions/v1/send-email-v2`, { + method: 'POST', + body: JSON.stringify({ + to: email, + html_body: htmlBody, + // ... other fields + }), +}); +``` + +### Other Notifications (`send-notification`) + +```typescript +import { EmailTemplateRenderer } from "../shared/email-template-renderer.ts"; + +// Fetch template and process shortcodes +const htmlContent = replaceVariables(template.body_html || template.body_text, allVariables); + +// Wrap with master template +const htmlBody = EmailTemplateRenderer.render({ + subject: subject, + content: htmlContent, + brandName: settings.brand_name || "ACCESS HUB", +}); + +// Send via provider (SMTP, Resend, etc.) +await sendViaSMTP({ html: htmlBody, ... }); +``` + +## Available Shortcodes + +See `ShortcodeProcessor.DEFAULT_DATA` in `supabase/shared/email-template-renderer.ts`: + +**User:** +- `{nama}` - User name +- `{email}` - User email + +**Order:** +- `{order_id}` - Order ID +- `{tanggal_pesanan}` - Order date +- `{total}` - Total amount +- `{metode_pembayaran}` - Payment method + +**Product:** +- `{produk}` - Product name +- `{kategori_produk}` - Product category + +**Access:** +- `{link_akses}` - Access link +- `{username_akses}` - Access username +- `{password_akses}` - Access password + +**Consulting:** +- `{tanggal_konsultasi}` - Consultation date +- `{jam_konsultasi}` - Consultation time +- `{link_meet}` - Meeting link + +**And many more...** + +## Creating New Templates + +### 1. Design Content-Only HTML + +Use brutalist components: + +```html +

Welcome!

+

Hello {nama}, welcome to {platform_name}!

+ +

Your Details

+ + + + + + + + + + + + + + + + + +
FieldValue
Email{email}
Plan{plan_name}
+ +

+ + Go to Dashboard + +

+ +
+ Success! Your account is ready to use. +
+``` + +### 2. Add to Database + +```sql +INSERT INTO notification_templates ( + key, + name, + is_active, + email_subject, + email_body_html +) VALUES ( + 'welcome_email', + 'Welcome Email', + true, + 'Welcome to {platform_name}!', + '---

Welcome!

...---' +); +``` + +### 3. Use in Edge Function + +```typescript +const template = await getTemplate('welcome_email'); +const htmlContent = processShortcodes(template.body_html, { + nama: user.name, + platform_name: settings.brand_name, + email: user.email, + plan_name: user.plan, + dashboard_link: 'https://...', +}); + +const htmlBody = EmailTemplateRenderer.render({ + subject: template.subject, + content: htmlContent, + brandName: settings.brand_name, +}); +``` + +## Migration Notes + +### Old Templates (Self-Contained HTML) + +If you have old templates with full HTML: + +**Before:** +```html + + + + + + +
+

Welcome!

+

Hello...

+
+ + +``` + +**After (Content-Only):** +```html +

Welcome!

+

Hello...

+``` + +Remove: +- `` +- ``, ``, `` tags +- `