fix(email): include course name in student welcome subject (#739)
* fix(email): include course name in student welcome subject The studentCourseWelcome template used a static subject, so bulk course assignments produced identical inbox titles. Resolve the subject from courseName at send time and sanitize it like other email headers. Co-authored-by: Rotimi Best <[email protected]> * fix(email): use course-only subject for student welcome email Co-authored-by: Rotimi Best <[email protected]> --------- Co-authored-by: Cursor Agent <[email protected]> Co-authored-by: Rotimi Best <[email protected]>
This commit is contained in:
co-authored by
Rotimi Best
Cursor Agent
parent
51f90bf450
commit
8288f157b3
@@ -3,6 +3,7 @@ import * as z from 'zod';
|
||||
import type { EmailResponse } from '../utils/types';
|
||||
import type { SendTemplateConfig } from './types';
|
||||
import { deliverEmail } from '../send';
|
||||
import { sanitizeEmailSubject } from '../utils/functions/email-helpers';
|
||||
|
||||
/**
|
||||
* Send a single email using a template
|
||||
@@ -13,10 +14,14 @@ export async function sendTemplateEmail<TSchema extends z.ZodType>(
|
||||
): Promise<EmailResponse[]> {
|
||||
const content = config.template.render(config.fields);
|
||||
|
||||
const resolvedSubject =
|
||||
config.subject ??
|
||||
(typeof config.template.subject === 'function' ? config.template.subject(config.fields) : config.template.subject);
|
||||
|
||||
return deliverEmail([
|
||||
{
|
||||
to: config.to,
|
||||
subject: config.subject ?? config.template.subject,
|
||||
subject: sanitizeEmailSubject(resolvedSubject),
|
||||
content,
|
||||
from: config.from ?? config.template.from,
|
||||
replyTo: config.replyTo ?? config.template.replyTo,
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { EmailId } from '../utils/types';
|
||||
* Base email template interface
|
||||
*/
|
||||
export interface EmailTemplate<TSchema extends z.ZodType = z.ZodType> {
|
||||
subject: string;
|
||||
subject: string | ((fields: z.infer<TSchema>) => string);
|
||||
schema: TSchema;
|
||||
render: (fields: z.infer<TSchema>) => string;
|
||||
from?: string;
|
||||
@@ -47,7 +47,7 @@ export interface SendTemplateConfig<TSchema extends z.ZodType = z.ZodType> exten
|
||||
*/
|
||||
export interface DefineEmailConfig<TSchema extends z.ZodType = z.ZodType> {
|
||||
id: EmailId;
|
||||
subject: string;
|
||||
subject: string | ((fields: z.infer<TSchema>) => string);
|
||||
schema: TSchema;
|
||||
render: (fields: z.infer<TSchema>) => string;
|
||||
from?: string;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ZEmailBranding } from '../core/branding';
|
||||
|
||||
export const studentCourseWelcomeEmail = defineEmail({
|
||||
id: 'studentCourseWelcome',
|
||||
subject: 'You have access to a course — sign in to get started',
|
||||
subject: (fields) => `You have access to ${fields.courseName} course`,
|
||||
schema: z.object({
|
||||
orgName: z.string().min(1),
|
||||
courseName: z.string().min(1),
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { studentCourseWelcomeEmail } from '../src/emails/student-course-welcome';
|
||||
|
||||
describe('studentCourseWelcomeEmail', () => {
|
||||
it('includes the course name in the subject', () => {
|
||||
const resolveSubject = studentCourseWelcomeEmail.template.subject;
|
||||
|
||||
expect(typeof resolveSubject).toBe('function');
|
||||
expect((resolveSubject as (fields: { courseName: string }) => string)({ courseName: 'React Basics' })).toBe(
|
||||
'You have access to React Basics course'
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user