7264 lines
273 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Build Power. Not Rent It. Own your digital infrastructure.">
<meta name="author" content="Bunker Operations">
<link rel="canonical" href="https://bnkserve.org/v2/features/email-templates/variables/">
<link rel="prev" href="../editor/">
<link rel="next" href="../versioning/">
<link rel="icon" href="../../../../assets/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
<title>Variables - Changemaker Lite</title>
<link rel="stylesheet" href="../../../../assets/stylesheets/main.484c7ddc.min.css">
<link rel="stylesheet" href="../../../../assets/stylesheets/palette.ab4e12ef.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Inter";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="../../../../stylesheets/extra.css">
<link rel="stylesheet" href="../../../../stylesheets/home.css">
<link rel="stylesheet" href="../../../../assets/css/video-player.css">
<script>__md_scope=new URL("../../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
<meta property="og:type" content="website" />
<meta property="og:title" content="Variables - Changemaker Lite" />
<meta property="og:description" content="Build Power. Not Rent It. Own your digital infrastructure." />
<meta property="og:image" content="https://bnkserve.org/assets/images/social/v2/features/email-templates/variables.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://bnkserve.org/v2/features/email-templates/variables/" />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:title" content="Variables - Changemaker Lite" />
<meta property="twitter:description" content="Build Power. Not Rent It. Own your digital infrastructure." />
<meta property="twitter:image" content="https://bnkserve.org/assets/images/social/v2/features/email-templates/variables.png" />
</head>
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="amber">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#template-variables-system" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
<img src="../../../../assets/logo.png" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Changemaker Lite
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Variables
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="amber" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3zm3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95zm-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31"/></svg>
</label>
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="deep-purple" data-md-color-accent="amber" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5s-1.65.15-2.39.42zM3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29zm.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14zM20.65 7l-1.77 3.79a7.02 7.02 0 0 0-2.38-4.15zm-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29zM12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44z"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
changemaker.lite
</div>
</a>
</div>
</nav>
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href="../../../.." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="../../../" class="md-tabs__link">
V2 Documentation
</a>
</li>
<li class="md-tabs__item">
<a href="../../../../phil/" class="md-tabs__link">
Philosophy
</a>
</li>
<li class="md-tabs__item">
<a href="../../../../v1/" class="md-tabs__link">
V1 Documentation (Legacy)
</a>
</li>
<li class="md-tabs__item">
<a href="../../../../blog/" class="md-tabs__link">
Blog
</a>
</li>
</ul>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../../../.." title="Changemaker Lite" class="md-nav__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
<img src="../../../../assets/logo.png" alt="logo">
</a>
Changemaker Lite
</label>
<div class="md-nav__source">
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
changemaker.lite
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../.." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" checked>
<div class="md-nav__link md-nav__container">
<a href="../../../" class="md-nav__link ">
<span class="md-ellipsis">
V2 Documentation
</span>
</a>
<label class="md-nav__link " for="__nav_2" id="__nav_2_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
V2 Documentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_2" >
<div class="md-nav__link md-nav__container">
<a href="../../../getting-started/" class="md-nav__link ">
<span class="md-ellipsis">
Getting Started
</span>
</a>
<label class="md-nav__link " for="__nav_2_2" id="__nav_2_2_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_2">
<span class="md-nav__icon md-icon"></span>
Getting Started
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../getting-started/quick-start/" class="md-nav__link">
<span class="md-ellipsis">
Quick Start
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_3" >
<div class="md-nav__link md-nav__container">
<a href="../../../architecture/" class="md-nav__link ">
<span class="md-ellipsis">
Architecture
</span>
</a>
<label class="md-nav__link " for="__nav_2_3" id="__nav_2_3_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_3">
<span class="md-nav__icon md-icon"></span>
Architecture
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../architecture/dual-api/" class="md-nav__link">
<span class="md-ellipsis">
Dual API System
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../architecture/authentication/" class="md-nav__link">
<span class="md-ellipsis">
Authentication & Security
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_4" >
<div class="md-nav__link md-nav__container">
<a href="../../../backend/" class="md-nav__link ">
<span class="md-ellipsis">
Backend
</span>
</a>
<label class="md-nav__link " for="__nav_2_4" id="__nav_2_4_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_4">
<span class="md-nav__icon md-icon"></span>
Backend
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../backend/modules/" class="md-nav__link">
<span class="md-ellipsis">
Modules
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../backend/services/" class="md-nav__link">
<span class="md-ellipsis">
Services
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../backend/middleware/" class="md-nav__link">
<span class="md-ellipsis">
Middleware
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../backend/utilities/" class="md-nav__link">
<span class="md-ellipsis">
Utilities
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_5" >
<div class="md-nav__link md-nav__container">
<a href="../../../frontend/" class="md-nav__link ">
<span class="md-ellipsis">
Frontend
</span>
</a>
<label class="md-nav__link " for="__nav_2_5" id="__nav_2_5_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_5">
<span class="md-nav__icon md-icon"></span>
Frontend
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../frontend/components/" class="md-nav__link">
<span class="md-ellipsis">
Components
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../frontend/layouts/" class="md-nav__link">
<span class="md-ellipsis">
Layouts
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../frontend/pages/" class="md-nav__link">
<span class="md-ellipsis">
Pages
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_6" >
<div class="md-nav__link md-nav__container">
<a href="../../../database/" class="md-nav__link ">
<span class="md-ellipsis">
Database
</span>
</a>
<label class="md-nav__link " for="__nav_2_6" id="__nav_2_6_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_6">
<span class="md-nav__icon md-icon"></span>
Database
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../database/schema/" class="md-nav__link">
<span class="md-ellipsis">
Schema Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../database/migrations/" class="md-nav__link">
<span class="md-ellipsis">
Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../database/seeding/" class="md-nav__link">
<span class="md-ellipsis">
Seeding
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../database/indexes/" class="md-nav__link">
<span class="md-ellipsis">
Indexes
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../database/models/" class="md-nav__link">
<span class="md-ellipsis">
Models
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_7" checked>
<div class="md-nav__link md-nav__container">
<a href="../../" class="md-nav__link ">
<span class="md-ellipsis">
Features
</span>
</a>
<label class="md-nav__link " for="__nav_2_7" id="__nav_2_7_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_7_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2_7">
<span class="md-nav__icon md-icon"></span>
Features
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../influence/" class="md-nav__link">
<span class="md-ellipsis">
Influence
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../landing-pages/" class="md-nav__link">
<span class="md-ellipsis">
Landing Pages
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_7_5" checked>
<div class="md-nav__link md-nav__container">
<a href="../" class="md-nav__link ">
<span class="md-ellipsis">
Email Templates
</span>
</a>
<label class="md-nav__link " for="__nav_2_7_5" id="__nav_2_7_5_label" tabindex="0">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="3" aria-labelledby="__nav_2_7_5_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2_7_5">
<span class="md-nav__icon md-icon"></span>
Email Templates
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../template-system/" class="md-nav__link">
<span class="md-ellipsis">
Template System
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../editor/" class="md-nav__link">
<span class="md-ellipsis">
Editor
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Variables
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Variables
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#overview" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#architecture" class="md-nav__link">
<span class="md-ellipsis">
Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#database-model" class="md-nav__link">
<span class="md-ellipsis">
Database Model
</span>
</a>
<nav class="md-nav" aria-label="Database Model">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#emailtemplatevariable-schema" class="md-nav__link">
<span class="md-ellipsis">
EmailTemplateVariable Schema
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#variable-types" class="md-nav__link">
<span class="md-ellipsis">
Variable Types
</span>
</a>
<nav class="md-nav" aria-label="Variable Types">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#required-variables" class="md-nav__link">
<span class="md-ellipsis">
Required Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#optional-variables" class="md-nav__link">
<span class="md-ellipsis">
Optional Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#conditional-variables" class="md-nav__link">
<span class="md-ellipsis">
Conditional Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#array-variables-loops" class="md-nav__link">
<span class="md-ellipsis">
Array Variables (Loops)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#admin-workflow" class="md-nav__link">
<span class="md-ellipsis">
Admin Workflow
</span>
</a>
<nav class="md-nav" aria-label="Admin Workflow">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#viewing-variables" class="md-nav__link">
<span class="md-ellipsis">
Viewing Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#adding-variable" class="md-nav__link">
<span class="md-ellipsis">
Adding Variable
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#editing-variable" class="md-nav__link">
<span class="md-ellipsis">
Editing Variable
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#deleting-variable" class="md-nav__link">
<span class="md-ellipsis">
Deleting Variable
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#reordering-variables" class="md-nav__link">
<span class="md-ellipsis">
Reordering Variables
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#developer-workflow" class="md-nav__link">
<span class="md-ellipsis">
Developer Workflow
</span>
</a>
<nav class="md-nav" aria-label="Developer Workflow">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#creating-variables-programmatically" class="md-nav__link">
<span class="md-ellipsis">
Creating Variables Programmatically
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#loading-variables-in-code" class="md-nav__link">
<span class="md-ellipsis">
Loading Variables in Code
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#validating-variables" class="md-nav__link">
<span class="md-ellipsis">
Validating Variables
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#code-examples" class="md-nav__link">
<span class="md-ellipsis">
Code Examples
</span>
</a>
<nav class="md-nav" aria-label="Code Examples">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#creating-variable-via-api" class="md-nav__link">
<span class="md-ellipsis">
Creating Variable via API
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#auto-generating-sample-data" class="md-nav__link">
<span class="md-ellipsis">
Auto-Generating Sample Data
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#variable-usage-detection" class="md-nav__link">
<span class="md-ellipsis">
Variable Usage Detection
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#common-variables-by-category" class="md-nav__link">
<span class="md-ellipsis">
Common Variables by Category
</span>
</a>
<nav class="md-nav" aria-label="Common Variables by Category">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#influence-templates" class="md-nav__link">
<span class="md-ellipsis">
INFLUENCE Templates
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#map-templates" class="md-nav__link">
<span class="md-ellipsis">
MAP Templates
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#system-templates" class="md-nav__link">
<span class="md-ellipsis">
SYSTEM Templates
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#troubleshooting" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
<nav class="md-nav" aria-label="Troubleshooting">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#problem-variable-not-appearing-in-editor" class="md-nav__link">
<span class="md-ellipsis">
Problem: Variable not appearing in editor
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-validation-error-for-optional-variable" class="md-nav__link">
<span class="md-ellipsis">
Problem: Validation error for optional variable
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-sample-value-not-used-in-preview" class="md-nav__link">
<span class="md-ellipsis">
Problem: Sample value not used in preview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-duplicate-variable-key-error" class="md-nav__link">
<span class="md-ellipsis">
Problem: Duplicate variable key error
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-variables-not-alphabetically-sorted" class="md-nav__link">
<span class="md-ellipsis">
Problem: Variables not alphabetically sorted
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#performance-considerations" class="md-nav__link">
<span class="md-ellipsis">
Performance Considerations
</span>
</a>
<nav class="md-nav" aria-label="Performance Considerations">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#variable-loading" class="md-nav__link">
<span class="md-ellipsis">
Variable Loading
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#validation-performance" class="md-nav__link">
<span class="md-ellipsis">
Validation Performance
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#best-practices" class="md-nav__link">
<span class="md-ellipsis">
Best Practices
</span>
</a>
<nav class="md-nav" aria-label="Best Practices">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#variable-naming-conventions" class="md-nav__link">
<span class="md-ellipsis">
Variable Naming Conventions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#documentation" class="md-nav__link">
<span class="md-ellipsis">
Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#sample-values" class="md-nav__link">
<span class="md-ellipsis">
Sample Values
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#required-vs-optional" class="md-nav__link">
<span class="md-ellipsis">
Required vs Optional
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#related-documentation" class="md-nav__link">
<span class="md-ellipsis">
Related Documentation
</span>
</a>
<nav class="md-nav" aria-label="Related Documentation">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#frontend-documentation" class="md-nav__link">
<span class="md-ellipsis">
Frontend Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#backend-documentation" class="md-nav__link">
<span class="md-ellipsis">
Backend Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#database-documentation" class="md-nav__link">
<span class="md-ellipsis">
Database Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#feature-documentation" class="md-nav__link">
<span class="md-ellipsis">
Feature Documentation
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../versioning/" class="md-nav__link">
<span class="md-ellipsis">
Versioning
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../media/" class="md-nav__link">
<span class="md-ellipsis">
Media
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../newsletter/" class="md-nav__link">
<span class="md-ellipsis">
Newsletter
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../observability/" class="md-nav__link">
<span class="md-ellipsis">
Observability
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../tunnel/" class="md-nav__link">
<span class="md-ellipsis">
Tunnel
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_8" >
<div class="md-nav__link md-nav__container">
<a href="../../../deployment/" class="md-nav__link ">
<span class="md-ellipsis">
Deployment
</span>
</a>
<label class="md-nav__link " for="__nav_2_8" id="__nav_2_8_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_8_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_8">
<span class="md-nav__icon md-icon"></span>
Deployment
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../deployment/docker-compose/" class="md-nav__link">
<span class="md-ellipsis">
Docker Compose
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/environment-variables/" class="md-nav__link">
<span class="md-ellipsis">
Environment Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/nginx/" class="md-nav__link">
<span class="md-ellipsis">
Nginx Configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/ssl-tls/" class="md-nav__link">
<span class="md-ellipsis">
SSL/TLS
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/tunneling/" class="md-nav__link">
<span class="md-ellipsis">
Tunneling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/monitoring-stack/" class="md-nav__link">
<span class="md-ellipsis">
Monitoring Stack
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/healthchecks/" class="md-nav__link">
<span class="md-ellipsis">
Health Checks
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/scaling/" class="md-nav__link">
<span class="md-ellipsis">
Scaling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/backup-restore/" class="md-nav__link">
<span class="md-ellipsis">
Backup & Restore
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_9" >
<div class="md-nav__link md-nav__container">
<a href="../../../development/" class="md-nav__link ">
<span class="md-ellipsis">
Development
</span>
</a>
<label class="md-nav__link " for="__nav_2_9" id="__nav_2_9_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_9_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_9">
<span class="md-nav__icon md-icon"></span>
Development
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../development/local-setup/" class="md-nav__link">
<span class="md-ellipsis">
Local Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/docker-workflow/" class="md-nav__link">
<span class="md-ellipsis">
Docker Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/git-workflow/" class="md-nav__link">
<span class="md-ellipsis">
Git Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/npm-commands/" class="md-nav__link">
<span class="md-ellipsis">
NPM Commands
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/migrations/" class="md-nav__link">
<span class="md-ellipsis">
Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/typescript/" class="md-nav__link">
<span class="md-ellipsis">
TypeScript
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/testing/" class="md-nav__link">
<span class="md-ellipsis">
Testing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/debugging/" class="md-nav__link">
<span class="md-ellipsis">
Debugging
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/code-style/" class="md-nav__link">
<span class="md-ellipsis">
Code Style
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_10" >
<div class="md-nav__link md-nav__container">
<a href="../../../api-reference/" class="md-nav__link ">
<span class="md-ellipsis">
API Reference
</span>
</a>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_10_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_10">
<span class="md-nav__icon md-icon"></span>
API Reference
</label>
<ul class="md-nav__list" data-md-scrollfix>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_11" >
<div class="md-nav__link md-nav__container">
<a href="../../../user-guides/" class="md-nav__link ">
<span class="md-ellipsis">
User Guides
</span>
</a>
<label class="md-nav__link " for="__nav_2_11" id="__nav_2_11_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_11_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_11">
<span class="md-nav__icon md-icon"></span>
User Guides
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../user-guides/admin-guide/" class="md-nav__link">
<span class="md-ellipsis">
Admin Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../user-guides/campaign-manager-guide/" class="md-nav__link">
<span class="md-ellipsis">
Campaign Manager Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../user-guides/map-organizer-guide/" class="md-nav__link">
<span class="md-ellipsis">
Map Organizer Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../user-guides/content-editor-guide/" class="md-nav__link">
<span class="md-ellipsis">
Content Editor Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../user-guides/volunteer-guide/" class="md-nav__link">
<span class="md-ellipsis">
Volunteer Guide
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_12" >
<div class="md-nav__link md-nav__container">
<a href="../../../troubleshooting/" class="md-nav__link ">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
<label class="md-nav__link " for="__nav_2_12" id="__nav_2_12_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_12_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_12">
<span class="md-nav__icon md-icon"></span>
Troubleshooting
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../troubleshooting/faq/" class="md-nav__link">
<span class="md-ellipsis">
FAQ
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/common-errors/" class="md-nav__link">
<span class="md-ellipsis">
Common Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/auth-issues/" class="md-nav__link">
<span class="md-ellipsis">
Auth Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/database-issues/" class="md-nav__link">
<span class="md-ellipsis">
Database Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/docker-issues/" class="md-nav__link">
<span class="md-ellipsis">
Docker Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/email-issues/" class="md-nav__link">
<span class="md-ellipsis">
Email Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/geocoding-issues/" class="md-nav__link">
<span class="md-ellipsis">
Geocoding Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/monitoring-issues/" class="md-nav__link">
<span class="md-ellipsis">
Monitoring Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/performance-optimization/" class="md-nav__link">
<span class="md-ellipsis">
Performance Optimization
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_13" >
<div class="md-nav__link md-nav__container">
<a href="../../../migration/" class="md-nav__link ">
<span class="md-ellipsis">
Migration
</span>
</a>
<label class="md-nav__link " for="__nav_2_13" id="__nav_2_13_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_13_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_13">
<span class="md-nav__icon md-icon"></span>
Migration
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../migration/feature-parity/" class="md-nav__link">
<span class="md-ellipsis">
Feature Parity
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../migration/breaking-changes/" class="md-nav__link">
<span class="md-ellipsis">
Breaking Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../migration/api-changes/" class="md-nav__link">
<span class="md-ellipsis">
API Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../migration/data-migration/" class="md-nav__link">
<span class="md-ellipsis">
Data Migration
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_14" >
<div class="md-nav__link md-nav__container">
<a href="../../../contributing/" class="md-nav__link ">
<span class="md-ellipsis">
Contributing
</span>
</a>
<label class="md-nav__link " for="__nav_2_14" id="__nav_2_14_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_14_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_14">
<span class="md-nav__icon md-icon"></span>
Contributing
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../contributing/development-setup/" class="md-nav__link">
<span class="md-ellipsis">
Development Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../contributing/code-of-conduct/" class="md-nav__link">
<span class="md-ellipsis">
Code of Conduct
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../contributing/pull-requests/" class="md-nav__link">
<span class="md-ellipsis">
Pull Requests
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../contributing/roadmap/" class="md-nav__link">
<span class="md-ellipsis">
Roadmap
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../../phil/" class="md-nav__link">
<span class="md-ellipsis">
Philosophy
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../../v1/" class="md-nav__link">
<span class="md-ellipsis">
V1 Documentation (Legacy)
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../../blog/" class="md-nav__link">
<span class="md-ellipsis">
Blog
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#overview" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#architecture" class="md-nav__link">
<span class="md-ellipsis">
Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#database-model" class="md-nav__link">
<span class="md-ellipsis">
Database Model
</span>
</a>
<nav class="md-nav" aria-label="Database Model">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#emailtemplatevariable-schema" class="md-nav__link">
<span class="md-ellipsis">
EmailTemplateVariable Schema
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#variable-types" class="md-nav__link">
<span class="md-ellipsis">
Variable Types
</span>
</a>
<nav class="md-nav" aria-label="Variable Types">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#required-variables" class="md-nav__link">
<span class="md-ellipsis">
Required Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#optional-variables" class="md-nav__link">
<span class="md-ellipsis">
Optional Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#conditional-variables" class="md-nav__link">
<span class="md-ellipsis">
Conditional Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#array-variables-loops" class="md-nav__link">
<span class="md-ellipsis">
Array Variables (Loops)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#admin-workflow" class="md-nav__link">
<span class="md-ellipsis">
Admin Workflow
</span>
</a>
<nav class="md-nav" aria-label="Admin Workflow">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#viewing-variables" class="md-nav__link">
<span class="md-ellipsis">
Viewing Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#adding-variable" class="md-nav__link">
<span class="md-ellipsis">
Adding Variable
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#editing-variable" class="md-nav__link">
<span class="md-ellipsis">
Editing Variable
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#deleting-variable" class="md-nav__link">
<span class="md-ellipsis">
Deleting Variable
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#reordering-variables" class="md-nav__link">
<span class="md-ellipsis">
Reordering Variables
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#developer-workflow" class="md-nav__link">
<span class="md-ellipsis">
Developer Workflow
</span>
</a>
<nav class="md-nav" aria-label="Developer Workflow">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#creating-variables-programmatically" class="md-nav__link">
<span class="md-ellipsis">
Creating Variables Programmatically
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#loading-variables-in-code" class="md-nav__link">
<span class="md-ellipsis">
Loading Variables in Code
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#validating-variables" class="md-nav__link">
<span class="md-ellipsis">
Validating Variables
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#code-examples" class="md-nav__link">
<span class="md-ellipsis">
Code Examples
</span>
</a>
<nav class="md-nav" aria-label="Code Examples">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#creating-variable-via-api" class="md-nav__link">
<span class="md-ellipsis">
Creating Variable via API
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#auto-generating-sample-data" class="md-nav__link">
<span class="md-ellipsis">
Auto-Generating Sample Data
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#variable-usage-detection" class="md-nav__link">
<span class="md-ellipsis">
Variable Usage Detection
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#common-variables-by-category" class="md-nav__link">
<span class="md-ellipsis">
Common Variables by Category
</span>
</a>
<nav class="md-nav" aria-label="Common Variables by Category">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#influence-templates" class="md-nav__link">
<span class="md-ellipsis">
INFLUENCE Templates
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#map-templates" class="md-nav__link">
<span class="md-ellipsis">
MAP Templates
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#system-templates" class="md-nav__link">
<span class="md-ellipsis">
SYSTEM Templates
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#troubleshooting" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
<nav class="md-nav" aria-label="Troubleshooting">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#problem-variable-not-appearing-in-editor" class="md-nav__link">
<span class="md-ellipsis">
Problem: Variable not appearing in editor
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-validation-error-for-optional-variable" class="md-nav__link">
<span class="md-ellipsis">
Problem: Validation error for optional variable
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-sample-value-not-used-in-preview" class="md-nav__link">
<span class="md-ellipsis">
Problem: Sample value not used in preview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-duplicate-variable-key-error" class="md-nav__link">
<span class="md-ellipsis">
Problem: Duplicate variable key error
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-variables-not-alphabetically-sorted" class="md-nav__link">
<span class="md-ellipsis">
Problem: Variables not alphabetically sorted
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#performance-considerations" class="md-nav__link">
<span class="md-ellipsis">
Performance Considerations
</span>
</a>
<nav class="md-nav" aria-label="Performance Considerations">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#variable-loading" class="md-nav__link">
<span class="md-ellipsis">
Variable Loading
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#validation-performance" class="md-nav__link">
<span class="md-ellipsis">
Validation Performance
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#best-practices" class="md-nav__link">
<span class="md-ellipsis">
Best Practices
</span>
</a>
<nav class="md-nav" aria-label="Best Practices">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#variable-naming-conventions" class="md-nav__link">
<span class="md-ellipsis">
Variable Naming Conventions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#documentation" class="md-nav__link">
<span class="md-ellipsis">
Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#sample-values" class="md-nav__link">
<span class="md-ellipsis">
Sample Values
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#required-vs-optional" class="md-nav__link">
<span class="md-ellipsis">
Required vs Optional
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#related-documentation" class="md-nav__link">
<span class="md-ellipsis">
Related Documentation
</span>
</a>
<nav class="md-nav" aria-label="Related Documentation">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#frontend-documentation" class="md-nav__link">
<span class="md-ellipsis">
Frontend Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#backend-documentation" class="md-nav__link">
<span class="md-ellipsis">
Backend Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#database-documentation" class="md-nav__link">
<span class="md-ellipsis">
Database Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#feature-documentation" class="md-nav__link">
<span class="md-ellipsis">
Feature Documentation
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<nav class="md-path" aria-label="Navigation" >
<ol class="md-path__list">
<li class="md-path__item">
<a href="../../../.." class="md-path__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-path__item">
<a href="../../../" class="md-path__link">
<span class="md-ellipsis">
V2 Documentation
</span>
</a>
</li>
<li class="md-path__item">
<a href="../../" class="md-path__link">
<span class="md-ellipsis">
Features
</span>
</a>
</li>
<li class="md-path__item">
<a href="../" class="md-path__link">
<span class="md-ellipsis">
Email Templates
</span>
</a>
</li>
</ol>
</nav>
<article class="md-content__inner md-typeset">
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/main/mkdocs/docs/v2/features/email-templates/variables.md" title="Edit this page" class="md-content__button md-icon" rel="edit">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 20H6V4h7v5h5v3.1l2-2V8l-6-6H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4zm10.2-7c.1 0 .3.1.4.2l1.3 1.3c.2.2.2.6 0 .8l-1 1-2.1-2.1 1-1c.1-.1.2-.2.4-.2m0 3.9L14.1 23H12v-2.1l6.1-6.1z"/></svg>
</a>
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/main/mkdocs/docs/v2/features/email-templates/variables.md" title="View source of this page" class="md-content__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 18c.56 0 1 .44 1 1s-.44 1-1 1-1-.44-1-1 .44-1 1-1m0-3c-2.73 0-5.06 1.66-6 4 .94 2.34 3.27 4 6 4s5.06-1.66 6-4c-.94-2.34-3.27-4-6-4m0 6.5a2.5 2.5 0 0 1-2.5-2.5 2.5 2.5 0 0 1 2.5-2.5 2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5M9.27 20H6V4h7v5h5v4.07c.7.08 1.36.25 2 .49V8l-6-6H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h4.5a8.2 8.2 0 0 1-1.23-2"/></svg>
</a>
<h1 id="template-variables-system">Template Variables System<a class="headerlink" href="#template-variables-system" title="Permanent link">&para;</a></h1>
<h2 id="overview">Overview<a class="headerlink" href="#overview" title="Permanent link">&para;</a></h2>
<p>The Template Variables System defines reusable placeholders for email templates, enabling dynamic content interpolation with validation, documentation, and sample values. Variables are defined per template and provide metadata for variable insertion UI, validation logic, and testing workflows.</p>
<p><strong>Key Features:</strong></p>
<ul>
<li><strong>Per-Template Variables</strong> — Each template has its own variable definitions</li>
<li><strong>Required vs Optional</strong> — Enforce required variables at runtime</li>
<li><strong>Conditional Variables</strong> — Boolean/truthy flags for <code>{{#if}}</code> blocks</li>
<li><strong>Sample Values</strong> — Example data for testing and preview</li>
<li><strong>Sort Order</strong> — Control display order in editor UI</li>
<li><strong>Documentation</strong> — Labels and descriptions for self-documenting templates</li>
<li><strong>Validation</strong> — Runtime checks prevent missing variable errors</li>
<li><strong>Reusability</strong> — Common variables (USER_NAME, USER_EMAIL) across templates</li>
</ul>
<p><strong>Benefits:</strong></p>
<ul>
<li><strong>Type Safety</strong> — Know what data is expected before sending</li>
<li><strong>Self-Documentation</strong> — Variables describe their purpose</li>
<li><strong>Better Testing</strong> — Sample values pre-fill test send forms</li>
<li><strong>Consistency</strong> — Standardized variable naming across templates</li>
<li><strong>Error Prevention</strong> — Catch missing variables before SMTP send</li>
</ul>
<hr />
<h2 id="architecture">Architecture<a class="headerlink" href="#architecture" title="Permanent link">&para;</a></h2>
<pre class="mermaid"><code>flowchart TB
subgraph "Database Layer"
Template[(EmailTemplate)]
Variables[(EmailTemplateVariable)]
Template --&gt;|1:N| Variables
end
subgraph "Variable Definition"
VarKey[Variable Key&lt;br/&gt;USER_NAME]
VarMeta[Metadata&lt;br/&gt;label, description, isRequired]
VarSample[Sample Value&lt;br/&gt;'John Doe']
VarSort[Sort Order&lt;br/&gt;1, 2, 3...]
VarKey --&gt; Variables
VarMeta --&gt; Variables
VarSample --&gt; Variables
VarSort --&gt; Variables
end
subgraph "Template Service"
Load[Load Template + Variables]
Validate[Validate Required Variables]
Interpolate[Handlebars Interpolation]
Load --&gt; Template
Load --&gt; Variables
Validate --&gt; Variables
Interpolate --&gt;|{{VAR}}| Data[Data Object]
end
subgraph "Editor UI"
InsertPanel[Variable Insertion Panel]
PreviewForm[Sample Data Form]
TestSend[Test Send Form]
Variables --&gt; InsertPanel
Variables --&gt; PreviewForm
Variables --&gt; TestSend
end
subgraph "Runtime Validation"
Send[Send Email]
Check{Required&lt;br/&gt;Variables&lt;br/&gt;Present?}
Error[Throw MissingVariableError]
Success[Send via SMTP]
Send --&gt; Validate
Validate --&gt; Check
Check --&gt;|No| Error
Check --&gt;|Yes| Interpolate
Interpolate --&gt; Success
end
style Template fill:#50c878,color:#fff
style Variables fill:#4a90e2,color:#fff
style Validate fill:#ffb347,color:#333</code></pre>
<p><strong>Component Responsibilities:</strong></p>
<ul>
<li><strong>EmailTemplateVariable</strong> — Database model storing variable metadata</li>
<li><strong>Variable Insertion Panel</strong> — Editor UI for inserting <code>{{VARIABLES}}</code></li>
<li><strong>Sample Data Form</strong> — Preview/test form pre-filled with sample values</li>
<li><strong>Validation Service</strong> — Runtime checks before template interpolation</li>
<li><strong>Handlebars Engine</strong> — Replaces <code>{{VAR}}</code> with data values</li>
</ul>
<hr />
<h2 id="database-model">Database Model<a class="headerlink" href="#database-model" title="Permanent link">&para;</a></h2>
<h3 id="emailtemplatevariable-schema">EmailTemplateVariable Schema<a class="headerlink" href="#emailtemplatevariable-schema" title="Permanent link">&para;</a></h3>
<p><strong>Table:</strong> <code>email_template_variables</code></p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>id</code></td>
<td>String (CUID)</td>
<td>Primary key</td>
</tr>
<tr>
<td><code>templateId</code></td>
<td>String</td>
<td>Foreign key to EmailTemplate</td>
</tr>
<tr>
<td><code>key</code></td>
<td>String</td>
<td>Variable name (UPPERCASE_WITH_UNDERSCORES)</td>
</tr>
<tr>
<td><code>label</code></td>
<td>String</td>
<td>Display label for UI ("User's Full Name")</td>
</tr>
<tr>
<td><code>description</code></td>
<td>String (optional)</td>
<td>Variable purpose and usage notes</td>
</tr>
<tr>
<td><code>isRequired</code></td>
<td>Boolean</td>
<td>If true, must be provided in data object</td>
</tr>
<tr>
<td><code>isConditional</code></td>
<td>Boolean</td>
<td>If true, used in <code>{{#if}}</code> blocks (truthy/falsy)</td>
</tr>
<tr>
<td><code>sampleValue</code></td>
<td>String (optional)</td>
<td>Example value for testing/preview</td>
</tr>
<tr>
<td><code>sortOrder</code></td>
<td>Int</td>
<td>Display order in UI (1, 2, 3...)</td>
</tr>
<tr>
<td><code>createdAt</code></td>
<td>DateTime</td>
<td>Creation timestamp</td>
</tr>
</tbody>
</table>
<p><strong>Relations:</strong>
- <code>template</code> — EmailTemplate (N:1)</p>
<p><strong>Constraints:</strong>
- Unique index on <code>(templateId, key)</code> — prevents duplicate variables per template
- Index on <code>sortOrder</code> for ordered queries</p>
<p><strong>Prisma Schema:</strong>
<div class="language-text highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>model EmailTemplateVariable {
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a> id String @id @default(cuid())
</span><span id="__span-0-3"><a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a> templateId String
</span><span id="__span-0-4"><a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a> key String
</span><span id="__span-0-5"><a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> label String
</span><span id="__span-0-6"><a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> description String?
</span><span id="__span-0-7"><a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> isRequired Boolean @default(false)
</span><span id="__span-0-8"><a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> isConditional Boolean @default(false)
</span><span id="__span-0-9"><a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> sampleValue String?
</span><span id="__span-0-10"><a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> sortOrder Int @default(0)
</span><span id="__span-0-11"><a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> createdAt DateTime @default(now())
</span><span id="__span-0-12"><a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>
</span><span id="__span-0-13"><a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> template EmailTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
</span><span id="__span-0-14"><a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a>
</span><span id="__span-0-15"><a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> @@unique([templateId, key])
</span><span id="__span-0-16"><a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> @@index([sortOrder])
</span><span id="__span-0-17"><a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> @@map(&quot;email_template_variables&quot;)
</span><span id="__span-0-18"><a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>}
</span></code></pre></div></p>
<hr />
<h2 id="variable-types">Variable Types<a class="headerlink" href="#variable-types" title="Permanent link">&para;</a></h2>
<h3 id="required-variables">Required Variables<a class="headerlink" href="#required-variables" title="Permanent link">&para;</a></h3>
<p><strong>Purpose:</strong> Must be provided in data object for template to send.</p>
<p><strong>Behavior:</strong>
- Validation checks for presence before interpolation
- Throws <code>MissingRequiredVariableError</code> if missing
- Marked with red "Required" badge in editor UI</p>
<p><strong>When to Use:</strong>
- Variables that appear in ALL template renders
- Variables without fallback values
- Critical data (e.g., recipient name, event date)</p>
<p><strong>Example:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplateVariable</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-1-3"><a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">template.id</span><span class="p">,</span>
</span><span id="__span-1-4"><a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;USER_NAME&#39;</span><span class="p">,</span>
</span><span id="__span-1-5"><a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;User Name&#39;</span><span class="p">,</span>
</span><span id="__span-1-6"><a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Full name of the email recipient&#39;</span><span class="p">,</span>
</span><span id="__span-1-7"><a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span><span class="w"> </span><span class="c1">// ← MUST be provided</span>
</span><span id="__span-1-8"><a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-1-9"><a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;John Doe&#39;</span><span class="p">,</span>
</span><span id="__span-1-10"><a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">1</span><span class="p">,</span>
</span><span id="__span-1-11"><a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-1-12"><a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a><span class="p">});</span>
</span></code></pre></div></p>
<p><strong>Template Usage:</strong>
<div class="language-html highlight"><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Dear {{USER_NAME}},<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="cm">&lt;!-- USER_NAME is required, error if missing --&gt;</span>
</span></code></pre></div></p>
<hr />
<h3 id="optional-variables">Optional Variables<a class="headerlink" href="#optional-variables" title="Permanent link">&para;</a></h3>
<p><strong>Purpose:</strong> May be omitted from data object (defaults to empty string).</p>
<p><strong>Behavior:</strong>
- No validation error if missing
- Handlebars renders as empty string if undefined
- Useful for conditional content or nice-to-have data</p>
<p><strong>When to Use:</strong>
- Variables that may not always be available (e.g., phone number)
- Variables with fallback text in template
- Conditional blocks that check presence</p>
<p><strong>Example:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplateVariable</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">template.id</span><span class="p">,</span>
</span><span id="__span-3-4"><a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;USER_PHONE&#39;</span><span class="p">,</span>
</span><span id="__span-3-5"><a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;User Phone&#39;</span><span class="p">,</span>
</span><span id="__span-3-6"><a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;User phone number (optional)&#39;</span><span class="p">,</span>
</span><span id="__span-3-7"><a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span><span class="w"> </span><span class="c1">// ← Can be omitted</span>
</span><span id="__span-3-8"><a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-3-9"><a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;(555) 123-4567&#39;</span><span class="p">,</span>
</span><span id="__span-3-10"><a id="__codelineno-3-10" name="__codelineno-3-10" href="#__codelineno-3-10"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">5</span><span class="p">,</span>
</span><span id="__span-3-11"><a id="__codelineno-3-11" name="__codelineno-3-11" href="#__codelineno-3-11"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-3-12"><a id="__codelineno-3-12" name="__codelineno-3-12" href="#__codelineno-3-12"></a><span class="p">});</span>
</span></code></pre></div></p>
<p><strong>Template Usage:</strong>
<div class="language-html highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a>{{#if USER_PHONE}}
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>We&#39;ll call you at {{USER_PHONE}}.<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a>{{else}}
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Add a phone number to receive SMS updates.<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a>{{/if}}
</span></code></pre></div></p>
<hr />
<h3 id="conditional-variables">Conditional Variables<a class="headerlink" href="#conditional-variables" title="Permanent link">&para;</a></h3>
<p><strong>Purpose:</strong> Boolean or truthy/falsy values for <code>{{#if}}</code> blocks.</p>
<p><strong>Behavior:</strong>
- <code>isConditional: true</code> marks variable as boolean-like
- Editor UI shows blue "Conditional" badge
- Used in <code>{{#if VAR}}...{{/if}}</code> blocks
- Can also be required or optional</p>
<p><strong>When to Use:</strong>
- Boolean flags (HAS_PHONE, IS_VERIFIED, IS_ADMIN)
- Existence checks (HAS_CUSTOM_MESSAGE, HAS_LOCATION)
- Feature flags (SHOW_DISCOUNT, SHOW_MAP_LINK)</p>
<p><strong>Example:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplateVariable</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">template.id</span><span class="p">,</span>
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;HAS_PHONE&#39;</span><span class="p">,</span>
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Has Phone Number&#39;</span><span class="p">,</span>
</span><span id="__span-5-6"><a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Whether user provided a phone number&#39;</span><span class="p">,</span>
</span><span id="__span-5-7"><a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-5-8"><a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span><span class="w"> </span><span class="c1">// ← Boolean/truthy variable</span>
</span><span id="__span-5-9"><a id="__codelineno-5-9" name="__codelineno-5-9" href="#__codelineno-5-9"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;true&#39;</span><span class="p">,</span>
</span><span id="__span-5-10"><a id="__codelineno-5-10" name="__codelineno-5-10" href="#__codelineno-5-10"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">4</span><span class="p">,</span>
</span><span id="__span-5-11"><a id="__codelineno-5-11" name="__codelineno-5-11" href="#__codelineno-5-11"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-5-12"><a id="__codelineno-5-12" name="__codelineno-5-12" href="#__codelineno-5-12"></a><span class="p">});</span>
</span></code></pre></div></p>
<p><strong>Template Usage:</strong>
<div class="language-html highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a>{{#if HAS_PHONE}}
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Contact: {{USER_PHONE}}<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span><span id="__span-6-3"><a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a>{{/if}}
</span></code></pre></div></p>
<p><strong>Truthy Values:</strong>
- <code>true</code>, <code>'true'</code>, <code>1</code>, non-empty strings, non-empty arrays</p>
<p><strong>Falsy Values:</strong>
- <code>false</code>, <code>'false'</code>, <code>0</code>, <code>''</code>, <code>null</code>, <code>undefined</code>, <code>[]</code></p>
<hr />
<h3 id="array-variables-loops">Array Variables (Loops)<a class="headerlink" href="#array-variables-loops" title="Permanent link">&para;</a></h3>
<p><strong>Purpose:</strong> Collections for <code>{{#each}}</code> blocks.</p>
<p><strong>Behavior:</strong>
- Not explicitly marked (same as other variables)
- Sample value should be JSON array string
- Used in <code>{{#each VAR}}...{{/each}}</code> loops</p>
<p><strong>When to Use:</strong>
- Lists of representatives, shift assignments, visit outcomes
- Dynamic content length (1-N items)</p>
<p><strong>Example:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplateVariable</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">template.id</span><span class="p">,</span>
</span><span id="__span-7-4"><a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;REPRESENTATIVES&#39;</span><span class="p">,</span>
</span><span id="__span-7-5"><a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Representative List&#39;</span><span class="p">,</span>
</span><span id="__span-7-6"><a id="__codelineno-7-6" name="__codelineno-7-6" href="#__codelineno-7-6"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Array of representative objects&#39;</span><span class="p">,</span>
</span><span id="__span-7-7"><a id="__codelineno-7-7" name="__codelineno-7-7" href="#__codelineno-7-7"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
</span><span id="__span-7-8"><a id="__codelineno-7-8" name="__codelineno-7-8" href="#__codelineno-7-8"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-7-9"><a id="__codelineno-7-9" name="__codelineno-7-9" href="#__codelineno-7-9"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="kt">JSON.stringify</span><span class="p">([</span>
</span><span id="__span-7-10"><a id="__codelineno-7-10" name="__codelineno-7-10" href="#__codelineno-7-10"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Jane Doe&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;MP&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">email</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;jane@parliament.ca&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-7-11"><a id="__codelineno-7-11" name="__codelineno-7-11" href="#__codelineno-7-11"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;John Smith&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Councillor&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">email</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;john@city.ca&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-7-12"><a id="__codelineno-7-12" name="__codelineno-7-12" href="#__codelineno-7-12"></a><span class="w"> </span><span class="p">]),</span>
</span><span id="__span-7-13"><a id="__codelineno-7-13" name="__codelineno-7-13" href="#__codelineno-7-13"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">10</span><span class="p">,</span>
</span><span id="__span-7-14"><a id="__codelineno-7-14" name="__codelineno-7-14" href="#__codelineno-7-14"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-7-15"><a id="__codelineno-7-15" name="__codelineno-7-15" href="#__codelineno-7-15"></a><span class="p">});</span>
</span></code></pre></div></p>
<p><strong>Template Usage:</strong>
<div class="language-html highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>{{#each REPRESENTATIVES}}
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a> <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>
</span><span id="__span-8-4"><a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a> <span class="p">&lt;</span><span class="nt">strong</span><span class="p">&gt;</span>{{name}}<span class="p">&lt;/</span><span class="nt">strong</span><span class="p">&gt;</span> ({{title}})<span class="p">&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
</span><span id="__span-8-5"><a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a> Email: {{email}}
</span><span id="__span-8-6"><a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a> <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span><span id="__span-8-7"><a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a>{{/each}}
</span><span id="__span-8-8"><a id="__codelineno-8-8" name="__codelineno-8-8" href="#__codelineno-8-8"></a><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></code></pre></div></p>
<p><strong>Data Object:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="p">{</span>
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a><span class="w"> </span><span class="nx">REPRESENTATIVES</span><span class="o">:</span><span class="w"> </span><span class="p">[</span>
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Jane Doe&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;MP&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">email</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;jane@parliament.ca&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;John Smith&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Councillor&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">email</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;john@city.ca&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-9-5"><a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a><span class="w"> </span><span class="p">],</span>
</span><span id="__span-9-6"><a id="__codelineno-9-6" name="__codelineno-9-6" href="#__codelineno-9-6"></a><span class="p">}</span>
</span></code></pre></div></p>
<hr />
<h2 id="admin-workflow">Admin Workflow<a class="headerlink" href="#admin-workflow" title="Permanent link">&para;</a></h2>
<h3 id="viewing-variables">Viewing Variables<a class="headerlink" href="#viewing-variables" title="Permanent link">&para;</a></h3>
<p><strong>From EmailTemplatesPage:</strong></p>
<ol>
<li><strong>Click Template Row</strong></li>
<li>
<p>Opens template detail modal</p>
</li>
<li>
<p><strong>Navigate to "Variables" Tab</strong></p>
</li>
<li>Shows table of all variables</li>
<li>
<p>Columns: Key, Label, Required, Conditional, Sample Value, Sort Order</p>
</li>
<li>
<p><strong>Variable Details</strong></p>
</li>
<li>Click variable row for description</li>
<li>See where variable is used in template content</li>
<li>View sample value</li>
</ol>
<p><strong>From EmailTemplateEditorPage:</strong></p>
<ol>
<li><strong>Open Template Editor</strong></li>
<li>
<p>Variables shown in right sidebar</p>
</li>
<li>
<p><strong>Variable Insertion Panel</strong></p>
</li>
<li>Variables listed with labels, badges, descriptions</li>
<li>Sorted by sortOrder ascending</li>
<li>Click "Insert to HTML/Text" buttons</li>
</ol>
<hr />
<h3 id="adding-variable">Adding Variable<a class="headerlink" href="#adding-variable" title="Permanent link">&para;</a></h3>
<p><strong>Step 1: Open Variables Tab</strong>
- EmailTemplatesPage → click template → "Variables" tab</p>
<p><strong>Step 2: Click "Add Variable" Button</strong>
- Opens variable creation modal</p>
<p><strong>Step 3: Enter Variable Metadata</strong></p>
<p><strong>Key (required):</strong>
- Uppercase with underscores (e.g., <code>USER_NAME</code>)
- Must be unique within template
- Used in template as <code>{{KEY}}</code></p>
<p><strong>Label (required):</strong>
- Display name for UI (e.g., "User's Full Name")
- Human-readable description</p>
<p><strong>Description (optional):</strong>
- Detailed explanation of variable purpose
- Usage notes (e.g., "Must be in YYYY-MM-DD format")</p>
<p><strong>Is Required:</strong>
- Toggle on if variable must always be provided
- Validation will fail if missing</p>
<p><strong>Is Conditional:</strong>
- Toggle on if variable is used in <code>{{#if}}</code> blocks
- UI shows blue "Conditional" badge</p>
<p><strong>Sample Value (optional):</strong>
- Example value for testing/preview
- Pre-fills test send form
- Shows expected data format</p>
<p><strong>Sort Order:</strong>
- Numeric order for UI display
- Lower numbers appear first (1, 2, 3...)
- Auto-assigned if not specified</p>
<p><strong>Step 4: Save Variable</strong>
- Click "Save" button
- Variable added to template
- Available in editor insertion panel</p>
<hr />
<h3 id="editing-variable">Editing Variable<a class="headerlink" href="#editing-variable" title="Permanent link">&para;</a></h3>
<p><strong>Step 1: Open Variables Tab</strong>
- EmailTemplatesPage → click template → "Variables" tab</p>
<p><strong>Step 2: Click Variable Row</strong>
- Opens variable edit modal
- Shows current values</p>
<p><strong>Step 3: Modify Fields</strong>
- Change label, description, flags, sample value
- Cannot change key (would break existing templates)</p>
<p><strong>Step 4: Save Changes</strong>
- Click "Save" button
- Variable updated in database</p>
<p><strong>Note:</strong> Changing variable key requires creating new variable and updating template content manually.</p>
<hr />
<h3 id="deleting-variable">Deleting Variable<a class="headerlink" href="#deleting-variable" title="Permanent link">&para;</a></h3>
<p><strong>Step 1: Check Template Usage</strong>
- Search template content for <code>{{VAR_KEY}}</code>
- Ensure variable is not used in subject/HTML/text</p>
<p><strong>Step 2: Click Delete Button</strong>
- Variables tab → click variable row → "Delete" button</p>
<p><strong>Step 3: Confirm Deletion</strong>
- Warning modal: "Are you sure? This cannot be undone."
- Click "Confirm Delete"</p>
<p><strong>Step 4: Verify Template Still Valid</strong>
- Open template editor
- Check preview renders without errors
- Send test email</p>
<p><strong>Warning:</strong> Deleting a variable that's still used in template content will cause rendering errors (<code>{{VAR}}</code> will appear as literal text).</p>
<hr />
<h3 id="reordering-variables">Reordering Variables<a class="headerlink" href="#reordering-variables" title="Permanent link">&para;</a></h3>
<p><strong>Step 1: Open Variables Tab</strong>
- EmailTemplatesPage → click template → "Variables" tab</p>
<p><strong>Step 2: Drag to Reorder</strong>
- Drag variable rows up/down
- Drop to new position</p>
<p><strong>Step 3: Save Sort Order</strong>
- Click "Save Order" button
- Updates <code>sortOrder</code> field for all variables</p>
<p><strong>Alternative: Manual Sort Order</strong>
- Edit variable → change sortOrder number
- Variables re-sort automatically</p>
<hr />
<h2 id="developer-workflow">Developer Workflow<a class="headerlink" href="#developer-workflow" title="Permanent link">&para;</a></h2>
<h3 id="creating-variables-programmatically">Creating Variables Programmatically<a class="headerlink" href="#creating-variables-programmatically" title="Permanent link">&para;</a></h3>
<p><strong>Seed Script Example:</strong></p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="c1">// api/prisma/seed.ts</span>
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a><span class="kd">const</span><span class="w"> </span><span class="nx">template</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplate</span><span class="p">.</span><span class="nx">findUnique</span><span class="p">({</span>
</span><span id="__span-10-4"><a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;shift-signup-confirmation&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-5"><a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a><span class="p">});</span>
</span><span id="__span-10-6"><a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a>
</span><span id="__span-10-7"><a id="__codelineno-10-7" name="__codelineno-10-7" href="#__codelineno-10-7"></a><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">template</span><span class="p">)</span><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="s1">&#39;Template not found&#39;</span><span class="p">);</span>
</span><span id="__span-10-8"><a id="__codelineno-10-8" name="__codelineno-10-8" href="#__codelineno-10-8"></a>
</span><span id="__span-10-9"><a id="__codelineno-10-9" name="__codelineno-10-9" href="#__codelineno-10-9"></a><span class="kd">const</span><span class="w"> </span><span class="nx">variables</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
</span><span id="__span-10-10"><a id="__codelineno-10-10" name="__codelineno-10-10" href="#__codelineno-10-10"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-11"><a id="__codelineno-10-11" name="__codelineno-10-11" href="#__codelineno-10-11"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;USER_NAME&#39;</span><span class="p">,</span>
</span><span id="__span-10-12"><a id="__codelineno-10-12" name="__codelineno-10-12" href="#__codelineno-10-12"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;User Name&#39;</span><span class="p">,</span>
</span><span id="__span-10-13"><a id="__codelineno-10-13" name="__codelineno-10-13" href="#__codelineno-10-13"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Full name of the volunteer&#39;</span><span class="p">,</span>
</span><span id="__span-10-14"><a id="__codelineno-10-14" name="__codelineno-10-14" href="#__codelineno-10-14"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
</span><span id="__span-10-15"><a id="__codelineno-10-15" name="__codelineno-10-15" href="#__codelineno-10-15"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-10-16"><a id="__codelineno-10-16" name="__codelineno-10-16" href="#__codelineno-10-16"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;John Doe&#39;</span><span class="p">,</span>
</span><span id="__span-10-17"><a id="__codelineno-10-17" name="__codelineno-10-17" href="#__codelineno-10-17"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">1</span><span class="p">,</span>
</span><span id="__span-10-18"><a id="__codelineno-10-18" name="__codelineno-10-18" href="#__codelineno-10-18"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-19"><a id="__codelineno-10-19" name="__codelineno-10-19" href="#__codelineno-10-19"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-20"><a id="__codelineno-10-20" name="__codelineno-10-20" href="#__codelineno-10-20"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;USER_EMAIL&#39;</span><span class="p">,</span>
</span><span id="__span-10-21"><a id="__codelineno-10-21" name="__codelineno-10-21" href="#__codelineno-10-21"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;User Email&#39;</span><span class="p">,</span>
</span><span id="__span-10-22"><a id="__codelineno-10-22" name="__codelineno-10-22" href="#__codelineno-10-22"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Email address of the volunteer&#39;</span><span class="p">,</span>
</span><span id="__span-10-23"><a id="__codelineno-10-23" name="__codelineno-10-23" href="#__codelineno-10-23"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
</span><span id="__span-10-24"><a id="__codelineno-10-24" name="__codelineno-10-24" href="#__codelineno-10-24"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-10-25"><a id="__codelineno-10-25" name="__codelineno-10-25" href="#__codelineno-10-25"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;john@example.com&#39;</span><span class="p">,</span>
</span><span id="__span-10-26"><a id="__codelineno-10-26" name="__codelineno-10-26" href="#__codelineno-10-26"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">2</span><span class="p">,</span>
</span><span id="__span-10-27"><a id="__codelineno-10-27" name="__codelineno-10-27" href="#__codelineno-10-27"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-28"><a id="__codelineno-10-28" name="__codelineno-10-28" href="#__codelineno-10-28"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-29"><a id="__codelineno-10-29" name="__codelineno-10-29" href="#__codelineno-10-29"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;SHIFT_TITLE&#39;</span><span class="p">,</span>
</span><span id="__span-10-30"><a id="__codelineno-10-30" name="__codelineno-10-30" href="#__codelineno-10-30"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Shift Title&#39;</span><span class="p">,</span>
</span><span id="__span-10-31"><a id="__codelineno-10-31" name="__codelineno-10-31" href="#__codelineno-10-31"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Name of the shift&#39;</span><span class="p">,</span>
</span><span id="__span-10-32"><a id="__codelineno-10-32" name="__codelineno-10-32" href="#__codelineno-10-32"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
</span><span id="__span-10-33"><a id="__codelineno-10-33" name="__codelineno-10-33" href="#__codelineno-10-33"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-10-34"><a id="__codelineno-10-34" name="__codelineno-10-34" href="#__codelineno-10-34"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Door Knocking - Downtown&#39;</span><span class="p">,</span>
</span><span id="__span-10-35"><a id="__codelineno-10-35" name="__codelineno-10-35" href="#__codelineno-10-35"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">3</span><span class="p">,</span>
</span><span id="__span-10-36"><a id="__codelineno-10-36" name="__codelineno-10-36" href="#__codelineno-10-36"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-37"><a id="__codelineno-10-37" name="__codelineno-10-37" href="#__codelineno-10-37"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-38"><a id="__codelineno-10-38" name="__codelineno-10-38" href="#__codelineno-10-38"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;SHIFT_DATE&#39;</span><span class="p">,</span>
</span><span id="__span-10-39"><a id="__codelineno-10-39" name="__codelineno-10-39" href="#__codelineno-10-39"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Shift Date&#39;</span><span class="p">,</span>
</span><span id="__span-10-40"><a id="__codelineno-10-40" name="__codelineno-10-40" href="#__codelineno-10-40"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Formatted shift date (e.g., &quot;Saturday, March 15, 2026&quot;)&#39;</span><span class="p">,</span>
</span><span id="__span-10-41"><a id="__codelineno-10-41" name="__codelineno-10-41" href="#__codelineno-10-41"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
</span><span id="__span-10-42"><a id="__codelineno-10-42" name="__codelineno-10-42" href="#__codelineno-10-42"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-10-43"><a id="__codelineno-10-43" name="__codelineno-10-43" href="#__codelineno-10-43"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Saturday, March 15, 2026&#39;</span><span class="p">,</span>
</span><span id="__span-10-44"><a id="__codelineno-10-44" name="__codelineno-10-44" href="#__codelineno-10-44"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">4</span><span class="p">,</span>
</span><span id="__span-10-45"><a id="__codelineno-10-45" name="__codelineno-10-45" href="#__codelineno-10-45"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-46"><a id="__codelineno-10-46" name="__codelineno-10-46" href="#__codelineno-10-46"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-47"><a id="__codelineno-10-47" name="__codelineno-10-47" href="#__codelineno-10-47"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;SHIFT_TIME&#39;</span><span class="p">,</span>
</span><span id="__span-10-48"><a id="__codelineno-10-48" name="__codelineno-10-48" href="#__codelineno-10-48"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Shift Time&#39;</span><span class="p">,</span>
</span><span id="__span-10-49"><a id="__codelineno-10-49" name="__codelineno-10-49" href="#__codelineno-10-49"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Shift time range (e.g., &quot;10:00 AM - 2:00 PM&quot;)&#39;</span><span class="p">,</span>
</span><span id="__span-10-50"><a id="__codelineno-10-50" name="__codelineno-10-50" href="#__codelineno-10-50"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
</span><span id="__span-10-51"><a id="__codelineno-10-51" name="__codelineno-10-51" href="#__codelineno-10-51"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-10-52"><a id="__codelineno-10-52" name="__codelineno-10-52" href="#__codelineno-10-52"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;10:00 AM - 2:00 PM&#39;</span><span class="p">,</span>
</span><span id="__span-10-53"><a id="__codelineno-10-53" name="__codelineno-10-53" href="#__codelineno-10-53"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">5</span><span class="p">,</span>
</span><span id="__span-10-54"><a id="__codelineno-10-54" name="__codelineno-10-54" href="#__codelineno-10-54"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-55"><a id="__codelineno-10-55" name="__codelineno-10-55" href="#__codelineno-10-55"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-56"><a id="__codelineno-10-56" name="__codelineno-10-56" href="#__codelineno-10-56"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;SHIFT_LOCATION&#39;</span><span class="p">,</span>
</span><span id="__span-10-57"><a id="__codelineno-10-57" name="__codelineno-10-57" href="#__codelineno-10-57"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Shift Location&#39;</span><span class="p">,</span>
</span><span id="__span-10-58"><a id="__codelineno-10-58" name="__codelineno-10-58" href="#__codelineno-10-58"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Meeting location for shift&#39;</span><span class="p">,</span>
</span><span id="__span-10-59"><a id="__codelineno-10-59" name="__codelineno-10-59" href="#__codelineno-10-59"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
</span><span id="__span-10-60"><a id="__codelineno-10-60" name="__codelineno-10-60" href="#__codelineno-10-60"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-10-61"><a id="__codelineno-10-61" name="__codelineno-10-61" href="#__codelineno-10-61"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Campaign Office (123 Main St)&#39;</span><span class="p">,</span>
</span><span id="__span-10-62"><a id="__codelineno-10-62" name="__codelineno-10-62" href="#__codelineno-10-62"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">6</span><span class="p">,</span>
</span><span id="__span-10-63"><a id="__codelineno-10-63" name="__codelineno-10-63" href="#__codelineno-10-63"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-64"><a id="__codelineno-10-64" name="__codelineno-10-64" href="#__codelineno-10-64"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-65"><a id="__codelineno-10-65" name="__codelineno-10-65" href="#__codelineno-10-65"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;HAS_PHONE&#39;</span><span class="p">,</span>
</span><span id="__span-10-66"><a id="__codelineno-10-66" name="__codelineno-10-66" href="#__codelineno-10-66"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Has Phone Number&#39;</span><span class="p">,</span>
</span><span id="__span-10-67"><a id="__codelineno-10-67" name="__codelineno-10-67" href="#__codelineno-10-67"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Whether user provided a phone number&#39;</span><span class="p">,</span>
</span><span id="__span-10-68"><a id="__codelineno-10-68" name="__codelineno-10-68" href="#__codelineno-10-68"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-10-69"><a id="__codelineno-10-69" name="__codelineno-10-69" href="#__codelineno-10-69"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
</span><span id="__span-10-70"><a id="__codelineno-10-70" name="__codelineno-10-70" href="#__codelineno-10-70"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;true&#39;</span><span class="p">,</span>
</span><span id="__span-10-71"><a id="__codelineno-10-71" name="__codelineno-10-71" href="#__codelineno-10-71"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">7</span><span class="p">,</span>
</span><span id="__span-10-72"><a id="__codelineno-10-72" name="__codelineno-10-72" href="#__codelineno-10-72"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-73"><a id="__codelineno-10-73" name="__codelineno-10-73" href="#__codelineno-10-73"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-74"><a id="__codelineno-10-74" name="__codelineno-10-74" href="#__codelineno-10-74"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;USER_PHONE&#39;</span><span class="p">,</span>
</span><span id="__span-10-75"><a id="__codelineno-10-75" name="__codelineno-10-75" href="#__codelineno-10-75"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;User Phone&#39;</span><span class="p">,</span>
</span><span id="__span-10-76"><a id="__codelineno-10-76" name="__codelineno-10-76" href="#__codelineno-10-76"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;User phone number (optional)&#39;</span><span class="p">,</span>
</span><span id="__span-10-77"><a id="__codelineno-10-77" name="__codelineno-10-77" href="#__codelineno-10-77"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-10-78"><a id="__codelineno-10-78" name="__codelineno-10-78" href="#__codelineno-10-78"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
</span><span id="__span-10-79"><a id="__codelineno-10-79" name="__codelineno-10-79" href="#__codelineno-10-79"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;(555) 123-4567&#39;</span><span class="p">,</span>
</span><span id="__span-10-80"><a id="__codelineno-10-80" name="__codelineno-10-80" href="#__codelineno-10-80"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">8</span><span class="p">,</span>
</span><span id="__span-10-81"><a id="__codelineno-10-81" name="__codelineno-10-81" href="#__codelineno-10-81"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-82"><a id="__codelineno-10-82" name="__codelineno-10-82" href="#__codelineno-10-82"></a><span class="p">];</span>
</span><span id="__span-10-83"><a id="__codelineno-10-83" name="__codelineno-10-83" href="#__codelineno-10-83"></a>
</span><span id="__span-10-84"><a id="__codelineno-10-84" name="__codelineno-10-84" href="#__codelineno-10-84"></a><span class="c1">// Upsert variables</span>
</span><span id="__span-10-85"><a id="__codelineno-10-85" name="__codelineno-10-85" href="#__codelineno-10-85"></a><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">const</span><span class="w"> </span><span class="nx">variable</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nx">variables</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-86"><a id="__codelineno-10-86" name="__codelineno-10-86" href="#__codelineno-10-86"></a><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplateVariable</span><span class="p">.</span><span class="nx">upsert</span><span class="p">({</span>
</span><span id="__span-10-87"><a id="__codelineno-10-87" name="__codelineno-10-87" href="#__codelineno-10-87"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-88"><a id="__codelineno-10-88" name="__codelineno-10-88" href="#__codelineno-10-88"></a><span class="w"> </span><span class="nx">templateId_key</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-89"><a id="__codelineno-10-89" name="__codelineno-10-89" href="#__codelineno-10-89"></a><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">template.id</span><span class="p">,</span>
</span><span id="__span-10-90"><a id="__codelineno-10-90" name="__codelineno-10-90" href="#__codelineno-10-90"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="kt">variable.key</span><span class="p">,</span>
</span><span id="__span-10-91"><a id="__codelineno-10-91" name="__codelineno-10-91" href="#__codelineno-10-91"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-92"><a id="__codelineno-10-92" name="__codelineno-10-92" href="#__codelineno-10-92"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-93"><a id="__codelineno-10-93" name="__codelineno-10-93" href="#__codelineno-10-93"></a><span class="w"> </span><span class="nx">update</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-94"><a id="__codelineno-10-94" name="__codelineno-10-94" href="#__codelineno-10-94"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="kt">variable.label</span><span class="p">,</span>
</span><span id="__span-10-95"><a id="__codelineno-10-95" name="__codelineno-10-95" href="#__codelineno-10-95"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="kt">variable.description</span><span class="p">,</span>
</span><span id="__span-10-96"><a id="__codelineno-10-96" name="__codelineno-10-96" href="#__codelineno-10-96"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">variable.isRequired</span><span class="p">,</span>
</span><span id="__span-10-97"><a id="__codelineno-10-97" name="__codelineno-10-97" href="#__codelineno-10-97"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">variable.isConditional</span><span class="p">,</span>
</span><span id="__span-10-98"><a id="__codelineno-10-98" name="__codelineno-10-98" href="#__codelineno-10-98"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="kt">variable.sampleValue</span><span class="p">,</span>
</span><span id="__span-10-99"><a id="__codelineno-10-99" name="__codelineno-10-99" href="#__codelineno-10-99"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">variable.sortOrder</span><span class="p">,</span>
</span><span id="__span-10-100"><a id="__codelineno-10-100" name="__codelineno-10-100" href="#__codelineno-10-100"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-101"><a id="__codelineno-10-101" name="__codelineno-10-101" href="#__codelineno-10-101"></a><span class="w"> </span><span class="nx">create</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-102"><a id="__codelineno-10-102" name="__codelineno-10-102" href="#__codelineno-10-102"></a><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">template.id</span><span class="p">,</span>
</span><span id="__span-10-103"><a id="__codelineno-10-103" name="__codelineno-10-103" href="#__codelineno-10-103"></a><span class="w"> </span><span class="p">...</span><span class="nx">variable</span><span class="p">,</span>
</span><span id="__span-10-104"><a id="__codelineno-10-104" name="__codelineno-10-104" href="#__codelineno-10-104"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-10-105"><a id="__codelineno-10-105" name="__codelineno-10-105" href="#__codelineno-10-105"></a><span class="w"> </span><span class="p">});</span>
</span><span id="__span-10-106"><a id="__codelineno-10-106" name="__codelineno-10-106" href="#__codelineno-10-106"></a><span class="p">}</span>
</span><span id="__span-10-107"><a id="__codelineno-10-107" name="__codelineno-10-107" href="#__codelineno-10-107"></a>
</span><span id="__span-10-108"><a id="__codelineno-10-108" name="__codelineno-10-108" href="#__codelineno-10-108"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`✓ Created </span><span class="si">${</span><span class="nx">variables</span><span class="p">.</span><span class="nx">length</span><span class="si">}</span><span class="sb"> variables for shift-signup-confirmation template`</span><span class="p">);</span>
</span></code></pre></div>
<hr />
<h3 id="loading-variables-in-code">Loading Variables in Code<a class="headerlink" href="#loading-variables-in-code" title="Permanent link">&para;</a></h3>
<p><strong>With Template:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">template</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplate</span><span class="p">.</span><span class="nx">findUnique</span><span class="p">({</span>
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;shift-signup-confirmation&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="w"> </span><span class="nx">include</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">variables</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a><span class="p">});</span>
</span><span id="__span-11-5"><a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a>
</span><span id="__span-11-6"><a id="__codelineno-11-6" name="__codelineno-11-6" href="#__codelineno-11-6"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Template variables:&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">template</span><span class="o">?</span><span class="p">.</span><span class="nx">variables</span><span class="p">);</span>
</span></code></pre></div></p>
<p><strong>Ordered by Sort:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">template</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplate</span><span class="p">.</span><span class="nx">findUnique</span><span class="p">({</span>
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;shift-signup-confirmation&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-12-3"><a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a><span class="w"> </span><span class="nx">include</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-12-4"><a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a><span class="w"> </span><span class="nx">variables</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-12-5"><a id="__codelineno-12-5" name="__codelineno-12-5" href="#__codelineno-12-5"></a><span class="w"> </span><span class="nx">orderBy</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;asc&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-12-6"><a id="__codelineno-12-6" name="__codelineno-12-6" href="#__codelineno-12-6"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-12-7"><a id="__codelineno-12-7" name="__codelineno-12-7" href="#__codelineno-12-7"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-12-8"><a id="__codelineno-12-8" name="__codelineno-12-8" href="#__codelineno-12-8"></a><span class="p">});</span>
</span></code></pre></div></p>
<p><strong>Required Variables Only:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">requiredVars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplateVariable</span><span class="p">.</span><span class="nx">findMany</span><span class="p">({</span>
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">template.id</span><span class="p">,</span>
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
</span><span id="__span-13-5"><a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-13-6"><a id="__codelineno-13-6" name="__codelineno-13-6" href="#__codelineno-13-6"></a><span class="p">});</span>
</span><span id="__span-13-7"><a id="__codelineno-13-7" name="__codelineno-13-7" href="#__codelineno-13-7"></a>
</span><span id="__span-13-8"><a id="__codelineno-13-8" name="__codelineno-13-8" href="#__codelineno-13-8"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Required variables:&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">requiredVars</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">key</span><span class="p">));</span>
</span></code></pre></div></p>
<hr />
<h3 id="validating-variables">Validating Variables<a class="headerlink" href="#validating-variables" title="Permanent link">&para;</a></h3>
<p><strong>Validation Function:</strong></p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="c1">// api/src/services/email.service.ts</span>
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a>
</span><span id="__span-14-3"><a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a><span class="kd">function</span><span class="w"> </span><span class="nx">validateVariables</span><span class="p">(</span>
</span><span id="__span-14-4"><a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="w"> </span><span class="nx">template</span><span class="o">:</span><span class="w"> </span><span class="kt">EmailTemplate</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">variables</span><span class="o">:</span><span class="w"> </span><span class="kt">EmailTemplateVariable</span><span class="p">[]</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-14-5"><a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="kt">Record</span><span class="o">&lt;</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">unknown</span><span class="o">&gt;</span>
</span><span id="__span-14-6"><a id="__codelineno-14-6" name="__codelineno-14-6" href="#__codelineno-14-6"></a><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-14-7"><a id="__codelineno-14-7" name="__codelineno-14-7" href="#__codelineno-14-7"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">missing</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[];</span>
</span><span id="__span-14-8"><a id="__codelineno-14-8" name="__codelineno-14-8" href="#__codelineno-14-8"></a>
</span><span id="__span-14-9"><a id="__codelineno-14-9" name="__codelineno-14-9" href="#__codelineno-14-9"></a><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">const</span><span class="w"> </span><span class="nx">variable</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nx">template</span><span class="p">.</span><span class="nx">variables</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-14-10"><a id="__codelineno-14-10" name="__codelineno-14-10" href="#__codelineno-14-10"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">variable</span><span class="p">.</span><span class="nx">isRequired</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="p">(</span><span class="nx">data</span><span class="p">[</span><span class="nx">variable</span><span class="p">.</span><span class="nx">key</span><span class="p">]</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">data</span><span class="p">[</span><span class="nx">variable</span><span class="p">.</span><span class="nx">key</span><span class="p">]</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="kc">null</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-14-11"><a id="__codelineno-14-11" name="__codelineno-14-11" href="#__codelineno-14-11"></a><span class="w"> </span><span class="nx">missing</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">variable</span><span class="p">.</span><span class="nx">key</span><span class="p">);</span>
</span><span id="__span-14-12"><a id="__codelineno-14-12" name="__codelineno-14-12" href="#__codelineno-14-12"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-14-13"><a id="__codelineno-14-13" name="__codelineno-14-13" href="#__codelineno-14-13"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-14-14"><a id="__codelineno-14-14" name="__codelineno-14-14" href="#__codelineno-14-14"></a>
</span><span id="__span-14-15"><a id="__codelineno-14-15" name="__codelineno-14-15" href="#__codelineno-14-15"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">missing</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mf">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-14-16"><a id="__codelineno-14-16" name="__codelineno-14-16" href="#__codelineno-14-16"></a><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">MissingRequiredVariableError</span><span class="p">(</span>
</span><span id="__span-14-17"><a id="__codelineno-14-17" name="__codelineno-14-17" href="#__codelineno-14-17"></a><span class="w"> </span><span class="sb">`Missing required variables for template </span><span class="si">${</span><span class="nx">template</span><span class="p">.</span><span class="nx">key</span><span class="si">}</span><span class="sb">: </span><span class="si">${</span><span class="nx">missing</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39;, &#39;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span>
</span><span id="__span-14-18"><a id="__codelineno-14-18" name="__codelineno-14-18" href="#__codelineno-14-18"></a><span class="w"> </span><span class="p">);</span>
</span><span id="__span-14-19"><a id="__codelineno-14-19" name="__codelineno-14-19" href="#__codelineno-14-19"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-14-20"><a id="__codelineno-14-20" name="__codelineno-14-20" href="#__codelineno-14-20"></a><span class="p">}</span>
</span></code></pre></div>
<p><strong>Usage:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-15-1"><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">template</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplate</span><span class="p">.</span><span class="nx">findUnique</span><span class="p">({</span>
</span><span id="__span-15-2"><a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;shift-reminder&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a><span class="w"> </span><span class="nx">include</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">variables</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-15-4"><a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a><span class="p">});</span>
</span><span id="__span-15-5"><a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a>
</span><span id="__span-15-6"><a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a><span class="k">try</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-15-7"><a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a><span class="w"> </span><span class="nx">validateVariables</span><span class="p">(</span><span class="nx">template</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-15-8"><a id="__codelineno-15-8" name="__codelineno-15-8" href="#__codelineno-15-8"></a><span class="w"> </span><span class="nx">USER_NAME</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;John Doe&#39;</span><span class="p">,</span>
</span><span id="__span-15-9"><a id="__codelineno-15-9" name="__codelineno-15-9" href="#__codelineno-15-9"></a><span class="w"> </span><span class="nx">SHIFT_DATE</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;2026-03-15&#39;</span><span class="p">,</span>
</span><span id="__span-15-10"><a id="__codelineno-15-10" name="__codelineno-15-10" href="#__codelineno-15-10"></a><span class="w"> </span><span class="c1">// Missing SHIFT_TITLE (required)</span>
</span><span id="__span-15-11"><a id="__codelineno-15-11" name="__codelineno-15-11" href="#__codelineno-15-11"></a><span class="w"> </span><span class="p">});</span>
</span><span id="__span-15-12"><a id="__codelineno-15-12" name="__codelineno-15-12" href="#__codelineno-15-12"></a><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-15-13"><a id="__codelineno-15-13" name="__codelineno-15-13" href="#__codelineno-15-13"></a><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Validation failed:&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
</span><span id="__span-15-14"><a id="__codelineno-15-14" name="__codelineno-15-14" href="#__codelineno-15-14"></a><span class="w"> </span><span class="c1">// Error: Missing required variables for template shift-reminder: SHIFT_TITLE</span>
</span><span id="__span-15-15"><a id="__codelineno-15-15" name="__codelineno-15-15" href="#__codelineno-15-15"></a><span class="p">}</span>
</span></code></pre></div></p>
<hr />
<h2 id="code-examples">Code Examples<a class="headerlink" href="#code-examples" title="Permanent link">&para;</a></h2>
<h3 id="creating-variable-via-api">Creating Variable via API<a class="headerlink" href="#creating-variable-via-api" title="Permanent link">&para;</a></h3>
<p><strong>Endpoint:</strong> <code>POST /api/email-templates/:id/variables</code></p>
<p><strong>Request Body:</strong>
<div class="language-json highlight"><pre><span></span><code><span id="__span-16-1"><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="p">{</span>
</span><span id="__span-16-2"><a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a><span class="w"> </span><span class="nt">&quot;key&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;USER_NAME&quot;</span><span class="p">,</span>
</span><span id="__span-16-3"><a id="__codelineno-16-3" name="__codelineno-16-3" href="#__codelineno-16-3"></a><span class="w"> </span><span class="nt">&quot;label&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;User Name&quot;</span><span class="p">,</span>
</span><span id="__span-16-4"><a id="__codelineno-16-4" name="__codelineno-16-4" href="#__codelineno-16-4"></a><span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Full name of the email recipient&quot;</span><span class="p">,</span>
</span><span id="__span-16-5"><a id="__codelineno-16-5" name="__codelineno-16-5" href="#__codelineno-16-5"></a><span class="w"> </span><span class="nt">&quot;isRequired&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
</span><span id="__span-16-6"><a id="__codelineno-16-6" name="__codelineno-16-6" href="#__codelineno-16-6"></a><span class="w"> </span><span class="nt">&quot;isConditional&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span>
</span><span id="__span-16-7"><a id="__codelineno-16-7" name="__codelineno-16-7" href="#__codelineno-16-7"></a><span class="w"> </span><span class="nt">&quot;sampleValue&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;John Doe&quot;</span><span class="p">,</span>
</span><span id="__span-16-8"><a id="__codelineno-16-8" name="__codelineno-16-8" href="#__codelineno-16-8"></a><span class="w"> </span><span class="nt">&quot;sortOrder&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span>
</span><span id="__span-16-9"><a id="__codelineno-16-9" name="__codelineno-16-9" href="#__codelineno-16-9"></a><span class="p">}</span>
</span></code></pre></div></p>
<p><strong>Route Implementation:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-17-1"><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a><span class="c1">// api/src/modules/email-templates/email-templates.routes.ts</span>
</span><span id="__span-17-2"><a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a>
</span><span id="__span-17-3"><a id="__codelineno-17-3" name="__codelineno-17-3" href="#__codelineno-17-3"></a><span class="nx">router</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">&#39;/:id/variables&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">requireRole</span><span class="p">(</span><span class="nx">SUPER_ADMIN</span><span class="p">),</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="p">(</span><span class="nx">req</span><span class="p">,</span><span class="w"> </span><span class="nx">res</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-17-4"><a id="__codelineno-17-4" name="__codelineno-17-4" href="#__codelineno-17-4"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">;</span>
</span><span id="__span-17-5"><a id="__codelineno-17-5" name="__codelineno-17-5" href="#__codelineno-17-5"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">label</span><span class="p">,</span><span class="w"> </span><span class="nx">description</span><span class="p">,</span><span class="w"> </span><span class="nx">isRequired</span><span class="p">,</span><span class="w"> </span><span class="nx">isConditional</span><span class="p">,</span><span class="w"> </span><span class="nx">sampleValue</span><span class="p">,</span><span class="w"> </span><span class="nx">sortOrder</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span>
</span><span id="__span-17-6"><a id="__codelineno-17-6" name="__codelineno-17-6" href="#__codelineno-17-6"></a>
</span><span id="__span-17-7"><a id="__codelineno-17-7" name="__codelineno-17-7" href="#__codelineno-17-7"></a><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-17-8"><a id="__codelineno-17-8" name="__codelineno-17-8" href="#__codelineno-17-8"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">variable</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplateVariable</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
</span><span id="__span-17-9"><a id="__codelineno-17-9" name="__codelineno-17-9" href="#__codelineno-17-9"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-17-10"><a id="__codelineno-17-10" name="__codelineno-17-10" href="#__codelineno-17-10"></a><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">id</span><span class="p">,</span>
</span><span id="__span-17-11"><a id="__codelineno-17-11" name="__codelineno-17-11" href="#__codelineno-17-11"></a><span class="w"> </span><span class="nx">key</span><span class="p">,</span>
</span><span id="__span-17-12"><a id="__codelineno-17-12" name="__codelineno-17-12" href="#__codelineno-17-12"></a><span class="w"> </span><span class="nx">label</span><span class="p">,</span>
</span><span id="__span-17-13"><a id="__codelineno-17-13" name="__codelineno-17-13" href="#__codelineno-17-13"></a><span class="w"> </span><span class="nx">description</span><span class="p">,</span>
</span><span id="__span-17-14"><a id="__codelineno-17-14" name="__codelineno-17-14" href="#__codelineno-17-14"></a><span class="w"> </span><span class="nx">isRequired</span><span class="o">:</span><span class="w"> </span><span class="kt">isRequired</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span>
</span><span id="__span-17-15"><a id="__codelineno-17-15" name="__codelineno-17-15" href="#__codelineno-17-15"></a><span class="w"> </span><span class="nx">isConditional</span><span class="o">:</span><span class="w"> </span><span class="kt">isConditional</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span>
</span><span id="__span-17-16"><a id="__codelineno-17-16" name="__codelineno-17-16" href="#__codelineno-17-16"></a><span class="w"> </span><span class="nx">sampleValue</span><span class="p">,</span>
</span><span id="__span-17-17"><a id="__codelineno-17-17" name="__codelineno-17-17" href="#__codelineno-17-17"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">sortOrder</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="mf">0</span><span class="p">,</span>
</span><span id="__span-17-18"><a id="__codelineno-17-18" name="__codelineno-17-18" href="#__codelineno-17-18"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-17-19"><a id="__codelineno-17-19" name="__codelineno-17-19" href="#__codelineno-17-19"></a><span class="w"> </span><span class="p">});</span>
</span><span id="__span-17-20"><a id="__codelineno-17-20" name="__codelineno-17-20" href="#__codelineno-17-20"></a>
</span><span id="__span-17-21"><a id="__codelineno-17-21" name="__codelineno-17-21" href="#__codelineno-17-21"></a><span class="w"> </span><span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">variable</span><span class="p">);</span>
</span><span id="__span-17-22"><a id="__codelineno-17-22" name="__codelineno-17-22" href="#__codelineno-17-22"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="o">:</span><span class="w"> </span><span class="kt">any</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-17-23"><a id="__codelineno-17-23" name="__codelineno-17-23" href="#__codelineno-17-23"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">code</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">&#39;P2002&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-17-24"><a id="__codelineno-17-24" name="__codelineno-17-24" href="#__codelineno-17-24"></a><span class="w"> </span><span class="c1">// Unique constraint violation</span>
</span><span id="__span-17-25"><a id="__codelineno-17-25" name="__codelineno-17-25" href="#__codelineno-17-25"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mf">400</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span><span class="w"> </span><span class="nx">error</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Variable key already exists for this template&#39;</span><span class="w"> </span><span class="p">});</span>
</span><span id="__span-17-26"><a id="__codelineno-17-26" name="__codelineno-17-26" href="#__codelineno-17-26"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-17-27"><a id="__codelineno-17-27" name="__codelineno-17-27" href="#__codelineno-17-27"></a><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="nx">error</span><span class="p">;</span>
</span><span id="__span-17-28"><a id="__codelineno-17-28" name="__codelineno-17-28" href="#__codelineno-17-28"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-17-29"><a id="__codelineno-17-29" name="__codelineno-17-29" href="#__codelineno-17-29"></a><span class="p">});</span>
</span></code></pre></div></p>
<hr />
<h3 id="auto-generating-sample-data">Auto-Generating Sample Data<a class="headerlink" href="#auto-generating-sample-data" title="Permanent link">&para;</a></h3>
<p><strong>Load Sample Data from Variables:</strong></p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-18-1"><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a><span class="kd">function</span><span class="w"> </span><span class="nx">generateSampleData</span><span class="p">(</span><span class="nx">variables</span><span class="o">:</span><span class="w"> </span><span class="kt">EmailTemplateVariable</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="nx">Record</span><span class="o">&lt;</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">unknown</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-18-2"><a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">sampleData</span><span class="o">:</span><span class="w"> </span><span class="kt">Record</span><span class="o">&lt;</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">unknown</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{};</span>
</span><span id="__span-18-3"><a id="__codelineno-18-3" name="__codelineno-18-3" href="#__codelineno-18-3"></a>
</span><span id="__span-18-4"><a id="__codelineno-18-4" name="__codelineno-18-4" href="#__codelineno-18-4"></a><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">const</span><span class="w"> </span><span class="nx">variable</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nx">variables</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-18-5"><a id="__codelineno-18-5" name="__codelineno-18-5" href="#__codelineno-18-5"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">variable</span><span class="p">.</span><span class="nx">sampleValue</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-18-6"><a id="__codelineno-18-6" name="__codelineno-18-6" href="#__codelineno-18-6"></a><span class="w"> </span><span class="c1">// Try to parse as JSON (for arrays/objects)</span>
</span><span id="__span-18-7"><a id="__codelineno-18-7" name="__codelineno-18-7" href="#__codelineno-18-7"></a><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-18-8"><a id="__codelineno-18-8" name="__codelineno-18-8" href="#__codelineno-18-8"></a><span class="w"> </span><span class="nx">sampleData</span><span class="p">[</span><span class="nx">variable</span><span class="p">.</span><span class="nx">key</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">variable</span><span class="p">.</span><span class="nx">sampleValue</span><span class="p">);</span>
</span><span id="__span-18-9"><a id="__codelineno-18-9" name="__codelineno-18-9" href="#__codelineno-18-9"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-18-10"><a id="__codelineno-18-10" name="__codelineno-18-10" href="#__codelineno-18-10"></a><span class="w"> </span><span class="c1">// Use as string</span>
</span><span id="__span-18-11"><a id="__codelineno-18-11" name="__codelineno-18-11" href="#__codelineno-18-11"></a><span class="w"> </span><span class="nx">sampleData</span><span class="p">[</span><span class="nx">variable</span><span class="p">.</span><span class="nx">key</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">variable</span><span class="p">.</span><span class="nx">sampleValue</span><span class="p">;</span>
</span><span id="__span-18-12"><a id="__codelineno-18-12" name="__codelineno-18-12" href="#__codelineno-18-12"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-18-13"><a id="__codelineno-18-13" name="__codelineno-18-13" href="#__codelineno-18-13"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">variable</span><span class="p">.</span><span class="nx">isConditional</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-18-14"><a id="__codelineno-18-14" name="__codelineno-18-14" href="#__codelineno-18-14"></a><span class="w"> </span><span class="c1">// Default conditional variables to true</span>
</span><span id="__span-18-15"><a id="__codelineno-18-15" name="__codelineno-18-15" href="#__codelineno-18-15"></a><span class="w"> </span><span class="nx">sampleData</span><span class="p">[</span><span class="nx">variable</span><span class="p">.</span><span class="nx">key</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
</span><span id="__span-18-16"><a id="__codelineno-18-16" name="__codelineno-18-16" href="#__codelineno-18-16"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-18-17"><a id="__codelineno-18-17" name="__codelineno-18-17" href="#__codelineno-18-17"></a><span class="w"> </span><span class="c1">// Default to empty string</span>
</span><span id="__span-18-18"><a id="__codelineno-18-18" name="__codelineno-18-18" href="#__codelineno-18-18"></a><span class="w"> </span><span class="nx">sampleData</span><span class="p">[</span><span class="nx">variable</span><span class="p">.</span><span class="nx">key</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">;</span>
</span><span id="__span-18-19"><a id="__codelineno-18-19" name="__codelineno-18-19" href="#__codelineno-18-19"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-18-20"><a id="__codelineno-18-20" name="__codelineno-18-20" href="#__codelineno-18-20"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-18-21"><a id="__codelineno-18-21" name="__codelineno-18-21" href="#__codelineno-18-21"></a>
</span><span id="__span-18-22"><a id="__codelineno-18-22" name="__codelineno-18-22" href="#__codelineno-18-22"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">sampleData</span><span class="p">;</span>
</span><span id="__span-18-23"><a id="__codelineno-18-23" name="__codelineno-18-23" href="#__codelineno-18-23"></a><span class="p">}</span>
</span></code></pre></div>
<p><strong>Usage in Editor:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-19-1"><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">template</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">api</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="sb">`/api/email-templates/</span><span class="si">${</span><span class="nx">id</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span><span id="__span-19-2"><a id="__codelineno-19-2" name="__codelineno-19-2" href="#__codelineno-19-2"></a><span class="kd">const</span><span class="w"> </span><span class="nx">sampleData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">generateSampleData</span><span class="p">(</span><span class="nx">template</span><span class="p">.</span><span class="nx">variables</span><span class="p">);</span>
</span><span id="__span-19-3"><a id="__codelineno-19-3" name="__codelineno-19-3" href="#__codelineno-19-3"></a>
</span><span id="__span-19-4"><a id="__codelineno-19-4" name="__codelineno-19-4" href="#__codelineno-19-4"></a><span class="nx">setSampleData</span><span class="p">(</span><span class="nx">sampleData</span><span class="p">);</span>
</span></code></pre></div></p>
<hr />
<h3 id="variable-usage-detection">Variable Usage Detection<a class="headerlink" href="#variable-usage-detection" title="Permanent link">&para;</a></h3>
<p><strong>Find Variables Used in Template Content:</strong></p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-20-1"><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></a><span class="kd">function</span><span class="w"> </span><span class="nx">findUsedVariables</span><span class="p">(</span><span class="nx">content</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">[]</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-2"><a id="__codelineno-20-2" name="__codelineno-20-2" href="#__codelineno-20-2"></a><span class="w"> </span><span class="c1">// Regex: matches {{VAR}} but not {{#if}}, {{/if}}, {{#each}}, etc.</span>
</span><span id="__span-20-3"><a id="__codelineno-20-3" name="__codelineno-20-3" href="#__codelineno-20-3"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">regex</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sr">/\{\{(?!#|\/|\^)([A-Z_]+)\}\}/g</span><span class="p">;</span>
</span><span id="__span-20-4"><a id="__codelineno-20-4" name="__codelineno-20-4" href="#__codelineno-20-4"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">matches</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">content</span><span class="p">.</span><span class="nx">matchAll</span><span class="p">(</span><span class="nx">regex</span><span class="p">);</span>
</span><span id="__span-20-5"><a id="__codelineno-20-5" name="__codelineno-20-5" href="#__codelineno-20-5"></a>
</span><span id="__span-20-6"><a id="__codelineno-20-6" name="__codelineno-20-6" href="#__codelineno-20-6"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">variables</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nb">Set</span><span class="o">&lt;</span><span class="kt">string</span><span class="o">&gt;</span><span class="p">();</span>
</span><span id="__span-20-7"><a id="__codelineno-20-7" name="__codelineno-20-7" href="#__codelineno-20-7"></a><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">const</span><span class="w"> </span><span class="nx">match</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nx">matches</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-8"><a id="__codelineno-20-8" name="__codelineno-20-8" href="#__codelineno-20-8"></a><span class="w"> </span><span class="nx">variables</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">match</span><span class="p">[</span><span class="mf">1</span><span class="p">]);</span>
</span><span id="__span-20-9"><a id="__codelineno-20-9" name="__codelineno-20-9" href="#__codelineno-20-9"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-20-10"><a id="__codelineno-20-10" name="__codelineno-20-10" href="#__codelineno-20-10"></a>
</span><span id="__span-20-11"><a id="__codelineno-20-11" name="__codelineno-20-11" href="#__codelineno-20-11"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Array</span><span class="p">.</span><span class="kr">from</span><span class="p">(</span><span class="nx">variables</span><span class="p">);</span>
</span><span id="__span-20-12"><a id="__codelineno-20-12" name="__codelineno-20-12" href="#__codelineno-20-12"></a><span class="p">}</span>
</span></code></pre></div>
<p><strong>Check for Unused Variables:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-21-1"><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">template</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplate</span><span class="p">.</span><span class="nx">findUnique</span><span class="p">({</span>
</span><span id="__span-21-2"><a id="__codelineno-21-2" name="__codelineno-21-2" href="#__codelineno-21-2"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="w"> </span><span class="kt">templateId</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-21-3"><a id="__codelineno-21-3" name="__codelineno-21-3" href="#__codelineno-21-3"></a><span class="w"> </span><span class="nx">include</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">variables</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-21-4"><a id="__codelineno-21-4" name="__codelineno-21-4" href="#__codelineno-21-4"></a><span class="p">});</span>
</span><span id="__span-21-5"><a id="__codelineno-21-5" name="__codelineno-21-5" href="#__codelineno-21-5"></a>
</span><span id="__span-21-6"><a id="__codelineno-21-6" name="__codelineno-21-6" href="#__codelineno-21-6"></a><span class="kd">const</span><span class="w"> </span><span class="nx">htmlVars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">findUsedVariables</span><span class="p">(</span><span class="nx">template</span><span class="p">.</span><span class="nx">htmlContent</span><span class="p">);</span>
</span><span id="__span-21-7"><a id="__codelineno-21-7" name="__codelineno-21-7" href="#__codelineno-21-7"></a><span class="kd">const</span><span class="w"> </span><span class="nx">textVars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">findUsedVariables</span><span class="p">(</span><span class="nx">template</span><span class="p">.</span><span class="nx">textContent</span><span class="p">);</span>
</span><span id="__span-21-8"><a id="__codelineno-21-8" name="__codelineno-21-8" href="#__codelineno-21-8"></a><span class="kd">const</span><span class="w"> </span><span class="nx">subjectVars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">findUsedVariables</span><span class="p">(</span><span class="nx">template</span><span class="p">.</span><span class="nx">subjectLine</span><span class="p">);</span>
</span><span id="__span-21-9"><a id="__codelineno-21-9" name="__codelineno-21-9" href="#__codelineno-21-9"></a>
</span><span id="__span-21-10"><a id="__codelineno-21-10" name="__codelineno-21-10" href="#__codelineno-21-10"></a><span class="kd">const</span><span class="w"> </span><span class="nx">usedVars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nb">Set</span><span class="p">([...</span><span class="nx">htmlVars</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">textVars</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">subjectVars</span><span class="p">]);</span>
</span><span id="__span-21-11"><a id="__codelineno-21-11" name="__codelineno-21-11" href="#__codelineno-21-11"></a>
</span><span id="__span-21-12"><a id="__codelineno-21-12" name="__codelineno-21-12" href="#__codelineno-21-12"></a><span class="kd">const</span><span class="w"> </span><span class="nx">unusedVars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">template</span><span class="p">.</span><span class="nx">variables</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="o">!</span><span class="nx">usedVars</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">key</span><span class="p">));</span>
</span><span id="__span-21-13"><a id="__codelineno-21-13" name="__codelineno-21-13" href="#__codelineno-21-13"></a>
</span><span id="__span-21-14"><a id="__codelineno-21-14" name="__codelineno-21-14" href="#__codelineno-21-14"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Unused variables:&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">unusedVars</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">key</span><span class="p">));</span>
</span></code></pre></div></p>
<hr />
<h2 id="common-variables-by-category">Common Variables by Category<a class="headerlink" href="#common-variables-by-category" title="Permanent link">&para;</a></h2>
<h3 id="influence-templates">INFLUENCE Templates<a class="headerlink" href="#influence-templates" title="Permanent link">&para;</a></h3>
<p><strong>Standard Variables:</strong></p>
<table>
<thead>
<tr>
<th>Key</th>
<th>Label</th>
<th>Required</th>
<th>Conditional</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>USER_NAME</code></td>
<td>User Name</td>
<td>Yes</td>
<td>No</td>
<td>Participant's full name</td>
</tr>
<tr>
<td><code>USER_EMAIL</code></td>
<td>User Email</td>
<td>Yes</td>
<td>No</td>
<td>Participant's email address</td>
</tr>
<tr>
<td><code>CAMPAIGN_TITLE</code></td>
<td>Campaign Title</td>
<td>Yes</td>
<td>No</td>
<td>Campaign name</td>
</tr>
<tr>
<td><code>CAMPAIGN_SLUG</code></td>
<td>Campaign Slug</td>
<td>Yes</td>
<td>No</td>
<td>URL-safe campaign identifier</td>
</tr>
<tr>
<td><code>CAMPAIGN_URL</code></td>
<td>Campaign URL</td>
<td>No</td>
<td>No</td>
<td>Full URL to campaign page</td>
</tr>
<tr>
<td><code>REPRESENTATIVE_NAME</code></td>
<td>Representative Name</td>
<td>Yes</td>
<td>No</td>
<td>Representative's full name</td>
</tr>
<tr>
<td><code>REPRESENTATIVE_TITLE</code></td>
<td>Representative Title</td>
<td>Yes</td>
<td>No</td>
<td>Representative's title (e.g., "MP for Downtown")</td>
</tr>
<tr>
<td><code>REPRESENTATIVE_EMAIL</code></td>
<td>Representative Email</td>
<td>Yes</td>
<td>No</td>
<td>Representative's email address</td>
</tr>
<tr>
<td><code>CUSTOM_MESSAGE</code></td>
<td>Custom Message</td>
<td>Yes</td>
<td>No</td>
<td>Participant's custom message to representative</td>
</tr>
<tr>
<td><code>RESPONSE_TEXT</code></td>
<td>Response Text</td>
<td>No</td>
<td>No</td>
<td>Participant's response wall submission</td>
</tr>
<tr>
<td><code>VERIFICATION_LINK</code></td>
<td>Verification Link</td>
<td>No</td>
<td>No</td>
<td>Unique verification URL</td>
</tr>
<tr>
<td><code>HAS_CUSTOM_MESSAGE</code></td>
<td>Has Custom Message</td>
<td>No</td>
<td>Yes</td>
<td>Whether participant added custom message</td>
</tr>
</tbody>
</table>
<p><strong>Usage Example:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-22-1"><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">emailService</span><span class="p">.</span><span class="nx">sendFromTemplate</span><span class="p">(</span><span class="s1">&#39;campaign-email&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-22-2"><a id="__codelineno-22-2" name="__codelineno-22-2" href="#__codelineno-22-2"></a><span class="w"> </span><span class="nx">recipientEmail</span><span class="o">:</span><span class="w"> </span><span class="kt">representative.email</span><span class="p">,</span>
</span><span id="__span-22-3"><a id="__codelineno-22-3" name="__codelineno-22-3" href="#__codelineno-22-3"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-22-4"><a id="__codelineno-22-4" name="__codelineno-22-4" href="#__codelineno-22-4"></a><span class="w"> </span><span class="nx">USER_NAME</span><span class="o">:</span><span class="w"> </span><span class="kt">participant.name</span><span class="p">,</span>
</span><span id="__span-22-5"><a id="__codelineno-22-5" name="__codelineno-22-5" href="#__codelineno-22-5"></a><span class="w"> </span><span class="nx">USER_EMAIL</span><span class="o">:</span><span class="w"> </span><span class="kt">participant.email</span><span class="p">,</span>
</span><span id="__span-22-6"><a id="__codelineno-22-6" name="__codelineno-22-6" href="#__codelineno-22-6"></a><span class="w"> </span><span class="nx">CAMPAIGN_TITLE</span><span class="o">:</span><span class="w"> </span><span class="kt">campaign.title</span><span class="p">,</span>
</span><span id="__span-22-7"><a id="__codelineno-22-7" name="__codelineno-22-7" href="#__codelineno-22-7"></a><span class="w"> </span><span class="nx">CAMPAIGN_SLUG</span><span class="o">:</span><span class="w"> </span><span class="kt">campaign.slug</span><span class="p">,</span>
</span><span id="__span-22-8"><a id="__codelineno-22-8" name="__codelineno-22-8" href="#__codelineno-22-8"></a><span class="w"> </span><span class="nx">REPRESENTATIVE_NAME</span><span class="o">:</span><span class="w"> </span><span class="kt">representative.name</span><span class="p">,</span>
</span><span id="__span-22-9"><a id="__codelineno-22-9" name="__codelineno-22-9" href="#__codelineno-22-9"></a><span class="w"> </span><span class="nx">REPRESENTATIVE_TITLE</span><span class="o">:</span><span class="w"> </span><span class="kt">representative.title</span><span class="p">,</span>
</span><span id="__span-22-10"><a id="__codelineno-22-10" name="__codelineno-22-10" href="#__codelineno-22-10"></a><span class="w"> </span><span class="nx">REPRESENTATIVE_EMAIL</span><span class="o">:</span><span class="w"> </span><span class="kt">representative.email</span><span class="p">,</span>
</span><span id="__span-22-11"><a id="__codelineno-22-11" name="__codelineno-22-11" href="#__codelineno-22-11"></a><span class="w"> </span><span class="nx">CUSTOM_MESSAGE</span><span class="o">:</span><span class="w"> </span><span class="kt">emailData.customMessage</span><span class="p">,</span>
</span><span id="__span-22-12"><a id="__codelineno-22-12" name="__codelineno-22-12" href="#__codelineno-22-12"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-22-13"><a id="__codelineno-22-13" name="__codelineno-22-13" href="#__codelineno-22-13"></a><span class="p">});</span>
</span></code></pre></div></p>
<hr />
<h3 id="map-templates">MAP Templates<a class="headerlink" href="#map-templates" title="Permanent link">&para;</a></h3>
<p><strong>Standard Variables:</strong></p>
<table>
<thead>
<tr>
<th>Key</th>
<th>Label</th>
<th>Required</th>
<th>Conditional</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>USER_NAME</code></td>
<td>User Name</td>
<td>Yes</td>
<td>No</td>
<td>Volunteer's full name</td>
</tr>
<tr>
<td><code>USER_EMAIL</code></td>
<td>User Email</td>
<td>Yes</td>
<td>No</td>
<td>Volunteer's email address</td>
</tr>
<tr>
<td><code>USER_PHONE</code></td>
<td>User Phone</td>
<td>No</td>
<td>No</td>
<td>Volunteer's phone number (optional)</td>
</tr>
<tr>
<td><code>HAS_PHONE</code></td>
<td>Has Phone</td>
<td>No</td>
<td>Yes</td>
<td>Whether user provided phone number</td>
</tr>
<tr>
<td><code>SHIFT_TITLE</code></td>
<td>Shift Title</td>
<td>Yes</td>
<td>No</td>
<td>Shift name</td>
</tr>
<tr>
<td><code>SHIFT_DATE</code></td>
<td>Shift Date</td>
<td>Yes</td>
<td>No</td>
<td>Formatted shift date</td>
</tr>
<tr>
<td><code>SHIFT_TIME</code></td>
<td>Shift Time</td>
<td>Yes</td>
<td>No</td>
<td>Shift time range (e.g., "10:00 AM - 2:00 PM")</td>
</tr>
<tr>
<td><code>SHIFT_LOCATION</code></td>
<td>Shift Location</td>
<td>Yes</td>
<td>No</td>
<td>Meeting location for shift</td>
</tr>
<tr>
<td><code>CUT_NAME</code></td>
<td>Cut Name</td>
<td>No</td>
<td>No</td>
<td>Canvass area name</td>
</tr>
<tr>
<td><code>IS_CUT_ASSIGNED</code></td>
<td>Is Cut Assigned</td>
<td>No</td>
<td>Yes</td>
<td>Whether volunteer is assigned to a cut</td>
</tr>
<tr>
<td><code>VISIT_COUNT</code></td>
<td>Visit Count</td>
<td>No</td>
<td>No</td>
<td>Number of doors knocked (session summary)</td>
</tr>
<tr>
<td><code>CONTACT_COUNT</code></td>
<td>Contact Count</td>
<td>No</td>
<td>No</td>
<td>Number of successful contacts</td>
</tr>
<tr>
<td><code>SUPPORT_COUNT</code></td>
<td>Support Count</td>
<td>No</td>
<td>No</td>
<td>Number of supporters identified</td>
</tr>
</tbody>
</table>
<p><strong>Usage Example:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-23-1"><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">emailService</span><span class="p">.</span><span class="nx">sendFromTemplate</span><span class="p">(</span><span class="s1">&#39;shift-signup-confirmation&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-23-2"><a id="__codelineno-23-2" name="__codelineno-23-2" href="#__codelineno-23-2"></a><span class="w"> </span><span class="nx">recipientEmail</span><span class="o">:</span><span class="w"> </span><span class="kt">volunteer.email</span><span class="p">,</span>
</span><span id="__span-23-3"><a id="__codelineno-23-3" name="__codelineno-23-3" href="#__codelineno-23-3"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-23-4"><a id="__codelineno-23-4" name="__codelineno-23-4" href="#__codelineno-23-4"></a><span class="w"> </span><span class="nx">USER_NAME</span><span class="o">:</span><span class="w"> </span><span class="kt">volunteer.name</span><span class="p">,</span>
</span><span id="__span-23-5"><a id="__codelineno-23-5" name="__codelineno-23-5" href="#__codelineno-23-5"></a><span class="w"> </span><span class="nx">USER_EMAIL</span><span class="o">:</span><span class="w"> </span><span class="kt">volunteer.email</span><span class="p">,</span>
</span><span id="__span-23-6"><a id="__codelineno-23-6" name="__codelineno-23-6" href="#__codelineno-23-6"></a><span class="w"> </span><span class="nx">USER_PHONE</span><span class="o">:</span><span class="w"> </span><span class="kt">volunteer.phone</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">,</span>
</span><span id="__span-23-7"><a id="__codelineno-23-7" name="__codelineno-23-7" href="#__codelineno-23-7"></a><span class="w"> </span><span class="nx">HAS_PHONE</span><span class="o">:</span><span class="w"> </span><span class="o">!!</span><span class="nx">volunteer</span><span class="p">.</span><span class="nx">phone</span><span class="p">,</span>
</span><span id="__span-23-8"><a id="__codelineno-23-8" name="__codelineno-23-8" href="#__codelineno-23-8"></a><span class="w"> </span><span class="nx">SHIFT_TITLE</span><span class="o">:</span><span class="w"> </span><span class="kt">shift.title</span><span class="p">,</span>
</span><span id="__span-23-9"><a id="__codelineno-23-9" name="__codelineno-23-9" href="#__codelineno-23-9"></a><span class="w"> </span><span class="nx">SHIFT_DATE</span><span class="o">:</span><span class="w"> </span><span class="kt">dayjs</span><span class="p">(</span><span class="nx">shift</span><span class="p">.</span><span class="nx">startTime</span><span class="p">).</span><span class="nx">format</span><span class="p">(</span><span class="s1">&#39;dddd, MMMM D, YYYY&#39;</span><span class="p">),</span>
</span><span id="__span-23-10"><a id="__codelineno-23-10" name="__codelineno-23-10" href="#__codelineno-23-10"></a><span class="w"> </span><span class="nx">SHIFT_TIME</span><span class="o">:</span><span class="w"> </span><span class="sb">`</span><span class="si">${</span><span class="nx">dayjs</span><span class="p">(</span><span class="nx">shift</span><span class="p">.</span><span class="nx">startTime</span><span class="p">).</span><span class="nx">format</span><span class="p">(</span><span class="s1">&#39;h:mm A&#39;</span><span class="p">)</span><span class="si">}</span><span class="sb"> - </span><span class="si">${</span><span class="nx">dayjs</span><span class="p">(</span><span class="nx">shift</span><span class="p">.</span><span class="nx">endTime</span><span class="p">).</span><span class="nx">format</span><span class="p">(</span><span class="s1">&#39;h:mm A&#39;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
</span><span id="__span-23-11"><a id="__codelineno-23-11" name="__codelineno-23-11" href="#__codelineno-23-11"></a><span class="w"> </span><span class="nx">SHIFT_LOCATION</span><span class="o">:</span><span class="w"> </span><span class="kt">shift.location</span><span class="p">,</span>
</span><span id="__span-23-12"><a id="__codelineno-23-12" name="__codelineno-23-12" href="#__codelineno-23-12"></a><span class="w"> </span><span class="nx">IS_CUT_ASSIGNED</span><span class="o">:</span><span class="w"> </span><span class="o">!!</span><span class="nx">shift</span><span class="p">.</span><span class="nx">cutId</span><span class="p">,</span>
</span><span id="__span-23-13"><a id="__codelineno-23-13" name="__codelineno-23-13" href="#__codelineno-23-13"></a><span class="w"> </span><span class="nx">CUT_NAME</span><span class="o">:</span><span class="w"> </span><span class="kt">shift.cut?.name</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">,</span>
</span><span id="__span-23-14"><a id="__codelineno-23-14" name="__codelineno-23-14" href="#__codelineno-23-14"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-23-15"><a id="__codelineno-23-15" name="__codelineno-23-15" href="#__codelineno-23-15"></a><span class="p">});</span>
</span></code></pre></div></p>
<hr />
<h3 id="system-templates">SYSTEM Templates<a class="headerlink" href="#system-templates" title="Permanent link">&para;</a></h3>
<p><strong>Standard Variables:</strong></p>
<table>
<thead>
<tr>
<th>Key</th>
<th>Label</th>
<th>Required</th>
<th>Conditional</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>USER_NAME</code></td>
<td>User Name</td>
<td>Yes</td>
<td>No</td>
<td>User's full name</td>
</tr>
<tr>
<td><code>USER_EMAIL</code></td>
<td>User Email</td>
<td>Yes</td>
<td>No</td>
<td>User's email address</td>
</tr>
<tr>
<td><code>VERIFICATION_LINK</code></td>
<td>Verification Link</td>
<td>No</td>
<td>No</td>
<td>Unique verification URL (expires 24h)</td>
</tr>
<tr>
<td><code>RESET_LINK</code></td>
<td>Reset Link</td>
<td>No</td>
<td>No</td>
<td>Unique password reset URL (expires 1h)</td>
</tr>
<tr>
<td><code>SUPPORT_EMAIL</code></td>
<td>Support Email</td>
<td>Yes</td>
<td>No</td>
<td>Platform support email address</td>
</tr>
<tr>
<td><code>SITE_NAME</code></td>
<td>Site Name</td>
<td>Yes</td>
<td>No</td>
<td>Platform name (from SiteSettings)</td>
</tr>
<tr>
<td><code>SITE_URL</code></td>
<td>Site URL</td>
<td>Yes</td>
<td>No</td>
<td>Platform base URL</td>
</tr>
<tr>
<td><code>LOGIN_URL</code></td>
<td>Login URL</td>
<td>No</td>
<td>No</td>
<td>Direct link to login page</td>
</tr>
<tr>
<td><code>LOCKOUT_REASON</code></td>
<td>Lockout Reason</td>
<td>No</td>
<td>No</td>
<td>Why account was locked (security)</td>
</tr>
</tbody>
</table>
<p><strong>Usage Example:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-24-1"><a id="__codelineno-24-1" name="__codelineno-24-1" href="#__codelineno-24-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">emailService</span><span class="p">.</span><span class="nx">sendFromTemplate</span><span class="p">(</span><span class="s1">&#39;password-reset&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-24-2"><a id="__codelineno-24-2" name="__codelineno-24-2" href="#__codelineno-24-2"></a><span class="w"> </span><span class="nx">recipientEmail</span><span class="o">:</span><span class="w"> </span><span class="kt">user.email</span><span class="p">,</span>
</span><span id="__span-24-3"><a id="__codelineno-24-3" name="__codelineno-24-3" href="#__codelineno-24-3"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-24-4"><a id="__codelineno-24-4" name="__codelineno-24-4" href="#__codelineno-24-4"></a><span class="w"> </span><span class="nx">USER_NAME</span><span class="o">:</span><span class="w"> </span><span class="kt">user.name</span><span class="p">,</span>
</span><span id="__span-24-5"><a id="__codelineno-24-5" name="__codelineno-24-5" href="#__codelineno-24-5"></a><span class="w"> </span><span class="nx">USER_EMAIL</span><span class="o">:</span><span class="w"> </span><span class="kt">user.email</span><span class="p">,</span>
</span><span id="__span-24-6"><a id="__codelineno-24-6" name="__codelineno-24-6" href="#__codelineno-24-6"></a><span class="w"> </span><span class="nx">RESET_LINK</span><span class="o">:</span><span class="w"> </span><span class="sb">`https://cmlite.org/reset-password/</span><span class="si">${</span><span class="nx">token</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
</span><span id="__span-24-7"><a id="__codelineno-24-7" name="__codelineno-24-7" href="#__codelineno-24-7"></a><span class="w"> </span><span class="nx">SUPPORT_EMAIL</span><span class="o">:</span><span class="w"> </span><span class="kt">siteSettings.supportEmail</span><span class="p">,</span>
</span><span id="__span-24-8"><a id="__codelineno-24-8" name="__codelineno-24-8" href="#__codelineno-24-8"></a><span class="w"> </span><span class="nx">SITE_NAME</span><span class="o">:</span><span class="w"> </span><span class="kt">siteSettings.siteName</span><span class="p">,</span>
</span><span id="__span-24-9"><a id="__codelineno-24-9" name="__codelineno-24-9" href="#__codelineno-24-9"></a><span class="w"> </span><span class="nx">SITE_URL</span><span class="o">:</span><span class="w"> </span><span class="kt">siteSettings.siteUrl</span><span class="p">,</span>
</span><span id="__span-24-10"><a id="__codelineno-24-10" name="__codelineno-24-10" href="#__codelineno-24-10"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-24-11"><a id="__codelineno-24-11" name="__codelineno-24-11" href="#__codelineno-24-11"></a><span class="p">});</span>
</span></code></pre></div></p>
<hr />
<h2 id="troubleshooting">Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permanent link">&para;</a></h2>
<h3 id="problem-variable-not-appearing-in-editor">Problem: Variable not appearing in editor<a class="headerlink" href="#problem-variable-not-appearing-in-editor" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms:</strong>
- Variable exists in database but not shown in editor insertion panel
- Variable missing from variables list</p>
<p><strong>Causes:</strong>
1. Variable belongs to different template
2. Template not refreshed after adding variable
3. Sort order is null or very high (out of view)</p>
<p><strong>Solutions:</strong></p>
<p><strong>Check variable exists:</strong>
<div class="language-sql highlight"><pre><span></span><code><span id="__span-25-1"><a id="__codelineno-25-1" name="__codelineno-25-1" href="#__codelineno-25-1"></a><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-25-2"><a id="__codelineno-25-2" name="__codelineno-25-2" href="#__codelineno-25-2"></a><span class="k">WHERE</span><span class="w"> </span><span class="n">template_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;cuid123&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;USER_NAME&#39;</span><span class="p">;</span>
</span></code></pre></div></p>
<p><strong>Verify template ID:</strong>
<div class="language-sql highlight"><pre><span></span><code><span id="__span-26-1"><a id="__codelineno-26-1" name="__codelineno-26-1" href="#__codelineno-26-1"></a><span class="k">SELECT</span><span class="w"> </span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">email_templates</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;shift-reminder&#39;</span><span class="p">;</span>
</span><span id="__span-26-2"><a id="__codelineno-26-2" name="__codelineno-26-2" href="#__codelineno-26-2"></a><span class="c1">-- Check ID matches variable.template_id</span>
</span></code></pre></div></p>
<p><strong>Refresh editor page:</strong>
- Hard refresh (Ctrl+Shift+R)
- Clear browser cache</p>
<p><strong>Check sort order:</strong>
<div class="language-sql highlight"><pre><span></span><code><span id="__span-27-1"><a id="__codelineno-27-1" name="__codelineno-27-1" href="#__codelineno-27-1"></a><span class="k">SELECT</span><span class="w"> </span><span class="k">key</span><span class="p">,</span><span class="w"> </span><span class="n">sort_order</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-27-2"><a id="__codelineno-27-2" name="__codelineno-27-2" href="#__codelineno-27-2"></a><span class="k">WHERE</span><span class="w"> </span><span class="n">template_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;cuid123&#39;</span>
</span><span id="__span-27-3"><a id="__codelineno-27-3" name="__codelineno-27-3" href="#__codelineno-27-3"></a><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">sort_order</span><span class="p">;</span>
</span><span id="__span-27-4"><a id="__codelineno-27-4" name="__codelineno-27-4" href="#__codelineno-27-4"></a>
</span><span id="__span-27-5"><a id="__codelineno-27-5" name="__codelineno-27-5" href="#__codelineno-27-5"></a><span class="c1">-- Update if needed</span>
</span><span id="__span-27-6"><a id="__codelineno-27-6" name="__codelineno-27-6" href="#__codelineno-27-6"></a><span class="k">UPDATE</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-27-7"><a id="__codelineno-27-7" name="__codelineno-27-7" href="#__codelineno-27-7"></a><span class="k">SET</span><span class="w"> </span><span class="n">sort_order</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span>
</span><span id="__span-27-8"><a id="__codelineno-27-8" name="__codelineno-27-8" href="#__codelineno-27-8"></a><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;variable-id&#39;</span><span class="p">;</span>
</span></code></pre></div></p>
<hr />
<h3 id="problem-validation-error-for-optional-variable">Problem: Validation error for optional variable<a class="headerlink" href="#problem-validation-error-for-optional-variable" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms:</strong>
- <code>MissingRequiredVariableError</code> thrown for variable marked as optional
- Email send fails unexpectedly</p>
<p><strong>Causes:</strong>
1. Variable incorrectly marked as required in database
2. Validation logic bug
3. Template uses variable in required context</p>
<p><strong>Solutions:</strong></p>
<p><strong>Check isRequired flag:</strong>
<div class="language-sql highlight"><pre><span></span><code><span id="__span-28-1"><a id="__codelineno-28-1" name="__codelineno-28-1" href="#__codelineno-28-1"></a><span class="k">SELECT</span><span class="w"> </span><span class="k">key</span><span class="p">,</span><span class="w"> </span><span class="n">is_required</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-28-2"><a id="__codelineno-28-2" name="__codelineno-28-2" href="#__codelineno-28-2"></a><span class="k">WHERE</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;USER_PHONE&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">template_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;cuid123&#39;</span><span class="p">;</span>
</span></code></pre></div></p>
<p><strong>Update to optional:</strong>
<div class="language-sql highlight"><pre><span></span><code><span id="__span-29-1"><a id="__codelineno-29-1" name="__codelineno-29-1" href="#__codelineno-29-1"></a><span class="k">UPDATE</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-29-2"><a id="__codelineno-29-2" name="__codelineno-29-2" href="#__codelineno-29-2"></a><span class="k">SET</span><span class="w"> </span><span class="n">is_required</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">false</span>
</span><span id="__span-29-3"><a id="__codelineno-29-3" name="__codelineno-29-3" href="#__codelineno-29-3"></a><span class="k">WHERE</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;USER_PHONE&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">template_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;cuid123&#39;</span><span class="p">;</span>
</span></code></pre></div></p>
<p><strong>Provide variable anyway:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-30-1"><a id="__codelineno-30-1" name="__codelineno-30-1" href="#__codelineno-30-1"></a><span class="c1">// Temporary fix: always provide optional variables</span>
</span><span id="__span-30-2"><a id="__codelineno-30-2" name="__codelineno-30-2" href="#__codelineno-30-2"></a><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-30-3"><a id="__codelineno-30-3" name="__codelineno-30-3" href="#__codelineno-30-3"></a><span class="w"> </span><span class="nx">USER_PHONE</span><span class="o">:</span><span class="w"> </span><span class="kt">volunteer.phone</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// Empty string if missing</span>
</span><span id="__span-30-4"><a id="__codelineno-30-4" name="__codelineno-30-4" href="#__codelineno-30-4"></a><span class="p">}</span>
</span></code></pre></div></p>
<p><strong>Check validation logic:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-31-1"><a id="__codelineno-31-1" name="__codelineno-31-1" href="#__codelineno-31-1"></a><span class="c1">// Ensure validation checks for undefined AND null</span>
</span><span id="__span-31-2"><a id="__codelineno-31-2" name="__codelineno-31-2" href="#__codelineno-31-2"></a><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">variable</span><span class="p">.</span><span class="nx">isRequired</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="p">(</span><span class="nx">data</span><span class="p">[</span><span class="nx">variable</span><span class="p">.</span><span class="nx">key</span><span class="p">]</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">data</span><span class="p">[</span><span class="nx">variable</span><span class="p">.</span><span class="nx">key</span><span class="p">]</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="kc">null</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-31-3"><a id="__codelineno-31-3" name="__codelineno-31-3" href="#__codelineno-31-3"></a><span class="w"> </span><span class="nx">missing</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">variable</span><span class="p">.</span><span class="nx">key</span><span class="p">);</span>
</span><span id="__span-31-4"><a id="__codelineno-31-4" name="__codelineno-31-4" href="#__codelineno-31-4"></a><span class="p">}</span>
</span></code></pre></div></p>
<hr />
<h3 id="problem-sample-value-not-used-in-preview">Problem: Sample value not used in preview<a class="headerlink" href="#problem-sample-value-not-used-in-preview" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms:</strong>
- Preview shows empty values instead of sample values
- Test send form doesn't pre-fill</p>
<p><strong>Causes:</strong>
1. Sample value is null in database
2. Sample data initialization bug
3. Variable added after editor loaded</p>
<p><strong>Solutions:</strong></p>
<p><strong>Check sample value exists:</strong>
<div class="language-sql highlight"><pre><span></span><code><span id="__span-32-1"><a id="__codelineno-32-1" name="__codelineno-32-1" href="#__codelineno-32-1"></a><span class="k">SELECT</span><span class="w"> </span><span class="k">key</span><span class="p">,</span><span class="w"> </span><span class="n">sample_value</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-32-2"><a id="__codelineno-32-2" name="__codelineno-32-2" href="#__codelineno-32-2"></a><span class="k">WHERE</span><span class="w"> </span><span class="n">template_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;cuid123&#39;</span><span class="p">;</span>
</span></code></pre></div></p>
<p><strong>Update sample value:</strong>
<div class="language-sql highlight"><pre><span></span><code><span id="__span-33-1"><a id="__codelineno-33-1" name="__codelineno-33-1" href="#__codelineno-33-1"></a><span class="k">UPDATE</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-33-2"><a id="__codelineno-33-2" name="__codelineno-33-2" href="#__codelineno-33-2"></a><span class="k">SET</span><span class="w"> </span><span class="n">sample_value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;John Doe&#39;</span>
</span><span id="__span-33-3"><a id="__codelineno-33-3" name="__codelineno-33-3" href="#__codelineno-33-3"></a><span class="k">WHERE</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;USER_NAME&#39;</span><span class="p">;</span>
</span></code></pre></div></p>
<p><strong>Refresh editor:</strong>
- Close and reopen EmailTemplateEditorPage
- Sample data reloads from variables</p>
<p><strong>Manual preview data:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-34-1"><a id="__codelineno-34-1" name="__codelineno-34-1" href="#__codelineno-34-1"></a><span class="c1">// Editor UI allows manual editing of sample data</span>
</span><span id="__span-34-2"><a id="__codelineno-34-2" name="__codelineno-34-2" href="#__codelineno-34-2"></a><span class="nx">setSampleData</span><span class="p">({</span>
</span><span id="__span-34-3"><a id="__codelineno-34-3" name="__codelineno-34-3" href="#__codelineno-34-3"></a><span class="w"> </span><span class="p">...</span><span class="nx">sampleData</span><span class="p">,</span>
</span><span id="__span-34-4"><a id="__codelineno-34-4" name="__codelineno-34-4" href="#__codelineno-34-4"></a><span class="w"> </span><span class="nx">USER_NAME</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Test Name&#39;</span><span class="p">,</span>
</span><span id="__span-34-5"><a id="__codelineno-34-5" name="__codelineno-34-5" href="#__codelineno-34-5"></a><span class="p">});</span>
</span></code></pre></div></p>
<hr />
<h3 id="problem-duplicate-variable-key-error">Problem: Duplicate variable key error<a class="headerlink" href="#problem-duplicate-variable-key-error" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms:</strong>
- <code>P2002: Unique constraint failed</code> error when creating variable
- Cannot add variable with same key</p>
<p><strong>Causes:</strong>
1. Variable already exists for this template
2. Attempting to create duplicate</p>
<p><strong>Solutions:</strong></p>
<p><strong>Check existing variables:</strong>
<div class="language-sql highlight"><pre><span></span><code><span id="__span-35-1"><a id="__codelineno-35-1" name="__codelineno-35-1" href="#__codelineno-35-1"></a><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-35-2"><a id="__codelineno-35-2" name="__codelineno-35-2" href="#__codelineno-35-2"></a><span class="k">WHERE</span><span class="w"> </span><span class="n">template_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;cuid123&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;USER_NAME&#39;</span><span class="p">;</span>
</span></code></pre></div></p>
<p><strong>Update existing instead:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-36-1"><a id="__codelineno-36-1" name="__codelineno-36-1" href="#__codelineno-36-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplateVariable</span><span class="p">.</span><span class="nx">upsert</span><span class="p">({</span>
</span><span id="__span-36-2"><a id="__codelineno-36-2" name="__codelineno-36-2" href="#__codelineno-36-2"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-36-3"><a id="__codelineno-36-3" name="__codelineno-36-3" href="#__codelineno-36-3"></a><span class="w"> </span><span class="nx">templateId_key</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-36-4"><a id="__codelineno-36-4" name="__codelineno-36-4" href="#__codelineno-36-4"></a><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">template.id</span><span class="p">,</span>
</span><span id="__span-36-5"><a id="__codelineno-36-5" name="__codelineno-36-5" href="#__codelineno-36-5"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;USER_NAME&#39;</span><span class="p">,</span>
</span><span id="__span-36-6"><a id="__codelineno-36-6" name="__codelineno-36-6" href="#__codelineno-36-6"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-36-7"><a id="__codelineno-36-7" name="__codelineno-36-7" href="#__codelineno-36-7"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-36-8"><a id="__codelineno-36-8" name="__codelineno-36-8" href="#__codelineno-36-8"></a><span class="w"> </span><span class="nx">update</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-36-9"><a id="__codelineno-36-9" name="__codelineno-36-9" href="#__codelineno-36-9"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;User Full Name&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// Updated label</span>
</span><span id="__span-36-10"><a id="__codelineno-36-10" name="__codelineno-36-10" href="#__codelineno-36-10"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-36-11"><a id="__codelineno-36-11" name="__codelineno-36-11" href="#__codelineno-36-11"></a><span class="w"> </span><span class="nx">create</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-36-12"><a id="__codelineno-36-12" name="__codelineno-36-12" href="#__codelineno-36-12"></a><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">template.id</span><span class="p">,</span>
</span><span id="__span-36-13"><a id="__codelineno-36-13" name="__codelineno-36-13" href="#__codelineno-36-13"></a><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;USER_NAME&#39;</span><span class="p">,</span>
</span><span id="__span-36-14"><a id="__codelineno-36-14" name="__codelineno-36-14" href="#__codelineno-36-14"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;User Full Name&#39;</span><span class="p">,</span>
</span><span id="__span-36-15"><a id="__codelineno-36-15" name="__codelineno-36-15" href="#__codelineno-36-15"></a><span class="w"> </span><span class="c1">// ...</span>
</span><span id="__span-36-16"><a id="__codelineno-36-16" name="__codelineno-36-16" href="#__codelineno-36-16"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-36-17"><a id="__codelineno-36-17" name="__codelineno-36-17" href="#__codelineno-36-17"></a><span class="p">});</span>
</span></code></pre></div></p>
<p><strong>Use different key:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-37-1"><a id="__codelineno-37-1" name="__codelineno-37-1" href="#__codelineno-37-1"></a><span class="c1">// If truly need separate variable</span>
</span><span id="__span-37-2"><a id="__codelineno-37-2" name="__codelineno-37-2" href="#__codelineno-37-2"></a><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;USER_FULL_NAME&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// Not USER_NAME</span>
</span></code></pre></div></p>
<hr />
<h3 id="problem-variables-not-alphabetically-sorted">Problem: Variables not alphabetically sorted<a class="headerlink" href="#problem-variables-not-alphabetically-sorted" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms:</strong>
- Variables appear in random order in editor
- Want alphabetical order instead of custom sort</p>
<p><strong>Causes:</strong>
- Sort order not set alphabetically
- Need to update sortOrder values</p>
<p><strong>Solutions:</strong></p>
<p><strong>Sort alphabetically by key:</strong>
<div class="language-sql highlight"><pre><span></span><code><span id="__span-38-1"><a id="__codelineno-38-1" name="__codelineno-38-1" href="#__codelineno-38-1"></a><span class="c1">-- Generate new sort order based on alphabetical order</span>
</span><span id="__span-38-2"><a id="__codelineno-38-2" name="__codelineno-38-2" href="#__codelineno-38-2"></a><span class="k">WITH</span><span class="w"> </span><span class="n">sorted</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="p">(</span>
</span><span id="__span-38-3"><a id="__codelineno-38-3" name="__codelineno-38-3" href="#__codelineno-38-3"></a><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">ROW_NUMBER</span><span class="p">()</span><span class="w"> </span><span class="n">OVER</span><span class="w"> </span><span class="p">(</span><span class="n">PARTITION</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">template_id</span><span class="w"> </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="k">key</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">new_order</span>
</span><span id="__span-38-4"><a id="__codelineno-38-4" name="__codelineno-38-4" href="#__codelineno-38-4"></a><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-38-5"><a id="__codelineno-38-5" name="__codelineno-38-5" href="#__codelineno-38-5"></a><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">template_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;cuid123&#39;</span>
</span><span id="__span-38-6"><a id="__codelineno-38-6" name="__codelineno-38-6" href="#__codelineno-38-6"></a><span class="p">)</span>
</span><span id="__span-38-7"><a id="__codelineno-38-7" name="__codelineno-38-7" href="#__codelineno-38-7"></a><span class="k">UPDATE</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-38-8"><a id="__codelineno-38-8" name="__codelineno-38-8" href="#__codelineno-38-8"></a><span class="k">SET</span><span class="w"> </span><span class="n">sort_order</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sorted</span><span class="p">.</span><span class="n">new_order</span>
</span><span id="__span-38-9"><a id="__codelineno-38-9" name="__codelineno-38-9" href="#__codelineno-38-9"></a><span class="k">FROM</span><span class="w"> </span><span class="n">sorted</span>
</span><span id="__span-38-10"><a id="__codelineno-38-10" name="__codelineno-38-10" href="#__codelineno-38-10"></a><span class="k">WHERE</span><span class="w"> </span><span class="n">email_template_variables</span><span class="p">.</span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sorted</span><span class="p">.</span><span class="n">id</span><span class="p">;</span>
</span></code></pre></div></p>
<p><strong>Sort by label:</strong>
<div class="language-sql highlight"><pre><span></span><code><span id="__span-39-1"><a id="__codelineno-39-1" name="__codelineno-39-1" href="#__codelineno-39-1"></a><span class="k">WITH</span><span class="w"> </span><span class="n">sorted</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="p">(</span>
</span><span id="__span-39-2"><a id="__codelineno-39-2" name="__codelineno-39-2" href="#__codelineno-39-2"></a><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">ROW_NUMBER</span><span class="p">()</span><span class="w"> </span><span class="n">OVER</span><span class="w"> </span><span class="p">(</span><span class="n">PARTITION</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">template_id</span><span class="w"> </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">label</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">new_order</span>
</span><span id="__span-39-3"><a id="__codelineno-39-3" name="__codelineno-39-3" href="#__codelineno-39-3"></a><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-39-4"><a id="__codelineno-39-4" name="__codelineno-39-4" href="#__codelineno-39-4"></a><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">template_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;cuid123&#39;</span>
</span><span id="__span-39-5"><a id="__codelineno-39-5" name="__codelineno-39-5" href="#__codelineno-39-5"></a><span class="p">)</span>
</span><span id="__span-39-6"><a id="__codelineno-39-6" name="__codelineno-39-6" href="#__codelineno-39-6"></a><span class="k">UPDATE</span><span class="w"> </span><span class="n">email_template_variables</span>
</span><span id="__span-39-7"><a id="__codelineno-39-7" name="__codelineno-39-7" href="#__codelineno-39-7"></a><span class="k">SET</span><span class="w"> </span><span class="n">sort_order</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sorted</span><span class="p">.</span><span class="n">new_order</span>
</span><span id="__span-39-8"><a id="__codelineno-39-8" name="__codelineno-39-8" href="#__codelineno-39-8"></a><span class="k">FROM</span><span class="w"> </span><span class="n">sorted</span>
</span><span id="__span-39-9"><a id="__codelineno-39-9" name="__codelineno-39-9" href="#__codelineno-39-9"></a><span class="k">WHERE</span><span class="w"> </span><span class="n">email_template_variables</span><span class="p">.</span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sorted</span><span class="p">.</span><span class="n">id</span><span class="p">;</span>
</span></code></pre></div></p>
<p><strong>Manual custom order:</strong>
- Use admin UI to drag-drop reorder
- Saves custom sortOrder values</p>
<hr />
<h2 id="performance-considerations">Performance Considerations<a class="headerlink" href="#performance-considerations" title="Permanent link">&para;</a></h2>
<h3 id="variable-loading">Variable Loading<a class="headerlink" href="#variable-loading" title="Permanent link">&para;</a></h3>
<p><strong>Current Implementation:</strong>
- Variables loaded with template via <code>include: { variables: true }</code>
- Single database query (JOIN)
- Fast (&lt; 10ms for typical templates)</p>
<p><strong>Optimization for Many Variables:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-40-1"><a id="__codelineno-40-1" name="__codelineno-40-1" href="#__codelineno-40-1"></a><span class="c1">// If template has 100+ variables, consider pagination</span>
</span><span id="__span-40-2"><a id="__codelineno-40-2" name="__codelineno-40-2" href="#__codelineno-40-2"></a><span class="kd">const</span><span class="w"> </span><span class="nx">variables</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplateVariable</span><span class="p">.</span><span class="nx">findMany</span><span class="p">({</span>
</span><span id="__span-40-3"><a id="__codelineno-40-3" name="__codelineno-40-3" href="#__codelineno-40-3"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">templateId</span><span class="o">:</span><span class="w"> </span><span class="kt">template.id</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-40-4"><a id="__codelineno-40-4" name="__codelineno-40-4" href="#__codelineno-40-4"></a><span class="w"> </span><span class="nx">orderBy</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;asc&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-40-5"><a id="__codelineno-40-5" name="__codelineno-40-5" href="#__codelineno-40-5"></a><span class="w"> </span><span class="nx">take</span><span class="o">:</span><span class="w"> </span><span class="kt">50</span><span class="p">,</span><span class="w"> </span><span class="c1">// Load first 50</span>
</span><span id="__span-40-6"><a id="__codelineno-40-6" name="__codelineno-40-6" href="#__codelineno-40-6"></a><span class="w"> </span><span class="nx">skip</span><span class="o">:</span><span class="w"> </span><span class="kt">0</span><span class="p">,</span><span class="w"> </span><span class="c1">// Offset for pagination</span>
</span><span id="__span-40-7"><a id="__codelineno-40-7" name="__codelineno-40-7" href="#__codelineno-40-7"></a><span class="p">});</span>
</span></code></pre></div></p>
<hr />
<h3 id="validation-performance">Validation Performance<a class="headerlink" href="#validation-performance" title="Permanent link">&para;</a></h3>
<p><strong>Required Variable Check:</strong>
- O(n) where n = number of required variables
- Fast for typical templates (&lt; 10 required vars)
- No database queries (uses in-memory variable list)</p>
<p><strong>Caching Variables:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-41-1"><a id="__codelineno-41-1" name="__codelineno-41-1" href="#__codelineno-41-1"></a><span class="c1">// Cache template + variables to avoid DB lookup per send</span>
</span><span id="__span-41-2"><a id="__codelineno-41-2" name="__codelineno-41-2" href="#__codelineno-41-2"></a><span class="kd">const</span><span class="w"> </span><span class="nx">templateCache</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nb">Map</span><span class="o">&lt;</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">EmailTemplate</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">variables</span><span class="o">:</span><span class="w"> </span><span class="kt">EmailTemplateVariable</span><span class="p">[]</span><span class="w"> </span><span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span><span id="__span-41-3"><a id="__codelineno-41-3" name="__codelineno-41-3" href="#__codelineno-41-3"></a>
</span><span id="__span-41-4"><a id="__codelineno-41-4" name="__codelineno-41-4" href="#__codelineno-41-4"></a><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">loadTemplate</span><span class="p">(</span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-41-5"><a id="__codelineno-41-5" name="__codelineno-41-5" href="#__codelineno-41-5"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">templateCache</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-41-6"><a id="__codelineno-41-6" name="__codelineno-41-6" href="#__codelineno-41-6"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">templateCache</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="o">!</span><span class="p">;</span>
</span><span id="__span-41-7"><a id="__codelineno-41-7" name="__codelineno-41-7" href="#__codelineno-41-7"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-41-8"><a id="__codelineno-41-8" name="__codelineno-41-8" href="#__codelineno-41-8"></a>
</span><span id="__span-41-9"><a id="__codelineno-41-9" name="__codelineno-41-9" href="#__codelineno-41-9"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">template</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">emailTemplate</span><span class="p">.</span><span class="nx">findUnique</span><span class="p">({</span>
</span><span id="__span-41-10"><a id="__codelineno-41-10" name="__codelineno-41-10" href="#__codelineno-41-10"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">isActive</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-41-11"><a id="__codelineno-41-11" name="__codelineno-41-11" href="#__codelineno-41-11"></a><span class="w"> </span><span class="nx">include</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">variables</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-41-12"><a id="__codelineno-41-12" name="__codelineno-41-12" href="#__codelineno-41-12"></a><span class="w"> </span><span class="p">});</span>
</span><span id="__span-41-13"><a id="__codelineno-41-13" name="__codelineno-41-13" href="#__codelineno-41-13"></a>
</span><span id="__span-41-14"><a id="__codelineno-41-14" name="__codelineno-41-14" href="#__codelineno-41-14"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">template</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-41-15"><a id="__codelineno-41-15" name="__codelineno-41-15" href="#__codelineno-41-15"></a><span class="w"> </span><span class="nx">templateCache</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">template</span><span class="p">);</span>
</span><span id="__span-41-16"><a id="__codelineno-41-16" name="__codelineno-41-16" href="#__codelineno-41-16"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-41-17"><a id="__codelineno-41-17" name="__codelineno-41-17" href="#__codelineno-41-17"></a>
</span><span id="__span-41-18"><a id="__codelineno-41-18" name="__codelineno-41-18" href="#__codelineno-41-18"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">template</span><span class="p">;</span>
</span><span id="__span-41-19"><a id="__codelineno-41-19" name="__codelineno-41-19" href="#__codelineno-41-19"></a><span class="p">}</span>
</span></code></pre></div></p>
<hr />
<h2 id="best-practices">Best Practices<a class="headerlink" href="#best-practices" title="Permanent link">&para;</a></h2>
<h3 id="variable-naming-conventions">Variable Naming Conventions<a class="headerlink" href="#variable-naming-conventions" title="Permanent link">&para;</a></h3>
<p><strong>Use UPPERCASE_WITH_UNDERSCORES:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-42-1"><a id="__codelineno-42-1" name="__codelineno-42-1" href="#__codelineno-42-1"></a><span class="c1">// ✓ Good</span>
</span><span id="__span-42-2"><a id="__codelineno-42-2" name="__codelineno-42-2" href="#__codelineno-42-2"></a><span class="nx">USER_NAME</span>
</span><span id="__span-42-3"><a id="__codelineno-42-3" name="__codelineno-42-3" href="#__codelineno-42-3"></a><span class="nx">SHIFT_DATE</span>
</span><span id="__span-42-4"><a id="__codelineno-42-4" name="__codelineno-42-4" href="#__codelineno-42-4"></a><span class="nx">HAS_PHONE</span>
</span><span id="__span-42-5"><a id="__codelineno-42-5" name="__codelineno-42-5" href="#__codelineno-42-5"></a><span class="nx">REPRESENTATIVE_EMAIL</span>
</span><span id="__span-42-6"><a id="__codelineno-42-6" name="__codelineno-42-6" href="#__codelineno-42-6"></a>
</span><span id="__span-42-7"><a id="__codelineno-42-7" name="__codelineno-42-7" href="#__codelineno-42-7"></a><span class="c1">// ✗ Bad</span>
</span><span id="__span-42-8"><a id="__codelineno-42-8" name="__codelineno-42-8" href="#__codelineno-42-8"></a><span class="nx">userName</span><span class="w"> </span><span class="c1">// Not uppercase</span>
</span><span id="__span-42-9"><a id="__codelineno-42-9" name="__codelineno-42-9" href="#__codelineno-42-9"></a><span class="nx">user</span><span class="o">-</span><span class="nx">name</span><span class="w"> </span><span class="c1">// Dashes not underscores</span>
</span><span id="__span-42-10"><a id="__codelineno-42-10" name="__codelineno-42-10" href="#__codelineno-42-10"></a><span class="nx">UserName</span><span class="w"> </span><span class="c1">// PascalCase</span>
</span></code></pre></div></p>
<p><strong>Be Descriptive:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-43-1"><a id="__codelineno-43-1" name="__codelineno-43-1" href="#__codelineno-43-1"></a><span class="c1">// ✓ Good</span>
</span><span id="__span-43-2"><a id="__codelineno-43-2" name="__codelineno-43-2" href="#__codelineno-43-2"></a><span class="nx">SHIFT_START_TIME</span>
</span><span id="__span-43-3"><a id="__codelineno-43-3" name="__codelineno-43-3" href="#__codelineno-43-3"></a><span class="nx">CAMPAIGN_TITLE</span>
</span><span id="__span-43-4"><a id="__codelineno-43-4" name="__codelineno-43-4" href="#__codelineno-43-4"></a><span class="nx">IS_EMAIL_VERIFIED</span>
</span><span id="__span-43-5"><a id="__codelineno-43-5" name="__codelineno-43-5" href="#__codelineno-43-5"></a>
</span><span id="__span-43-6"><a id="__codelineno-43-6" name="__codelineno-43-6" href="#__codelineno-43-6"></a><span class="c1">// ✗ Bad</span>
</span><span id="__span-43-7"><a id="__codelineno-43-7" name="__codelineno-43-7" href="#__codelineno-43-7"></a><span class="nx">TIME</span><span class="w"> </span><span class="c1">// Too vague</span>
</span><span id="__span-43-8"><a id="__codelineno-43-8" name="__codelineno-43-8" href="#__codelineno-43-8"></a><span class="nx">TITLE</span><span class="w"> </span><span class="c1">// Ambiguous</span>
</span><span id="__span-43-9"><a id="__codelineno-43-9" name="__codelineno-43-9" href="#__codelineno-43-9"></a><span class="nx">VERIFIED</span><span class="w"> </span><span class="c1">// Missing context</span>
</span></code></pre></div></p>
<p><strong>Prefix Booleans with IS/HAS:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-44-1"><a id="__codelineno-44-1" name="__codelineno-44-1" href="#__codelineno-44-1"></a><span class="c1">// ✓ Good</span>
</span><span id="__span-44-2"><a id="__codelineno-44-2" name="__codelineno-44-2" href="#__codelineno-44-2"></a><span class="nx">HAS_PHONE</span>
</span><span id="__span-44-3"><a id="__codelineno-44-3" name="__codelineno-44-3" href="#__codelineno-44-3"></a><span class="nx">IS_VERIFIED</span>
</span><span id="__span-44-4"><a id="__codelineno-44-4" name="__codelineno-44-4" href="#__codelineno-44-4"></a><span class="nx">IS_CUT_ASSIGNED</span>
</span><span id="__span-44-5"><a id="__codelineno-44-5" name="__codelineno-44-5" href="#__codelineno-44-5"></a>
</span><span id="__span-44-6"><a id="__codelineno-44-6" name="__codelineno-44-6" href="#__codelineno-44-6"></a><span class="c1">// ✗ Bad</span>
</span><span id="__span-44-7"><a id="__codelineno-44-7" name="__codelineno-44-7" href="#__codelineno-44-7"></a><span class="nx">PHONE</span><span class="w"> </span><span class="c1">// Not clearly boolean</span>
</span><span id="__span-44-8"><a id="__codelineno-44-8" name="__codelineno-44-8" href="#__codelineno-44-8"></a><span class="nx">VERIFIED</span><span class="w"> </span><span class="c1">// Ambiguous (boolean or timestamp?)</span>
</span></code></pre></div></p>
<hr />
<h3 id="documentation">Documentation<a class="headerlink" href="#documentation" title="Permanent link">&para;</a></h3>
<p><strong>Always Provide Labels:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-45-1"><a id="__codelineno-45-1" name="__codelineno-45-1" href="#__codelineno-45-1"></a><span class="c1">// ✓ Good</span>
</span><span id="__span-45-2"><a id="__codelineno-45-2" name="__codelineno-45-2" href="#__codelineno-45-2"></a><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;User\&#39;s Full Name&#39;</span><span class="p">,</span>
</span><span id="__span-45-3"><a id="__codelineno-45-3" name="__codelineno-45-3" href="#__codelineno-45-3"></a><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Full name of the email recipient&#39;</span><span class="p">,</span>
</span><span id="__span-45-4"><a id="__codelineno-45-4" name="__codelineno-45-4" href="#__codelineno-45-4"></a>
</span><span id="__span-45-5"><a id="__codelineno-45-5" name="__codelineno-45-5" href="#__codelineno-45-5"></a><span class="c1">// ✗ Bad</span>
</span><span id="__span-45-6"><a id="__codelineno-45-6" name="__codelineno-45-6" href="#__codelineno-45-6"></a><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Name&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// Too generic</span>
</span><span id="__span-45-7"><a id="__codelineno-45-7" name="__codelineno-45-7" href="#__codelineno-45-7"></a><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">,</span>
</span></code></pre></div></p>
<p><strong>Document Expected Format:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-46-1"><a id="__codelineno-46-1" name="__codelineno-46-1" href="#__codelineno-46-1"></a><span class="c1">// ✓ Good</span>
</span><span id="__span-46-2"><a id="__codelineno-46-2" name="__codelineno-46-2" href="#__codelineno-46-2"></a><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Shift date in format &quot;Saturday, March 15, 2026&quot;&#39;</span><span class="p">,</span>
</span><span id="__span-46-3"><a id="__codelineno-46-3" name="__codelineno-46-3" href="#__codelineno-46-3"></a><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Saturday, March 15, 2026&#39;</span><span class="p">,</span>
</span><span id="__span-46-4"><a id="__codelineno-46-4" name="__codelineno-46-4" href="#__codelineno-46-4"></a>
</span><span id="__span-46-5"><a id="__codelineno-46-5" name="__codelineno-46-5" href="#__codelineno-46-5"></a><span class="c1">// ✗ Bad</span>
</span><span id="__span-46-6"><a id="__codelineno-46-6" name="__codelineno-46-6" href="#__codelineno-46-6"></a><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;The date&#39;</span><span class="p">,</span>
</span><span id="__span-46-7"><a id="__codelineno-46-7" name="__codelineno-46-7" href="#__codelineno-46-7"></a><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;2026-03-15&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// Doesn&#39;t match expected format</span>
</span></code></pre></div></p>
<hr />
<h3 id="sample-values">Sample Values<a class="headerlink" href="#sample-values" title="Permanent link">&para;</a></h3>
<p><strong>Provide Realistic Examples:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-47-1"><a id="__codelineno-47-1" name="__codelineno-47-1" href="#__codelineno-47-1"></a><span class="c1">// ✓ Good</span>
</span><span id="__span-47-2"><a id="__codelineno-47-2" name="__codelineno-47-2" href="#__codelineno-47-2"></a><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;John Doe&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// USER_NAME</span>
</span><span id="__span-47-3"><a id="__codelineno-47-3" name="__codelineno-47-3" href="#__codelineno-47-3"></a><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Saturday, March 15, 2026&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// SHIFT_DATE</span>
</span><span id="__span-47-4"><a id="__codelineno-47-4" name="__codelineno-47-4" href="#__codelineno-47-4"></a><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;(555) 123-4567&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// USER_PHONE</span>
</span><span id="__span-47-5"><a id="__codelineno-47-5" name="__codelineno-47-5" href="#__codelineno-47-5"></a>
</span><span id="__span-47-6"><a id="__codelineno-47-6" name="__codelineno-47-6" href="#__codelineno-47-6"></a><span class="c1">// ✗ Bad</span>
</span><span id="__span-47-7"><a id="__codelineno-47-7" name="__codelineno-47-7" href="#__codelineno-47-7"></a><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;test&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// Not realistic</span>
</span><span id="__span-47-8"><a id="__codelineno-47-8" name="__codelineno-47-8" href="#__codelineno-47-8"></a><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;123&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// Not realistic phone</span>
</span></code></pre></div></p>
<p><strong>Use JSON for Arrays/Objects:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-48-1"><a id="__codelineno-48-1" name="__codelineno-48-1" href="#__codelineno-48-1"></a><span class="c1">// ✓ Good</span>
</span><span id="__span-48-2"><a id="__codelineno-48-2" name="__codelineno-48-2" href="#__codelineno-48-2"></a><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="kt">JSON.stringify</span><span class="p">([</span>
</span><span id="__span-48-3"><a id="__codelineno-48-3" name="__codelineno-48-3" href="#__codelineno-48-3"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Jane Doe&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">email</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;jane@example.com&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-48-4"><a id="__codelineno-48-4" name="__codelineno-48-4" href="#__codelineno-48-4"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;John Smith&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">email</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;john@example.com&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-48-5"><a id="__codelineno-48-5" name="__codelineno-48-5" href="#__codelineno-48-5"></a><span class="p">]),</span>
</span><span id="__span-48-6"><a id="__codelineno-48-6" name="__codelineno-48-6" href="#__codelineno-48-6"></a>
</span><span id="__span-48-7"><a id="__codelineno-48-7" name="__codelineno-48-7" href="#__codelineno-48-7"></a><span class="c1">// ✗ Bad</span>
</span><span id="__span-48-8"><a id="__codelineno-48-8" name="__codelineno-48-8" href="#__codelineno-48-8"></a><span class="nx">sampleValue</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;array of representatives&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// Not parseable</span>
</span></code></pre></div></p>
<hr />
<h3 id="required-vs-optional">Required vs Optional<a class="headerlink" href="#required-vs-optional" title="Permanent link">&para;</a></h3>
<p><strong>Make Variables Required If:</strong>
- Used in subject line (always visible)
- Critical to email meaning (e.g., event date)
- No reasonable default value</p>
<p><strong>Make Variables Optional If:</strong>
- Used in conditional blocks (<code>{{#if}}</code>)
- Nice-to-have but not critical
- Has fallback text in template</p>
<hr />
<h2 id="related-documentation">Related Documentation<a class="headerlink" href="#related-documentation" title="Permanent link">&para;</a></h2>
<h3 id="frontend-documentation">Frontend Documentation<a class="headerlink" href="#frontend-documentation" title="Permanent link">&para;</a></h3>
<ul>
<li><strong><a href="../../frontend/pages/email-template-editor-page.md">EmailTemplateEditorPage.tsx</a></strong> — Variable insertion panel</li>
<li><strong><a href="../../frontend/pages/email-templates-page.md">EmailTemplatesPage.tsx</a></strong> — Variables tab</li>
</ul>
<h3 id="backend-documentation">Backend Documentation<a class="headerlink" href="#backend-documentation" title="Permanent link">&para;</a></h3>
<ul>
<li><strong><a href="../../api/modules/email-templates.md">Email Templates Module</a></strong> — Variable CRUD API</li>
<li><code>GET /api/email-templates/:id/variables</code> — List variables</li>
<li><code>POST /api/email-templates/:id/variables</code> — Create variable</li>
<li><code>PUT /api/email-templates/:id/variables/:varId</code> — Update variable</li>
<li><code>DELETE /api/email-templates/:id/variables/:varId</code> — Delete variable</li>
</ul>
<h3 id="database-documentation">Database Documentation<a class="headerlink" href="#database-documentation" title="Permanent link">&para;</a></h3>
<ul>
<li><strong><a href="../../../database/models/email-templates/">Email Templates Models</a></strong> — EmailTemplateVariable schema</li>
</ul>
<h3 id="feature-documentation">Feature Documentation<a class="headerlink" href="#feature-documentation" title="Permanent link">&para;</a></h3>
<ul>
<li><strong><a href="../template-system/">template-system.md</a></strong> — Email template engine overview</li>
<li><strong><a href="../editor/">editor.md</a></strong> — Email template editor interface</li>
<li><strong><a href="../versioning/">versioning.md</a></strong> — Template version history</li>
</ul>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<nav class="md-footer__inner md-grid" aria-label="Footer" >
<a href="../editor/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Editor">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
Previous
</span>
<div class="md-ellipsis">
Editor
</div>
</div>
</a>
<a href="../versioning/" class="md-footer__link md-footer__link--next" aria-label="Next: Versioning">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
Versioning
</div>
</div>
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg>
</div>
</a>
</nav>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
<div class="md-copyright__highlight">
Copyright &copy; 2024 The Bunker Operations <a href="#__consent">Change cookie settings</a>
</div>
</div>
<div class="md-social">
<a href="https://gitea.bnkops.com/admin" target="_blank" rel="noopener" title="Gitea Repository" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</a>
<a href="https://listmonk.bnkops.com/subscription/form" target="_blank" rel="noopener" title="Newsletter" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M536.4-26.3c9.8-3.5 20.6-1 28 6.3s9.8 18.2 6.3 28l-178 496.9c-5 13.9-18.1 23.1-32.8 23.1-14.2 0-27-8.6-32.3-21.7l-64.2-158c-4.5-11-2.5-23.6 5.2-32.6l94.5-112.4c5.1-6.1 4.7-15-.9-20.6s-14.6-6-20.6-.9l-112.4 94.3c-9.1 7.6-21.6 9.6-32.6 5.2L38.1 216.8c-13.1-5.3-21.7-18.1-21.7-32.3 0-14.7 9.2-27.8 23.1-32.8z"/></svg>
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "../../../..", "features": ["announce.dismiss", "content.action.edit", "content.action.view", "content.code.annotate", "content.code.copy", "content.tooltips", "navigation.expand", "navigation.footer", "navigation.indexes", "navigation.path", "navigation.prune", "navigation.sections", "navigation.tabs", "navigation.tabs.sticky", "navigation.top", "navigation.tracking", "search.highlight", "search.share", "search.suggest", "toc.follow"], "search": "../../../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../../../../assets/javascripts/bundle.79ae519e.min.js"></script>
<script src="../../../../javascripts/home.js"></script>
<script src="../../../../javascripts/github-widget.js"></script>
<script src="../../../../javascripts/gitea-widget.js"></script>
<script src="../../../../assets/js/env-config.js"></script>
<script src="../../../../assets/js/video-player.js"></script>
</body>
</html>