Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5bbc698
feat: modernize email templates with better-svelte-email
niemyjski Aug 27, 2026
a76cb3d
feat: modernize email templates with Svelte 5, Tailwind, Storybook
niemyjski May 27, 2026
87669d1
fix: correct Storybook rendering bugs and text formatting issues
niemyjski May 27, 2026
dc2ee58
fix(email-templates): address PR feedback and staff engineer review
niemyjski May 28, 2026
c75f4b0
fix: address all PR review feedback
niemyjski May 30, 2026
e05cc90
fix: correct Fields sample data format in Storybook preview
niemyjski May 30, 2026
a99c424
docs: add before/after email template comparison screenshots from Mai…
niemyjski May 30, 2026
17018f4
refactor: replace raw HTML stats table with Row/Column components in …
niemyjski May 30, 2026
08dece4
chore: remove nodemailer test dependency, add .gstack to .gitignore
niemyjski May 30, 2026
684b41f
fix(email): harden template production readiness
niemyjski Aug 27, 2026
e466ce6
fix(email): keep template tooling in sync
niemyjski Jul 16, 2026
a6055b0
fix(email): harden template tooling after merge
niemyjski Jul 26, 2026
5edb525
feat: add email template parity validation
niemyjski Aug 27, 2026
8f272fe
ci: stabilize email pixel parity
niemyjski Jul 26, 2026
8f06fe9
fix(email): close reviewer feedback audit
niemyjski Jul 26, 2026
c753a62
Fix email links and invitation acceptance
niemyjski Jul 30, 2026
4ed2bd3
fix(email): strip nondeterministic SSR markers
niemyjski Aug 5, 2026
37106bf
chore(email): refresh template toolchain dependencies
niemyjski Aug 6, 2026
58d445a
fix(email): route usage notices correctly
niemyjski Aug 10, 2026
f2ccc15
Fix auth client formatting after base merge
niemyjski Aug 19, 2026
10f9ca3
refactor(email): simplify template modernization
niemyjski Aug 27, 2026
ff5d6c2
fix(mail): safely render event links and regression labels
niemyjski Aug 25, 2026
cbdcaf6
refactor(email): extract invitation authentication changes
niemyjski Aug 25, 2026
52e8c0d
refactor(email): consolidate renderer into ClientApp
niemyjski Aug 25, 2026
6b307d6
refactor(email): reuse application theme
niemyjski Aug 27, 2026
0a66f41
fix(email): run renderer after email bundle
niemyjski Aug 27, 2026
3c028c1
fix(email): preserve JSON-LD subject serialization
niemyjski Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,15 @@ jobs:
- name: Build
run: npm run build

- name: Verify Generated Email HTML is Up-To-Date
run: |
changes="$(git status --short --untracked-files=all -- ../../Exceptionless.Core/Mail/Templates/)"
if [ -n "$changes" ]; then
echo "$changes"
echo "::error::Generated email artifacts are stale. Run 'npm run build:emails' in src/Exceptionless.Web/ClientApp and commit the updated files."
exit 1
fi

- name: Run Unit Tests
run: echo "npm run test:unit"

