From 16e38b6569fa359c97aade0c0729427e4143a50f Mon Sep 17 00:00:00 2001 From: Brandon Korous Date: Fri, 10 Apr 2026 12:44:52 -0700 Subject: [PATCH] feat: enhance application detail and overview with recruiter information and update chat email template --- .../components/shared/application-detail.tsx | 2 +- .../shared/application-overview-tab.tsx | 2 +- .../applications/views/detail.repository.ts | 1 + .../src/services/chat/service.ts | 29 ++++--------- .../src/templates/chat/index.ts | 41 +++++++++++++++++++ .../src/templates/index.ts | 1 + 6 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 services/notification-service/src/templates/chat/index.ts diff --git a/apps/portal/src/app/portal/applications/components/shared/application-detail.tsx b/apps/portal/src/app/portal/applications/components/shared/application-detail.tsx index cacce6d62..35b44faba 100644 --- a/apps/portal/src/app/portal/applications/components/shared/application-detail.tsx +++ b/apps/portal/src/app/portal/applications/components/shared/application-detail.tsx @@ -29,7 +29,7 @@ export function DetailLoader({ const client = createAuthenticatedClient(token); const response = await client.get( `/applications/${id}/view/detail`, - { params: { include: 'timeline' } }, + { params: { include: 'timeline,recruiter' } }, ); if (!signal?.cancelled) setApplication(response.data || null); } catch (error) { diff --git a/apps/portal/src/app/portal/applications/components/shared/application-overview-tab.tsx b/apps/portal/src/app/portal/applications/components/shared/application-overview-tab.tsx index 820e91fef..a9d647bda 100644 --- a/apps/portal/src/app/portal/applications/components/shared/application-overview-tab.tsx +++ b/apps/portal/src/app/portal/applications/components/shared/application-overview-tab.tsx @@ -27,7 +27,7 @@ export function ApplicationOverviewTab({ application }: OverviewTabProps) { ? recruiterName.split(" ").map((n: string) => n[0]).join("").toUpperCase() : "?"; - const companyRecruiter = (job as any)?.company_recruiter; + const companyRecruiter = (application as any)?.company_recruiter; const companyRecruiterName = companyRecruiter?.user?.name || null; const companyRecruiterEmail = companyRecruiter?.user?.email || null; const companyRecruiterInitials = companyRecruiterName diff --git a/services/ats-service/src/v3/applications/views/detail.repository.ts b/services/ats-service/src/v3/applications/views/detail.repository.ts index 3bcdf490f..b611cb22a 100644 --- a/services/ats-service/src/v3/applications/views/detail.repository.ts +++ b/services/ats-service/src/v3/applications/views/detail.repository.ts @@ -26,6 +26,7 @@ export class ApplicationDetailRepository { if (include?.includes('recruiter')) { selectClause += `,recruiter:recruiters!candidate_recruiter_id(id, bio, phone, tagline, specialties, status, user_id, user:users!recruiters_user_id_fkey(name, email))`; + selectClause += `,company_recruiter:recruiters!applications_company_recruiter_id_fkey(id, bio, phone, tagline, specialties, status, user_id, user:users!recruiters_user_id_fkey(name, email))`; } if (include?.includes('audit') || include?.includes('timeline')) { selectClause += `,audit_log:application_audit_log!application_audit_log_application_id_fkey(id, action, performed_by_user_id, performed_by_role, company_id, old_value, new_value, metadata, ip_address, user_agent, created_at)`; diff --git a/services/notification-service/src/services/chat/service.ts b/services/notification-service/src/services/chat/service.ts index a91c29e1d..92bc17831 100644 --- a/services/notification-service/src/services/chat/service.ts +++ b/services/notification-service/src/services/chat/service.ts @@ -1,7 +1,7 @@ import { Resend } from 'resend'; import { Logger } from '@splits-network/shared-logging'; import { NotificationRepository } from '../../repository.js'; -import type { EmailSource } from '../../templates/base.js'; +import { chatMessageEmail } from '../../templates/chat/index.js'; export interface ChatMessageEmailData { recipient: string; @@ -21,24 +21,6 @@ export class ChatEmailService { private logger: Logger ) {} - private renderChatEmail(data: ChatMessageEmailData): string { - const preview = data.preview ? data.preview : 'New message'; - return ` - - -

You have a new message

-

${data.senderName} sent you a message:

-

${preview}

-

- - View conversation - -

- - - `.trim(); - } - async sendNewMessageEmail(data: ChatMessageEmailData): Promise { const subject = `New message from ${data.senderName}`; @@ -65,11 +47,18 @@ export class ChatEmailService { }); try { + const html = chatMessageEmail({ + senderName: data.senderName, + recipientName: data.recipientName, + preview: data.preview, + conversationUrl: data.conversationUrl, + }); + const { data: result, error } = await this.resend.emails.send({ from: this.fromEmail, to: data.recipient, subject, - html: this.renderChatEmail(data), + html, }); if (error) { diff --git a/services/notification-service/src/templates/chat/index.ts b/services/notification-service/src/templates/chat/index.ts new file mode 100644 index 000000000..758a355fa --- /dev/null +++ b/services/notification-service/src/templates/chat/index.ts @@ -0,0 +1,41 @@ +/** + * Chat Email Templates + * Notification emails for new chat messages. + */ + +import { baseEmailTemplate, EmailSource } from '../base.js'; +import { heading, paragraph, button, infoCard } from '../components.js'; + +export interface ChatMessageEmailData { + senderName: string; + recipientName: string; + preview?: string | null; + conversationUrl: string; + source?: EmailSource; +} + +export function chatMessageEmail(data: ChatMessageEmailData): string { + const preview = data.preview || 'New message'; + + const content = ` +${heading({ level: 1, text: 'New message', kicker: 'MESSAGING' })} + +${paragraph(`${data.senderName} sent you a message:`)} + +${infoCard({ + title: 'Message Preview', + items: [ + { label: 'From', value: data.senderName, highlight: true }, + { label: 'Message', value: preview }, + ], +})} + +${button({ href: data.conversationUrl, text: 'View Conversation \u2192', variant: 'primary' })} + `.trim(); + + return baseEmailTemplate({ + preheader: `${data.senderName}: ${preview}`, + content, + source: data.source || 'portal', + }); +} diff --git a/services/notification-service/src/templates/index.ts b/services/notification-service/src/templates/index.ts index 99315a8c1..7e5c3bbbc 100644 --- a/services/notification-service/src/templates/index.ts +++ b/services/notification-service/src/templates/index.ts @@ -12,3 +12,4 @@ export * from './components.js'; // Domain-specific templates export * from './applications/index.js'; export * from './placements/index.js'; +export * from './chat/index.js';