8.1 KiB
Email Templates
The Email Templates feature provides a complete email template management system with variable substitution, versioning, and rich text editing. Create reusable email templates for campaigns, notifications, and communications.
Overview
The Email Templates system consists of four integrated components:
- Template System - Template CRUD and management
- Editor - Rich text editor with variable insertion
- Variables - Dynamic content placeholders
- Versioning - Template version history
Features
Template Management
- Create/edit/delete templates
- Category organization
- Template types (campaign, notification, system)
- Published/draft status
- Search and filtering
- Clone templates
Rich Text Editor
- WYSIWYG HTML editor
- Variable insertion menu
- Preview mode
- HTML source view
- Image upload (future)
- Link management
Variable System
Dynamic placeholders:
- User variables -
{{user.name}},{{user.email}} - Campaign variables -
{{campaign.name}},{{campaign.description}} - Representative variables -
{{rep.name}},{{rep.title}},{{rep.email}} - Custom variables - Template-specific placeholders
- System variables -
{{site.name}},{{current.date}}
Version History
- Auto-save on changes
- Version diff viewer
- Restore previous versions
- Change log
User Flow
Admin Experience
-
Create Template (
/app/email-templates)- Click "New Template"
- Enter name and category
- Set template type
- Save draft
-
Edit Template (
/app/email-templates/:id/edit)- Full-screen rich text editor
- Insert variables from dropdown
- Preview with sample data
- Save changes
-
Use Template
- Select template in campaign form
- Variables auto-populated from context
- Send email with processed template
-
Manage Versions (
/app/email-templates/:id/versions)- View version history
- Compare versions
- Restore previous version
Architecture
Backend Components
Module:
api/src/modules/email-templates/email-templates.routes.ts- Template CRUDapi/src/modules/email-templates/email-templates.service.ts- Business logicapi/src/modules/email-templates/email-templates.schemas.ts- Zod validation
Database Models:
EmailTemplate- Template definitions (name, content, variables)EmailTemplateVersion- Version history (future)
Email Processing:
- Variable substitution in
email.service.ts - Mustache-style templating:
{{variable}} - HTML escaping for security
Frontend Components
Admin Pages:
admin/src/pages/EmailTemplatesPage.tsx- Template management tableadmin/src/pages/EmailTemplateEditorPage.tsx- Full-screen editor
Editor Components:
admin/src/components/email-templates/TemplateEditor.tsx- Rich text editoradmin/src/components/email-templates/VariableInserter.tsx- Variable dropdown
Configuration
Template Types
- campaign - Campaign email templates
- notification - User notifications
- system - System emails (verification, password reset)
- custom - Custom templates
Template Categories
- Influence - Campaign-related templates
- Map - Shift/canvass notifications
- User - User account emails
- System - Automated system emails
Variable System
Available Variables
User Context:
{{user.id}} # User ID
{{user.email}} # Email address
{{user.name}} # Full name
{{user.role}} # User role
Campaign Context:
{{campaign.id}} # Campaign ID
{{campaign.name}} # Campaign name
{{campaign.description}} # Description
{{campaign.emailTemplate}} # Email body
Representative Context:
{{rep.name}} # Representative name
{{rep.title}} # Title (MP, MLA, etc.)
{{rep.email}} # Email address
{{rep.phone}} # Phone number
{{rep.district}} # District name
System Context:
{{site.name}} # Site name
{{site.url}} # Site URL
{{current.date}} # Current date
{{current.year}} # Current year
Variable Insertion
// Insert variable at cursor position
editor.insertContent('{{user.name}}');
// Variable dropdown menu
<Select>
<Option value="{{user.name}}">User Name</Option>
<Option value="{{user.email}}">User Email</Option>
<Option value="{{rep.name}}">Representative Name</Option>
</Select>
Variable Processing
Server-side processing in email.service.ts:
function processTemplate(
template: string,
variables: Record<string, any>
): string {
let processed = template;
for (const [key, value] of Object.entries(variables)) {
const placeholder = `{{${key}}}`;
processed = processed.replace(
new RegExp(placeholder, 'g'),
escapeHtml(String(value))
);
}
return processed;
}
Database Schema
EmailTemplate Model
model EmailTemplate {
id Int @id @default(autoincrement())
name String
subject String
body String @db.Text
category String?
type String @default("custom")
variables Json? # Available variables
published Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
API Endpoints
Admin Endpoints
GET /api/email-templates # List templates
POST /api/email-templates # Create template
GET /api/email-templates/:id # Get template
PATCH /api/email-templates/:id # Update template
DELETE /api/email-templates/:id # Delete template
POST /api/email-templates/:id/clone # Clone template
GET /api/email-templates/:id/preview # Preview with sample data
Security
HTML Escaping
All variable values are HTML-escaped to prevent XSS:
import { escapeHtml } from '../utils/sanitize';
const safe = escapeHtml(userInput);
// Converts: < > & " ' to HTML entities
Template Validation
- Subject line: 1-200 characters
- Body: Required, max 50,000 characters
- Variables: Valid JSON object
- Category: Predefined list
Best Practices
Template Design
- Clear subject lines (50-60 chars)
- Personalize with variables
- Mobile-responsive HTML
- Plain text alternative
- Unsubscribe link
- Branding consistency
Variable Usage
- Document available variables
- Provide defaults for missing values
- Test with sample data
- Validate variable names
Version Management
- Meaningful version names
- Document changes
- Test before publishing
- Keep version history
Desktop-Only Editor
Email template editor requires desktop browser:
const screens = Grid.useBreakpoint();
const isMobile = !screens.md;
if (isMobile) {
return (
<Alert
message="Desktop Required"
description="Email editor requires desktop browser"
type="warning"
/>
);
}
Integration Points
Campaign Emails
Campaign emails use templates:
// Select template in campaign form
<Select>
{templates.map(t => (
<Option value={t.id}>{t.name}</Option>
))}
</Select>
// Process template with campaign data
const emailBody = processTemplate(template.body, {
'user.name': user.name,
'campaign.name': campaign.name,
'rep.name': representative.name,
});
System Emails
System emails (verification, password reset):
// Load system template
const template = await getTemplateByType('email-verification');
// Process with user data
const emailBody = processTemplate(template.body, {
'user.name': user.name,
'verify.link': verificationUrl,
});
// Send email
await emailService.sendEmail({
to: user.email,
subject: template.subject,
html: emailBody,
});