Files
meet-hub/EMAIL-TEMPLATE-SYSTEM.md

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