feat: agregar soporte para plantillas de correo electrónico y contexto en el envío de notificaciones

This commit is contained in:
2026-08-20 11:38:50 -03:00
parent e4441fa476
commit b6399d28fc
13 changed files with 174 additions and 11 deletions
@@ -91,12 +91,17 @@ export async function dispatchEmail(params: {
systemToken: string;
type: string;
email: string;
subject: string;
message: string;
subject?: string;
message?: string;
templateId?: string;
context?: Record<string, unknown>;
}): Promise<DispatchResult> {
try {
if (!params.email.trim() || !params.subject.trim() || !params.message.trim()) {
return { success: false, error: "Email dispatch requires email, subject, and message" };
const hasContent = Boolean(params.subject?.trim() && params.message?.trim());
const hasTemplate = Boolean(params.templateId?.trim() && params.context);
if (!params.email.trim() || (!hasContent && !hasTemplate)) {
return { success: false, error: "Email dispatch requires email and either subject/message or templateId/context" };
}
// Send worker-rendered content directly so cancellation jobs survive appointment deletion.
@@ -106,8 +111,10 @@ export async function dispatchEmail(params: {
{
systemToken: params.systemToken,
email: params.email,
subject: params.subject,
message: params.message,
...(params.subject ? { subject: params.subject } : {}),
...(params.message ? { message: params.message } : {}),
...(params.templateId ? { templateId: params.templateId } : {}),
...(params.context ? { context: params.context } : {}),
},
{
headers: { "Content-Type": "application/json" },
@@ -202,6 +202,8 @@ export class JobProcessor {
email: content.email || "",
subject: content.subject,
message: content.message,
templateId: content.emailTemplateId,
context: content.emailContext,
});
break;
case "system":
@@ -45,6 +45,8 @@ export interface ResolvedNotificationContent {
companyOwnerId?: string;
subject: string;
message: string;
emailTemplateId?: string;
emailContext?: Record<string, unknown>;
}
const appointmentSchema = new Schema(
@@ -169,6 +171,8 @@ export async function resolveNotificationContent(job: INotificationJobDocument):
companyOwnerId: companyOwnerId ? String(companyOwnerId) : undefined,
subject: fallbackSubject.trim(),
message: fallbackMessage.trim(),
emailTemplateId: job.payload.emailTemplateId?.trim(),
emailContext: job.payload.emailContext,
};
}
}
@@ -15,6 +15,8 @@ export interface NotificationJobPayload {
message?: string;
emailSubject?: string;
emailMessage?: string;
emailTemplateId?: string;
emailContext?: Record<string, unknown>;
wapMessage?: string;
systemSubject?: string;
systemMessage?: string;
@@ -43,6 +43,30 @@ describe("ChannelDispatchers", () => {
);
});
it("sends email template data when provided", async () => {
await dispatchEmail({
appointmentId: "appointment-1",
systemToken: "system-token",
type: "reminder",
email: "client@example.com",
templateId: "template-1",
context: { username: "Client Name" },
});
expect(mockedAxios.post).toHaveBeenCalledWith(
"https://api.example.com/notifications/send-email",
{
systemToken: "system-token",
email: "client@example.com",
templateId: "template-1",
context: { username: "Client Name" },
},
{
headers: { "Content-Type": "application/json" },
}
);
});
it("sends the job type in the WhatsApp system notification payload", async () => {
await dispatchWhatsApp({
companyId: "company-1",