Add study user re-engagement reminder emails; v1.1.19
Sends 7/14/30-day inactivity emails to study users who haven't logged in, with one-click HMAC-signed unsubscribe and automatic re-arm on next login. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -584,3 +584,61 @@ export async function sendStudyReminderEmail(email, displayName, studyTitle, sec
|
||||
console.error('[study-reminder] send exception:', err)
|
||||
}
|
||||
}
|
||||
|
||||
const REENGAGEMENT_COPY = {
|
||||
'7d': {
|
||||
subject: 'Your study is waiting for you',
|
||||
eyebrow: 'Come Back',
|
||||
headline: 'Pick up where you left off',
|
||||
body: "It's been a week — your progress is saved and your next lesson is ready whenever you are.",
|
||||
cta: 'Continue Studying',
|
||||
},
|
||||
'14d': {
|
||||
subject: "Don't lose your momentum",
|
||||
eyebrow: 'Still With You',
|
||||
headline: 'Your spot is still saved',
|
||||
body: 'Two weeks have passed, but every note and completed lesson is still right there. A little each day adds up.',
|
||||
cta: 'Return to Your Study',
|
||||
},
|
||||
'30d': {
|
||||
subject: 'Your progress is still here',
|
||||
eyebrow: 'We Saved Your Spot',
|
||||
headline: "It's been a month — come back anytime",
|
||||
body: 'Your study progress is still intact and waiting. There\'s no deadline — come back whenever you\'re ready.',
|
||||
cta: 'Open Your Study',
|
||||
},
|
||||
}
|
||||
|
||||
export async function sendStudyReengagementEmail(email, displayName, studyTitle, studyUrl, tier, unsubUrl) {
|
||||
if (!process.env.RESEND_API_KEY) return
|
||||
const copy = REENGAGEMENT_COPY[tier]
|
||||
if (!copy) return
|
||||
try {
|
||||
const resend = new Resend(process.env.RESEND_API_KEY)
|
||||
const namePart = typeof displayName === 'string' && displayName.trim() ? displayName.trim() : 'friend'
|
||||
const subject = `${copy.subject}${studyTitle ? ` — ${studyTitle}` : ''}`
|
||||
const bodyHtml = (
|
||||
`<p style="margin:0 0 16px;">Hi <strong style="color:#f0ead8;">${escapeHtml(namePart)}</strong>,</p>` +
|
||||
`<p style="margin:0 0 16px;">${escapeHtml(copy.body)}${studyTitle ? ` Your current study: <strong style="color:#f0ead8;">${escapeHtml(studyTitle)}</strong>.` : ''}</p>`
|
||||
)
|
||||
const unsubLine = unsubUrl ? `<p style="margin:16px 0 0;font-size:11px;color:#7a7060;">Not interested? <a href="${escapeHtml(unsubUrl)}" style="color:#7a7060;">Unsubscribe from these reminders.</a></p>` : ''
|
||||
const footerHtml = `<p style="margin:0;font-family:Georgia,serif;font-size:12px;font-weight:300;color:#7a7060;line-height:1.6;">Grace and peace,<br/>Verse by Verse with Nate</p>${unsubLine}`
|
||||
const { error } = await resend.emails.send({
|
||||
from: process.env.RESEND_FROM ?? DEFAULT_RESEND_FROM,
|
||||
to: [email],
|
||||
subject,
|
||||
text: `Hi ${namePart},\n\n${copy.body}${studyTitle ? ` Your current study: ${studyTitle}.` : ''}\n\n${studyUrl}\n\nGrace and peace,\nVerse by Verse with Nate${unsubUrl ? `\n\nUnsubscribe: ${unsubUrl}` : ''}`,
|
||||
html: buildBrandedEmailHtml({
|
||||
title: copy.headline,
|
||||
eyebrow: copy.eyebrow,
|
||||
bodyHtml,
|
||||
ctaLabel: copy.cta,
|
||||
ctaUrl: studyUrl,
|
||||
footerHtml,
|
||||
}),
|
||||
})
|
||||
if (error) console.error('[study-reengagement] send error:', error)
|
||||
} catch (err) {
|
||||
console.error('[study-reengagement] send exception:', err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user