Alert Templates
The Alert Templates UI is a frontend-only template management system accessible from the Admin Dashboard's Templates tab. It allows Malet Owners and platform administrators to design, preview, and export email and SMS alert templates using Handlebars syntax โ without requiring backend CRUD infrastructure.
NOTE
The backend alerts subgraph is a TCP microservice that reads .hbs templates from disk at startup. It does not yet expose a GraphQL CRUD API for template management. This frontend tool provides a design workspace for authoring templates, with JSON export for manual backend deployment. See Alerts Resilience for the backend architecture.
Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Admin Dashboard (+page.svelte) โ
โ Tab: "Templates" (lazy-loaded) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ AlertTemplateEditor.svelte โ โ
โ โ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โ โ
โ โ โ Sidebar โ โ Editor Pane โ โ Preview Panel โ โ โ
โ โ โ (List + โ โ (Body/Subj/ โ โ (Email iframe โ โ โ
โ โ โ Filters) โ โ Variants) โ โ or SMS phone โ โ โ
โ โ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ mockup) โ โ โ
โ โ โโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ alertTemplateStore.svelte.ts โ
โ (localStorage persistence) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Store: `alertTemplateStore.svelte.ts`
A Svelte 5 runes class (AlertTemplateStore) managing:
- Templates array โ persisted to
localStorageundermall_alert_templates - CRUD operations โ create, update, delete, duplicate
- A/B variants โ up to 3 variants per template (A, B, C)
- Variable detection โ auto-extracts
{{variable}}patterns from body and subject - Template compilation โ client-side Handlebars-like substitution with sample data
- Zod validation โ runtime schema validation via
AlertTemplateSchema
Component: `AlertTemplateEditor.svelte`
Three-panel layout:
- Sidebar โ Template list with search, channel filter (All/Email/SMS), create form, export all button
- Editor Pane โ Name input, channel selector, subject line (Email only), A/B variant tabs, Handlebars body textarea, variable inspector
- Preview Panel โ Live-rendered preview with device toggle
Component: `AlertTemplatePreview.svelte`
Renders two preview modes:
- Email โ Sandboxed
<iframe>with browser-chrome mockup (traffic light dots, From address, subject line), Desktop/Mobile width toggle - SMS โ Phone mockup with notch, carrier bar, iMessage-style bubble, character count with multi-segment warning (>160 chars)
Data Model
AlertTemplate
interface AlertTemplate {
id: string; // Auto-generated: tpl_{timestamp}_{random}
name: string; // Required, min 1 char
channel: 'EMAIL' | 'SMS'; // Delivery channel
subject: string; // Email subject line (empty for SMS)
body: string; // Handlebars template body
variables: string[]; // Auto-detected {{variable}} names
variants: TemplateVariant[]; // A/B overrides (max 3)
sampleData: Record<string, string>; // Variable โ sample value mapping
createdAt: string; // ISO timestamp
updatedAt: string; // ISO timestamp
}
TemplateVariant
interface TemplateVariant {
key: 'A' | 'B' | 'C'; // Variant identifier
subject: string; // Override subject line
body: string; // Override body template
isControl: boolean; // Whether this is the control variant
}
CRUD Operations
| Operation | Method | Behavior |
|---|---|---|
| Create | store.create(name, channel) |
Scaffolds a template with default Handlebars body |
| Update | store.update(id, updates) |
Auto-re-extracts variables on body change |
| Delete | store.delete(id) |
Removes from store + localStorage |
| Duplicate | store.duplicate(id) |
Deep clone with " (Copy)" name suffix |
| Export | store.exportTemplate(id) |
JSON download of a single template |
| Export All | store.exportAll() |
JSON download of the full template array |
| Import | store.importTemplates(json) |
Zod-validated bulk import with regenerated IDs |
Variable Detection
Templates use {{variable}} Handlebars syntax for dynamic content. The variable extractor scans the body and subject for {{ ... }} patterns and automatically populates the Variable Inspector panel.
The extractor excludes Handlebars control constructs:
- Block helpers:
{{#if ready}},{{/if}},{{#each items}} - Comments:
{{! this is a comment }} - Partials:
{{> header_partial}}
Detected variables appear with editable sample value inputs. Changes to sample values immediately update the live preview.
A/B Variant Comparison
Each template supports up to 3 variants (A, B, C) accessed via pill tabs above the body editor. Each variant independently overrides the subject line and body content.
Compare Mode
Toggle the โ Compare button to view all variants side-by-side in the preview panel. This enables visual comparison of variant wording, layout, and length without switching tabs.
TIP
Variants are a design-time tool โ they don't distribute traffic. When the backend adds A/B testing support, the variant data structure (TemplateVariant) is ready for GraphQL transmission.
Preview Rendering
Email Preview
| Feature | Implementation |
|---|---|
| Rendering | Sandboxed <iframe> with sandbox="" (no scripts) |
| Chrome | Traffic light dots, "From: noreply@ngwenya.app", subject line |
| Desktop | Full iframe width |
| Mobile | Constrained to 375px max-width |
SMS Preview
| Feature | Implementation |
|---|---|
| Rendering | Stripped HTML โ plain text in bubble UI |
| Chrome | Phone frame with notch, "Mallnline" carrier, live clock |
| Bubble | Blue iMessage-style right-aligned bubble |
| Length | Character count with "multi-segment" warning at >160 chars |
Admin Dashboard Integration
The Templates tab is lazy-loaded in src/routes/admin/+page.svelte:
{#if activeTab === 'templates'}
{#await import('$lib/components/admin/AlertTemplateEditor.svelte') then module}
<module.default />
{/await}
{/if}
This ensures the template editor and its dependencies (Handlebars compilation, Zod schemas) are only loaded when the user navigates to the Templates tab.
Persistence
Templates are stored in localStorage under the key mall_alert_templates as a JSON array. The export format matches the AlertTemplate interface exactly, enabling:
- Manual backup via single or bulk JSON export
- Cross-environment migration via import
- Backend handoff when the alerts subgraph adds GraphQL CRUD
Future Backend Wiring
When the alerts subgraph exposes a GraphQL TemplateService:
- Replace
localStoragepersistence with GraphQL mutations - Wire CRUD operations to
CREATE_ALERT_TEMPLATE/UPDATE_ALERT_TEMPLATEmutations - Add server-side Handlebars compilation for production-accurate preview
- Wire A/B variants to the backend distribution engine
File Map
| File | Purpose |
|---|---|
src/stores/alertTemplateStore.svelte.ts |
Store: CRUD, variants, compilation, validation |
src/lib/components/admin/AlertTemplateEditor.svelte |
Three-panel editor UI |
src/lib/components/admin/AlertTemplatePreview.svelte |
Email iframe + SMS phone preview |
src/routes/admin/+page.svelte |
Templates tab integration |
tests/alertTemplateStore.test.ts |
29 unit tests |
Related
- Alerts Resilience โ Backend alerts architecture, TCP transport, Handlebars templates, delivery pipeline
- Alerts Subgraph โ Subgraph internals: preference storage, email/SMS delivery, WebSocket subscriptions
- Blog Editor UX โ Companion editor in the Admin Dashboard with its own pipeline workflow
- Admin Analytics Dashboards โ Analytics tabs alongside the Templates tab in the Admin Dashboard
- Invite & Notification Pipeline โ Cross-subgraph notification flow using the alerts TCP service
- uMail Domain Verification โ DNS setup for
mallnline.comensuring email template delivery via verified Resend sender