359 lines
7.8 KiB
Markdown
359 lines
7.8 KiB
Markdown
# 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
|
|
<h1>Payment Successful!</h1>
|
|
<p>Hello <strong>{nama}</strong>, your payment has been confirmed.</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Parameter</th>
|
|
<th>Value</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Order ID</td>
|
|
<td>{order_id}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
```
|
|
|
|
**WRONG** (full HTML):
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>...</head>
|
|
<body>
|
|
<h1>Payment Successful!</h1>
|
|
...
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
### 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 `<html>`, `<head>`, or `<body>` 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
|
|
<h1>Welcome!</h1>
|
|
<p>Hello <strong>{nama}</strong>, welcome to <strong>{platform_name}</strong>!</p>
|
|
|
|
<h2>Your Details</h2>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Field</th>
|
|
<th>Value</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Email</td>
|
|
<td>{email}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Plan</td>
|
|
<td>{plan_name}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<p style="margin-top: 30px;">
|
|
<a href="{dashboard_link}" class="btn btn-full">
|
|
Go to Dashboard
|
|
</a>
|
|
</p>
|
|
|
|
<blockquote class="alert-success">
|
|
<strong>Success!</strong> Your account is ready to use.
|
|
</blockquote>
|
|
```
|
|
|
|
### 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}!',
|
|
'---<h1>Welcome!</h1>...---'
|
|
);
|
|
```
|
|
|
|
### 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
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<style>
|
|
body { font-family: Arial; }
|
|
.container { max-width: 600px; }
|
|
h1 { color: #0066cc; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>Welcome!</h1>
|
|
<p>Hello...</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
**After (Content-Only):**
|
|
```html
|
|
<h1>Welcome!</h1>
|
|
<p>Hello...</p>
|
|
```
|
|
|
|
Remove:
|
|
- `<!DOCTYPE html>`
|
|
- `<html>`, `<head>`, `<body>` tags
|
|
- `<style>` blocks
|
|
- Container `<div>` wrappers
|
|
- Header/footer HTML
|
|
|
|
Keep:
|
|
- Content HTML only
|
|
- Shortcode placeholders `{variable}`
|
|
- Inline styles for special cases (rare)
|
|
|
|
## Benefits
|
|
|
|
✅ **Consistent Branding** - All emails have same header/footer
|
|
✅ **Single Source of Truth** - One master template controls design
|
|
✅ **Easy Updates** - Change design in one place
|
|
✅ **Email Client Compatible** - Master template has all the fixes
|
|
✅ **Less Duplication** - No more reinventing styles per template
|
|
✅ **Auto-Styling** - `.tiptap-content` CSS makes content look good
|
|
|
|
## Files
|
|
|
|
- `supabase/shared/email-template-renderer.ts` - Master template + renderer
|
|
- `supabase/functions/send-auth-otp/index.ts` - Uses master template
|
|
- `supabase/functions/send-notification/index.ts` - Uses master template
|
|
- `supabase/migrations/20250102000005_fix_auth_email_template_content_only.sql` - Auth template update
|
|
- `email-master-template.html` - Visual reference of master template
|
|
|
|
## Testing
|
|
|
|
### Test Master Template
|
|
|
|
```bash
|
|
# Test with curl
|
|
curl -X POST https://lovable.backoffice.biz.id/functions/v1/send-auth-otp \
|
|
-H "Authorization: Bearer YOUR_SERVICE_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"user_id":"TEST_ID","email":"test@example.com"}'
|
|
```
|
|
|
|
### Expected Result
|
|
|
|
Email should have:
|
|
- Black header with "ACCESS HUB" logo
|
|
- Notification ID (NOTIF #XXXXXX)
|
|
- Content styled with brutalist design
|
|
- Gray footer with unsubscribe links
|
|
- Responsive on mobile
|
|
|
|
## Troubleshooting
|
|
|
|
### Email Has No Styling
|
|
|
|
**Cause:** Template has full HTML, getting double-wrapped
|
|
|
|
**Fix:** Remove `<html>`, `<head>`, `<body>` from database template
|
|
|
|
### Styling Not Applied
|
|
|
|
**Cause:** Content not in `.tiptap-content` wrapper
|
|
|
|
**Fix:** Master template automatically wraps `{{content}}` in `.tiptap-content` div
|
|
|
|
### Broken Layout
|
|
|
|
**Cause:** Old template has container divs/wrappers
|
|
|
|
**Fix:** Remove container divs, keep only content HTML
|
|
|
|
---
|
|
|
|
**Status:** ✅ Unified system active
|
|
**Last Updated:** 2025-01-02
|