Expand Down
44 changes: 1 addition & 43 deletions src/Exceptionless.Core/Mail/Templates/contact-request.html
Original file line number Diff line number Diff line change
@@ -1,43 +1 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{Subject}}</title>
</head>
<body style="font-family: Helvetica, Arial, sans-serif; color: #2c2c2c; line-height: 1.5;">
<h1 style="font-size: 22px; font-weight: 400;">{{Subject}}</h1>
<dl>
<dt>Name</dt>
<dd>{{Name}}</dd>
<dt>Email</dt>
<dd>{{EmailAddress}}</dd>
{{#if Company}}
<dt>Company</dt>
<dd>{{Company}}</dd>
{{/if}}
<dt>Subject</dt>
<dd>{{RequestSubject}}</dd>
</dl>

<h2 style="font-size: 18px; font-weight: 400;">Message</h2>
{{#each MessageLines}}
<p>{{this}}</p>
{{/each}}

<hr>
<dl>
{{#if ClientIpAddress}}
<dt>IP address</dt>
<dd>{{ClientIpAddress}}</dd>
{{/if}}
{{#if UserAgent}}
<dt>User agent</dt>
<dd>{{UserAgent}}</dd>
{{/if}}
{{#if Referrer}}
<dt>Referrer</dt>
<dd>{{Referrer}}</dd>
{{/if}}
</dl>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html id="__better-svelte-email" lang="en" dir="ltr" style="overflow:hidden"><head><meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="x-apple-disable-message-reformatting"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta charset="utf-8"> <title>{{Subject}}</title></head> <body style="background-color:hsl(0 0% 100%);color:hsl(221 39% 11%);font-family:Helvetica,Arial,sans-serif;line-height:1.5;color:hsl(221 39% 11%)"><h1 style="font-size:22px;font-weight:400">{{Subject}}</h1> <dl><dt>Name</dt> <dd>{{Name}}</dd> <dt>Email</dt> <dd>{{EmailAddress}}</dd> {{#if Company}} <dt>Company</dt> <dd>{{Company}}</dd> {{/if}} <dt>Subject</dt> <dd>{{RequestSubject}}</dd></dl> <h2 style="font-size:18px;font-weight:400">Message</h2> {{#each MessageLines}} <p>{{this}}</p> {{/each}} <hr> <dl>{{#if ClientIpAddress}} <dt>IP address</dt> <dd>{{ClientIpAddress}}</dd> {{/if}} {{#if UserAgent}} <dt>User agent</dt> <dd>{{UserAgent}}</dd> {{/if}} {{#if Referrer}} <dt>Referrer</dt> <dd>{{Referrer}}</dd> {{/if}}</dl></body></html>
37 changes: 30 additions & 7 deletions src/Exceptionless.Core/Mail/Templates/event-notice.html

Large diffs are not rendered by default.

37 changes: 30 additions & 7 deletions src/Exceptionless.Core/Mail/Templates/organization-added.html

Large diffs are not rendered by default.

37 changes: 30 additions & 7 deletions src/Exceptionless.Core/Mail/Templates/organization-invited.html

Large diffs are not rendered by default.

34 changes: 27 additions & 7 deletions src/Exceptionless.Core/Mail/Templates/organization-notice.html

Large diffs are not rendered by default.

Large diffs are not rendered by default.

76 changes: 63 additions & 13 deletions src/Exceptionless.Core/Mail/Templates/project-daily-summary.html

Large diffs are not rendered by default.

30 changes: 23 additions & 7 deletions src/Exceptionless.Core/Mail/Templates/user-email-verify.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" style="background:#f7f7f7!important"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="viewport" content="width=device-width"><title>{{Subject}}</title><style>@media only screen{html{min-height:100%;background:#f7f7f7}}@media only screen and (max-width:596px){table.body img{width:auto;height:auto}table.body center{min-width:0!important}table.body .container{width:95%!important}table.body .columns{height:auto!important;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding-left:16px!important;padding-right:16px!important}th.small-3{display:inline-block!important;width:25%!important}th.small-4{display:inline-block!important;width:33.33333%!important}th.small-12{display:inline-block!important;width:100%!important}table.menu{width:100%!important}table.menu td,table.menu th{width:auto!important;display:inline-block!important}table.menu.vertical td,table.menu.vertical th{display:block!important}table.menu[align=center]{width:auto!important}}</style></head><body style="-moz-box-sizing:border-box;-ms-text-size-adjust:100%;-webkit-box-sizing:border-box;-webkit-text-size-adjust:100%;Margin:0;background:#f7f7f7!important;box-sizing:border-box;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;min-width:100%;padding:0;text-align:left;width:100%!important"><span class="preheader" style="color:#f7f7f7;display:none!important;font-size:1px;line-height:1px;max-height:0;max-width:0;mso-hide:all!important;opacity:0;overflow:hidden;visibility:hidden"></span><table class="body" style="Margin:0;background:#f7f7f7!important;border-collapse:collapse;border-spacing:0;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;height:100%;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;width:100%"><tr style="padding:0;text-align:left;vertical-align:top"><td class="center" align="center" valign="top" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"><center data-parsed style="min-width:580px;width:100%"><table align="center" class="wrapper header float-center" style="Margin:0 auto;background:#2c2c2c;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%"><tr style="padding:0;text-align:left;vertical-align:top"><td class="wrapper-inner" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:8px 15px;text-align:left;vertical-align:top;word-wrap:break-word"><table align="center" class="container" style="Margin:0 auto;background:0 0;border-collapse:collapse;border-spacing:0;margin:0 auto;padding:0;text-align:inherit;vertical-align:top;width:580px"><tbody><tr style="padding:0;text-align:left;vertical-align:top"><td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"><img src="https://be.exceptionless.io/img/exceptionless-logo.png" alt="Exceptionless" style="-ms-interpolation-mode:bicubic;clear:both;display:block;margin-left:15px;max-width:100%;outline:0;text-decoration:none;width:auto"></td></tr></tbody></table></td></tr></table><table align="center" class="container float-center" style="Margin:0 auto;background:#f7f7f7;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:580px"><tbody><tr style="padding:0;text-align:left;vertical-align:top"><td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"><table class="spacer" style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"><tbody><tr style="padding:0;text-align:left;vertical-align:top"><td height="5px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:5px;font-weight:400;hyphens:auto;line-height:5px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">&#xA0;</td></tr></tbody></table><table class="row" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%"><tbody><tr style="padding:0;text-align:left;vertical-align:top"><th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0 auto;padding:0;padding-bottom:16px;padding-left:16px;padding-right:16px;text-align:left;width:564px"><table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"><tr style="padding:0;text-align:left;vertical-align:top"><th style="Margin:0;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"><h1 style="Margin:0;Margin-bottom:10px;color:inherit;font-family:Helvetica,Arial,sans-serif;font-size:34px;font-weight:400;line-height:1.3;margin:0;margin-bottom:5px;margin-top:0;padding:0;text-align:left;word-wrap:normal">Hello {{UserFullName}},</h1><p class="lead" style="Margin:0;Margin-bottom:10px;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:20px;font-weight:400;line-height:1.6;margin:0;margin-bottom:10px;padding:0;text-align:left">We're ready to activate your account. All we need to do is make sure this is your email address.</p><center data-parsed style="min-width:532px;width:100%"><table class="button button radius float-center" style="Margin:0 0 16px 0;border-collapse:collapse;border-spacing:0;float:none;margin:0 0 16px 0;padding:0;text-align:center;vertical-align:top;width:auto"><tr style="padding:0;text-align:left;vertical-align:top"><td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"><table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"><tr style="padding:0;text-align:left;vertical-align:top"><td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;background:#5E9A00;border:none;border-collapse:collapse!important;border-radius:3px;color:#fefefe;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"><a href="{{BaseUrl}}/account/verify?token={{UserVerifyEmailAddressToken}}" style="Margin:0;border:0 solid #5E9A00;border-radius:3px;color:#fefefe;display:inline-block;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:700;line-height:1.3;margin:0;padding:8px 16px 8px 16px;text-align:left;text-decoration:none">Verify Address </a></td></tr></table></td></tr></table></center><table class="spacer" style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"><tbody><tr style="padding:0;text-align:left;vertical-align:top"><td height="5px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:5px;font-weight:400;hyphens:auto;line-height:5px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">&#xA0;</td></tr></tbody></table><p style="Margin:0;Margin-bottom:10px;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;text-align:left">If you didn't create an Exceptionless account, just delete this email and everything will go back to the way it was.</p></th><th class="expander" style="Margin:0;color:#2c2c2c;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th></tr></table></th></tr></tbody></table></td></tr></tbody></table><script type="application/ld+json" align="center" class="float-center">{
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html id="__better-svelte-email" lang="en" dir="ltr" style="overflow:hidden"><head><meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="x-apple-disable-message-reformatting"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <title>{{Subject}}</title> <meta name="color-scheme" content="light only"> <meta name="supported-color-schemes" content="light only"> <style>
:root{color-scheme:light only;supported-color-schemes:light}
html,body{margin:0!important;padding:0!important;width:100%!important;min-width:100%!important;background:#fff!important;color:#111827!important;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}
table{border-collapse:collapse;border-spacing:0}
td{box-sizing:border-box}
img{border:0;line-height:100%;outline:none;text-decoration:none}
[data-email-header]>tbody>tr>td{padding-top:8px;padding-bottom:8px}
[data-email-content]>tbody>tr>td{padding:5px 16px 16px;-webkit-hyphens:auto;hyphens:auto;word-wrap:break-word}
[data-email-button-row]{margin-bottom:21px}
[data-email-button-row] a span{line-height:1.3!important}
[data-email-content] [data-email-button-row]:last-child{margin-bottom:16px}
p{margin:0 0 10px!important}
p+p{margin-top:15px!important}
@media only screen and (max-width:596px){[data-email-header]>tbody>tr>td{padding-left:15px;padding-right:15px}[data-email-header]>tbody>tr>td>table{width:95%!important}[data-email-container]{width:95%!important}}
</style></head> <body style="background-color:hsl(0 0% 100%);color:hsl(221 39% 11%)"><table align="center" width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation"><tbody><tr><td style="background-color:hsl(0 0% 100%);color:hsl(221 39% 11%);font-family:Helvetica,Arial,sans-serif"> <table align="center" width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation" data-email-header="true" style="background-color:hsl(221 39% 11%);width:100%;padding-left:16px;padding-right:16px;padding-top:8px;padding-bottom:8px"><tbody><tr><td><table align="center" width="100%" role="presentation" cellspacing="0" cellpadding="0" border="0" style="max-width:37.5em;margin-left:auto;margin-right:auto;max-width:580px"><tbody><tr style="width: 100%;"><td><img alt="Exceptionless" src="https://be.exceptionless.io/img/exceptionless-logo.png" width="235" height="50" style="display:block;outline:none;border:none;text-decoration:none;margin-left:15px;display:block"></td></tr></tbody></table></td></tr></tbody></table> <table align="center" width="100%" role="presentation" cellspacing="0" cellpadding="0" border="0" style="max-width:37.5em;background-color:hsl(0 0% 100%);margin-left:auto;margin-right:auto;max-width:580px" data-email-container="true"><tbody><tr style="width: 100%;"><td><table align="center" width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation" data-email-content="true" style="padding-left:16px;padding-right:16px;padding-top:8px;padding-bottom:8px"><tbody><tr><td><h1 style="color:hsl(221 39% 11%);margin-top:0px;margin-bottom:5px;font-size:34px;line-height:1.3;font-weight:400">Hello {{UserFullName}},</h1> <p style="font-size:14px;line-height:24px;margin:16px 0;color:hsl(221 39% 11%);font-size:20px;line-height:1.6">We're ready to activate your account. All we need to do is make sure this is your email address.</p> <table align="center" width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation" data-email-button-row="true" style="text-align:center"><tbody><tr><td><a href="{{BaseUrl}}/account/verify?token={{UserVerifyEmailAddressToken}}" style="line-height:100%;text-decoration:none;display:inline-block;max-width:100%;background-color:hsl(96 64% 46%);display:inline-block;border-radius:3px;padding-left:16px;padding-right:16px;padding-top:8px;padding-bottom:8px;font-size:16px;line-height:1.5;font-weight:700;color:rgb(255, 255, 255);text-decoration-line:none"><span><!--[if mso]><i style="letter-spacing: 0px;mso-font-width:-100%;mso-text-raise:0pt" hidden>&nbsp;</i><![endif]--></span> <span style="max-width:100%;display:inline-block;line-height:120%;text-decoration:none;text-transform:none;mso-padding-alt:0px">Verify Address</span> <span style="display: none;"><!--[if mso]><i style="letter-spacing: 0px;mso-font-width:-100%" hidden>&nbsp;</i><![endif]--></span></a></td></tr></tbody></table> <p style="font-size:14px;line-height:24px;margin:16px 0;color:hsl(221 39% 11%);font-size:16px;line-height:1.5;line-height:1.3">If you didn't create an Exceptionless account, just delete this email and everything will go back to the way it was.</p></td></tr></tbody></table></td></tr></tbody></table></td></tr></tbody></table> <script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "EmailMessage",
"description": {{json Subject}},
"potentialAction": {
"@type": "ViewAction",
"target": "{{BaseUrl}}/account/verify?token={{UserVerifyEmailAddressToken}}",
"url": "{{BaseUrl}}/account/verify?token={{UserVerifyEmailAddressToken}}",
"name": "Verify Address"
},
"@type": "ViewAction",
"target": "{{BaseUrl}}/account/verify?token={{UserVerifyEmailAddressToken}}",
"url": "{{BaseUrl}}/account/verify?token={{UserVerifyEmailAddressToken}}",
"name": "Verify Address"
},
"publisher": {
"@type": "Organization",
"name": "Exceptionless",
"url": "https://exceptionless.com",
"logo": "https://be.exceptionless.io/img/exceptionless-48.png"
}
}</script></center></td></tr></table><!-- prevent Gmail on iOS font size manipulation --><div style="display:none;white-space:nowrap;font:15px courier;line-height:0">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div></body></html>
}
</script></body></html>
Loading
Loading