Replace MailerSend template sprawl with a repo-owned render endpoint - #1065
Draft
hturnbull93 wants to merge 7 commits into
Draft
Replace MailerSend template sprawl with a repo-owned render endpoint#1065hturnbull93 wants to merge 7 commits into
hturnbull93 wants to merge 7 commits into
Conversation
Airtable's onboarding automation sent every welcome email via one of 7
hand-authored MailerSend templates, hardcoding chapter links/leaders that
already drift from the live National Groups data, and only distinguishing
volunteer vs non-volunteer recipients. Adds a render endpoint that composes
subject/html/text from live chapter data, the recipient's actual intent
(act-now/keep-informed/volunteer/lead), and language - so new chapters and
languages no longer need a new MailerSend template + script branch.
- src/lib/server/onboardingEmail/ - chapter lookup (reuses the National
Groups fetch), intent/language copy, HTML + plain-text renderers
- src/routes/api/onboarding-email - bearer-secret POST endpoint returning
{subject, html, text}; render-only, never sends mail itself
- src/routes/api/unsubscribe - suppresses on both MailerSend domains and
flips the Members "Email subscription" checkbox; no unsubscribe
mechanism existed in any of the 7 templates before this
- src/routes/onboarding-email-preview - dev-only QA page, 404s outside dev
- airtable-mailersend-emails.js - tries the new endpoint first, falls
through to the untouched template_id logic on any failure
- email-templates/ - raw MailerSend payloads for all 7 templates, kept as
migration reference and to document the old routing gaps (Canada-FR was
unreachable in practice; Lead hands off via mailto instead of a form
submission)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GtQwhn5Fb9eiC4LsmRs47q
✅ Deploy Preview for pauseai ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
hturnbull93
marked this pull request as ready for review
September 2, 2026 17:54
svelte-check generates $env/static/private types from .env, which CI populates via `cp template.env .env`. The static imports added in this branch (MAILERSEND_API_KEY in the unsubscribe route, ONBOARDING_EMAIL_RENDER_SECRET in the onboarding-email route) referenced members missing from template.env, failing `pnpm check` and blocking the deploy preview. Values left empty; Netlify secret scanning rejects real values in committed files. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019P2Yg2vt6iV6w1rAL1wS4h
Two failures blocking the deploy preview for PauseAI#1065: - Netlify's build does not populate .env from template.env; it exposes only the vars configured in the Netlify environment. That list has MAILERSEND_API_KEY but not ONBOARDING_EMAIL_RENDER_SECRET, and $env/static/private fails the build outright on a missing member. Switch the onboarding-email route to $env/dynamic/private (runtime lookup, no build-time export requirement), matching the pattern in contact-us/+page.server.ts. - eslint's typed linting ran on airtable-mailersend-emails.js, which is an Airtable script-runtime file not covered by tsconfig.check.json, producing a parser error. Add it to eslint's globalIgnores. pnpm check and pnpm build both pass locally. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019P2Yg2vt6iV6w1rAL1wS4h
Gate the QA route on hostname rather than dev only: dev, localhost, and *.netlify.app can load it; the production domain still 404s. Lets reviewers eyeball the rendered emails on the PR's deploy preview without running the site locally. The page renders public National Groups data and a fake recipient, no PII, so preview URLs are an acceptable exposure. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019P2Yg2vt6iV6w1rAL1wS4h
hturnbull93
marked this pull request as draft
September 2, 2026 18:23
Replace the fixed list of 10 preset intent x chapter x language combos with a form exposing each renderer input on its own: first name (text), country (free-text with a datalist of the 196 known names), intent (select, including the empty/unrecognised case), and languages (checkboxes over the stored vocabulary). Values live in the query string so a preview is shareable, and any change auto-submits the GET form (plain submit button as the no-JS fallback). Adds a "resolved" panel showing which branch the inputs hit: email language, intent bucket, and whether the country matched a National Groups chapter (with leader and link count) or fell back to Global. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019P2Yg2vt6iV6w1rAL1wS4h
…scheme - Add an optional languageOverride to OnboardingEmailParams so the preview can force en/es/fr directly instead of reverse-engineering country + languages. The production render endpoint never sets it. - Preview language control is now three radios (en/es/fr) — the only languages copy.ts actually has copy for. - Country is a <select> populated from the active National Groups records (new listActiveChapterCountries() helper, same base/table/filter as the chapter lookup), plus a "global fallback" option. These are the only countries that resolve to a real chapter block. - Pin the page to a light color-scheme with explicit background/colour on the wrapper and the two light-background panels; the site's dark theme was leaving light text on them (white-on-white). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019P2Yg2vt6iV6w1rAL1wS4h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Replaces the 7 hand-authored MailerSend templates driven by
airtable-mailersend-emails.jswith a repo-owned render endpoint, matrixed on intent × chapter × language instead of one template ID per combination. Chapter content (leader, links) now comes live from the same Airtable "National Groups" data/api/national-groupsalready uses, instead of being hand-copied per template and drifting out of date.src/lib/server/onboardingEmail/— chapter lookup, intent copy (act-now / keep-informed / volunteer / lead), language handling (en/es/fr), HTML + plain-text rendererssrc/routes/api/onboarding-email— bearer-secret POST endpoint, returns{subject, html, text}. Render-only — never sends mail itself, never touches the MailerSend API key.src/routes/api/unsubscribe— new. No unsubscribe mechanism existed in any of the 7 templates before this (MailerSend'sunsubscribe_rate: 0reflected "no link offered," not "no one wants out"). Flips the MembersEmail subscriptioncheckbox and suppresses on both MailerSend domains.src/routes/onboarding-email-preview— dev-only QA page (404s outsidedev), renders a sample of intent × chapter × language combos.airtable-mailersend-emails.js— tries the new endpoint first; on any failure (network error, timeout, non-OK response, missing fields) falls through to the originaltemplate_idlogic, byte-for-byte unchanged. Worst case is today's behavior.email-templates/— raw MailerSend payloads for all 7 old templates, kept as migration reference and to document routing gaps found along the way (CANADA_FR_TEMPLATEwas unreachable in practice —languagesis always empty at send time;Leadintent normally hands off via amailto:link rather than a form submission, perdocs/join-form-flow.md).Please manually check / review / adjust
eslocale exists elsewhere in the codebase, and the old Spanish template only ever covered the volunteer case. Needs a native/fluent Spanish speaker from the org to read it.messages.ts) uses informal tu; the old Canada-FR MailerSend template used formal vous. Matched vous to stay consistent with the reviewed body copy — meaning the email doesn't literally echo the join form's wording. Confirm that's the right call./onboarding-email-previewand confirm the new subject is acceptable.NationalGroup/Airtable)./onboarding-email-previewlocally and look at real rendered output — only agent/automated smoke-testing has happened so far, no human has eyeballed it.Pre-merge / pre-launch checklist
ONBOARDING_EMAIL_RENDER_SECRETandMAILERSEND_API_KEYin prod (Netlify env vars) — both currently placeholder-only in.env(gitignored, no real values committed).Onboarding email render secret) and the optionalrender_endpoint_urloverride in the Airtable automation's script input config panel — the script references these by name but Airtable-side mapping isn't part of this repo.template_id) sends get any automatic unsubscribe-footer/compliance injection. Docs suggest that's template-engine-only, so the shell here builds its own unsubscribe link + physical address (Box C5957, Kwikstaartlaan 42, 3704GS Zeist, The Netherlands) — never got a definitive answer from MailerSend, worth chasing before this goes live.🤖 Generated with Claude Code
https://claude.ai/code/session_01GtQwhn5Fb9eiC4LsmRs47q