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:

  1. Template System - Template CRUD and management
  2. Editor - Rich text editor with variable insertion
  3. Variables - Dynamic content placeholders
  4. 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

  1. Create Template (/app/email-templates)

    • Click "New Template"
    • Enter name and category
    • Set template type
    • Save draft
  2. Edit Template (/app/email-templates/:id/edit)

    • Full-screen rich text editor
    • Insert variables from dropdown
    • Preview with sample data
    • Save changes
  3. Use Template

    • Select template in campaign form
    • Variables auto-populated from context
    • Send email with processed template
  4. 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 CRUD
  • api/src/modules/email-templates/email-templates.service.ts - Business logic
  • api/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 table
  • admin/src/pages/EmailTemplateEditorPage.tsx - Full-screen editor

Editor Components:

  • admin/src/components/email-templates/TemplateEditor.tsx - Rich text editor
  • admin/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,
});