From 80725ed81fe73e74a59d9adafff9f74bcb6c9970 Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Sat, 11 Apr 2026 01:59:03 +0700 Subject: [PATCH] feat(notifications): add saved search email alert templates Add the two missing Handlebars templates (saved_search_alert and saved_search_digest) that are referenced by the real-time event handler and daily digest cron but were never defined, causing a runtime crash. Includes corresponding unit tests. Co-Authored-By: Paperclip --- .../__tests__/template.service.spec.ts | 48 ++++++++++++++++++- .../services/template.service.ts | 37 ++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/apps/api/src/modules/notifications/infrastructure/__tests__/template.service.spec.ts b/apps/api/src/modules/notifications/infrastructure/__tests__/template.service.spec.ts index 282bf90..af3bb11 100644 --- a/apps/api/src/modules/notifications/infrastructure/__tests__/template.service.spec.ts +++ b/apps/api/src/modules/notifications/infrastructure/__tests__/template.service.spec.ts @@ -39,10 +39,52 @@ describe('TemplateService', () => { expect(service.hasTemplate('unknown.template')).toBe(false); }); - it('getTemplateKeys returns all 9 template keys', () => { + it('render returns rendered subject and body for saved_search_alert template', () => { + const result = service.render('saved_search_alert', { + userName: 'Nguyễn Văn A', + searchName: 'Chung cư Quận 7', + listingTitle: 'Căn hộ 2PN tầng cao view sông', + listingPrice: '3.500.000.000', + listingDistrict: 'Quận 7', + listingCity: 'Hồ Chí Minh', + listingUrl: '/listings/abc123', + }); + + expect(result.subject).toBe('Tin mới phù hợp tìm kiếm "Chung cư Quận 7"'); + expect(result.body).toContain('Nguyễn Văn A'); + expect(result.body).toContain('Chung cư Quận 7'); + expect(result.body).toContain('Căn hộ 2PN tầng cao view sông'); + expect(result.body).toContain('3.500.000.000 VNĐ'); + expect(result.body).toContain('Quận 7'); + expect(result.body).toContain('/listings/abc123'); + }); + + it('render returns rendered subject and body for saved_search_digest template', () => { + const result = service.render('saved_search_digest', { + userName: 'Trần Thị B', + searchName: 'Villa Thủ Đức', + matchCount: 3, + listings: [ + { title: 'Villa 1', price: '5.000.000.000', district: 'Thủ Đức', city: 'Hồ Chí Minh', url: '/listings/1' }, + { title: 'Villa 2', price: '7.000.000.000', district: 'Thủ Đức', city: 'Hồ Chí Minh', url: '/listings/2' }, + ], + }); + + expect(result.subject).toBe('3 bất động sản mới khớp tìm kiếm "Villa Thủ Đức"'); + expect(result.body).toContain('Trần Thị B'); + expect(result.body).toContain('Villa Thủ Đức'); + expect(result.body).toContain('3'); + expect(result.body).toContain('Villa 1'); + expect(result.body).toContain('5.000.000.000 VNĐ'); + expect(result.body).toContain('Villa 2'); + expect(result.body).toContain('/listings/1'); + expect(result.body).toContain('/listings/2'); + }); + + it('getTemplateKeys returns all 11 template keys', () => { const keys = service.getTemplateKeys(); - expect(keys).toHaveLength(9); + expect(keys).toHaveLength(11); expect(keys).toContain('user.registered'); expect(keys).toContain('agent.verified'); expect(keys).toContain('listing.approved'); @@ -52,5 +94,7 @@ describe('TemplateService', () => { expect(keys).toContain('password.reset'); expect(keys).toContain('payment.confirmed'); expect(keys).toContain('subscription.expiring'); + expect(keys).toContain('saved_search_alert'); + expect(keys).toContain('saved_search_digest'); }); }); diff --git a/apps/api/src/modules/notifications/infrastructure/services/template.service.ts b/apps/api/src/modules/notifications/infrastructure/services/template.service.ts index 563a0a4..e1b3465 100644 --- a/apps/api/src/modules/notifications/infrastructure/services/template.service.ts +++ b/apps/api/src/modules/notifications/infrastructure/services/template.service.ts @@ -75,6 +75,43 @@ const TEMPLATES: Record = { body: `

Gói đăng ký đã bị huỷ

Gói {{planTier}} của bạn đã bị huỷ.

Bạn có thể đăng ký lại bất cứ lúc nào để tiếp tục sử dụng đầy đủ tính năng.

+

Trân trọng,
Đội ngũ GoodGo

`, + }, + 'saved_search_alert': { + subject: 'Tin mới phù hợp tìm kiếm "{{searchName}}"', + body: `

Xin chào {{userName}}!

+

Có tin đăng mới phù hợp với tìm kiếm đã lưu "{{searchName}}" của bạn:

+ + + + + + + +
{{listingTitle}}
+ Giá: {{listingPrice}} VNĐ
+ Khu vực: {{listingDistrict}}, {{listingCity}} +
+

Xem chi tiết

+

Bạn có thể tắt thông báo cho tìm kiếm này trong phần Tìm kiếm đã lưu.

+

Trân trọng,
Đội ngũ GoodGo

`, + }, + 'saved_search_digest': { + subject: '{{matchCount}} bất động sản mới khớp tìm kiếm "{{searchName}}"', + body: `

Xin chào {{userName}}!

+

{{matchCount}} bất động sản mới phù hợp với tìm kiếm đã lưu "{{searchName}}" của bạn:

+ + {{#each listings}} + + + + {{/each}} +
+ {{title}}
+ Giá: {{price}} VNĐ · {{district}}, {{city}} +
+

Xem tất cả kết quả

+

Bạn có thể tắt thông báo cho tìm kiếm này trong phần Tìm kiếm đã lưu.

Trân trọng,
Đội ngũ GoodGo

`, }, };