Skip to content

Commit 1418cd9

Browse files
committedAug 15, 2021
Renamed EmailNotificationService.js to NotificationsSchedulerService.js
Modified templates to add links Adding web notifications for post interview candidate action and upcoming resource booking expiry Tasks 476 and 477#issuecomment-898886697
1 parent 48b3625 commit 1418cd9

File tree

5 files changed

+140
-81
lines changed

5 files changed

+140
-81
lines changed
 

‎app.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const eventHandlers = require('./src/eventHandlers')
1515
const interviewService = require('./src/services/InterviewService')
1616
const { processScheduler } = require('./src/services/PaymentSchedulerService')
1717
const { sendSurveys } = require('./src/services/SurveyService')
18-
const emailNotificationService = require('./src/services/EmailNotificationService')
18+
const notificationSchedulerService = require('./src/services/NotificationsSchedulerService')
1919

2020
// setup express app
2121
const app = express()
@@ -105,11 +105,11 @@ const server = app.listen(app.get('port'), () => {
105105
// schedule payment processing
106106
schedule.scheduleJob(config.PAYMENT_PROCESSING.CRON, processScheduler)
107107

108-
schedule.scheduleJob(config.CRON_CANDIDATE_REVIEW, emailNotificationService.sendCandidatesAvailableEmails)
109-
schedule.scheduleJob(config.CRON_INTERVIEW_COMING_UP, emailNotificationService.sendInterviewComingUpEmails)
110-
schedule.scheduleJob(config.CRON_INTERVIEW_COMPLETED, emailNotificationService.sendInterviewCompletedEmails)
111-
schedule.scheduleJob(config.CRON_POST_INTERVIEW, emailNotificationService.sendPostInterviewActionEmails)
112-
schedule.scheduleJob(config.CRON_UPCOMING_RESOURCE_BOOKING, emailNotificationService.sendResourceBookingExpirationEmails)
108+
schedule.scheduleJob(config.CRON_CANDIDATE_REVIEW, notificationSchedulerService.sendCandidatesAvailableNotifications)
109+
schedule.scheduleJob(config.CRON_INTERVIEW_COMING_UP, notificationSchedulerService.sendInterviewComingUpNotifications)
110+
schedule.scheduleJob(config.CRON_INTERVIEW_COMPLETED, notificationSchedulerService.sendInterviewCompletedNotifications)
111+
schedule.scheduleJob(config.CRON_POST_INTERVIEW, notificationSchedulerService.sendPostInterviewActionNotifications)
112+
schedule.scheduleJob(config.CRON_UPCOMING_RESOURCE_BOOKING, notificationSchedulerService.sendResourceBookingExpirationNotifications)
113113
})
114114

115115
if (process.env.NODE_ENV === 'test') {

‎config/email_template.config.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ module.exports = {
112112
*/
113113
notificationEmailTemplates: {
114114
'taas.notification.job-candidate-resume-viewed': {
115-
subject: 'Topcoder - job candidate resume viewed',
115+
subject: 'Topcoder - Client View Resume for Job {{jobName}}',
116116
body: '',
117117
recipients: [],
118118
from: config.NOTIFICATION_SENDER_EMAIL,
@@ -161,14 +161,14 @@ module.exports = {
161161
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
162162
},
163163
'taas.notification.team-created': {
164-
subject: 'Topcoder - Team Created',
164+
subject: 'Topcoder - New Team {{teamName}} Created',
165165
body: '',
166166
recipients: [],
167167
from: config.NOTIFICATION_SENDER_EMAIL,
168168
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
169169
},
170170
'taas.notification.job-created': {
171-
subject: 'Topcoder - Job Created',
171+
subject: 'Topcoder - New Job {{jobTitle}} Created in Team {{teamName}}',
172172
body: '',
173173
recipients: [],
174174
from: config.NOTIFICATION_SENDER_EMAIL,
@@ -182,14 +182,14 @@ module.exports = {
182182
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
183183
},
184184
'taas.notification.job-candidate-selected': {
185-
subject: 'Topcoder - Job Candidate Selected',
185+
subject: 'Topcoder - Job Candidate {{userHandle}} Selected for {{jobTitle}} in Team {{teamName}}',
186186
body: '',
187187
recipients: config.NOTIFICATION_OPS_EMAILS,
188188
from: config.NOTIFICATION_SENDER_EMAIL,
189189
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
190190
},
191191
'taas.notification.resource-booking-placed': {
192-
subject: 'Topcoder - Resource Booking Placed',
192+
subject: 'Topcoder - Resource Booking {{userHandle}} Placed for Job {{jobTitle}} in Team {{teamName}}',
193193
body: '',
194194
recipients: [],
195195
from: config.NOTIFICATION_SENDER_EMAIL,

‎data/notifications-email-template.html

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -101,25 +101,23 @@
101101
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">Job title</td>
102102
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">No of resource bookings</td>
103103
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">Candidates</td>
104-
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">Status</td>
104+
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">Review</td>
105105
</tr>
106106
{{#each teamJobs}}
107107
<tr style="border:1px solid black;border-collapse:collapse">
108-
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.title}}</td>
108+
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
109+
<a href={{this.jobUrl}} target="_blank" rel="noopener noreferrer">{{this.title}}</a>
110+
</td>
109111
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.nResourceBookings}}</td>
110112
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
111113
<ul style="list-style-type:none;margin:0;padding:0;">
112114
{{#each this.jobCandidates}}
113-
<li><a href={{../reviewLink}} target="_blank" rel="noopener noreferrer">{{this.handle}}</a></li>
115+
<li>{{this.handle}}</li>
114116
{{/each}}
115117
</ul>
116118
</td>
117119
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
118-
<ul style="list-style-type:none;margin:0;padding:0;">
119-
{{#each this.jobCandidates}}
120-
<li>{{this.status}}</li>
121-
{{/each}}
122-
</ul>
120+
<a href={{this.reviewLink}} target="_blank" rel="noopener noreferrer">Link</a>
123121
</td>
124122
</tr>
125123
{{/each}}
@@ -136,7 +134,9 @@
136134
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">Date and Time</td>
137135
</tr>
138136
<tr>
139-
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.jobTitle}}</td>
137+
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
138+
<a href={{this.jobUrl}} target="_blank" rel="noopener noreferrer">{{this.jobTitle}}</a>
139+
</td>
140140
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.handle}}</td>
141141
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
142142
<a href={{this.interviewLink}} target="_blank" rel="noopener noreferrer">Link</a></td>
@@ -160,7 +160,9 @@
160160
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">Interviews</td>
161161
</tr>
162162
<tr>
163-
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.jobTitle}}</td>
163+
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
164+
<a href={{this.jobUrl}} target="_blank" rel="noopener noreferrer">{{this.jobTitle}}</a>
165+
</td>
164166
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.startTime}}</td>
165167
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
166168
<a href={{this.interviewLink}} target="_blank" rel="noopener noreferrer">Link</a></td>
@@ -178,7 +180,9 @@
178180
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">Interviews</td>
179181
</tr>
180182
<tr>
181-
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.jobTitle}}</td>
183+
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
184+
<a href={{this.jobUrl}} target="_blank" rel="noopener noreferrer">{{this.jobTitle}}</a>
185+
</td>
182186
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.handle}}</td>
183187
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.startTime}}</td>
184188
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
@@ -205,7 +209,9 @@
205209
</tr>
206210
{{#each teamInterviews}}
207211
<tr>
208-
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.jobTitle}}</td>
212+
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
213+
<a href={{this.jobUrl}} target="_blank" rel="noopener noreferrer">{{this.jobTitle}}</a>
214+
</td>
209215
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.handle}}</td>
210216
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.startTime}}</td>
211217
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
@@ -224,7 +230,7 @@
224230

225231
{{#if notificationType.upcomingResourceBookingExpiration}}
226232
<span style="font-weight:bold;">Team Name:</span>
227-
{{teamName}}
233+
<a href={{this.teamUrl}} target="_blank" rel="noopener noreferrer">{{teamName}}</a>
228234
<table style="font-size:13px;border:1px solid black;border-collapse:collapse;width:95%;">
229235
<tr style="font-weight:bold;border:1px solid black;border-collapse:collapse">
230236
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">Job title</td>
@@ -233,8 +239,12 @@
233239
</tr>
234240
{{#each teamResourceBookings}}
235241
<tr>
236-
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.jobTitle}}</td>
237-
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.handle}}</td>
242+
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
243+
<a href={{this.jobUrl}} target="_blank" rel="noopener noreferrer">{{this.jobTitle}}</a>
244+
</td>
245+
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">
246+
<a href={{this.resourceBookingUrl}} target="_blank" rel="noopener noreferrer">{{this.handle}}</a>
247+
</td>
238248
<td style="border:1px solid black;border-collapse:collapse;text-align:center;line-height:1.5;">{{this.endDate}}</td>
239249
</tr>
240250
{{/each}}
@@ -246,7 +256,7 @@
246256

247257
Your resume for the job "{{jobName}}" has been viewed by the client. <br/>
248258
{{/if}}
249-
259+
250260
{{#if notificationType.newTeamCreated}}
251261
<span style="font-weight:bold;">Team:</span>
252262
<a href={{teamUrl}} target="_blank" rel="noopener noreferrer">{{teamName}}</a>

‎src/services/JobCandidateService.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const logger = require('../common/logger')
1414
const errors = require('../common/errors')
1515
const models = require('../models')
1616
const JobService = require('./JobService')
17-
const EmailNotificationService = require('./EmailNotificationService')
17+
const NotificationSchedulerService = require('./NotificationsSchedulerService')
1818
const JobCandidate = models.JobCandidate
1919
const esClient = helper.getESClient()
2020

@@ -376,7 +376,7 @@ async function downloadJobCandidateResume (currentUser, id) {
376376
const { handle } = await helper.getUserById(jobCandidate.userId, true)
377377
const { email } = await helper.getMemberDetailsByHandle(handle)
378378

379-
await EmailNotificationService.sendEmail(currentUser, {
379+
await NotificationSchedulerService.sendNotification(currentUser, {
380380
template: 'taas.notification.job-candidate-resume-viewed',
381381
recipients: [email],
382382
data: {

‎src/services/EmailNotificationService.js renamed to ‎src/services/NotificationsSchedulerService.js

Lines changed: 101 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Email notification service - has the cron handlers for sending different types of email notifications
2+
* Notification scheduler service - has the cron handlers for sending different types of notifications (email, web etc)
33
*/
44
const _ = require('lodash')
55
const { Op } = require('sequelize')
@@ -15,9 +15,9 @@ const constants = require('../../app-constants')
1515
const logger = require('../common/logger')
1616

1717
const localLogger = {
18-
debug: (message, context) => logger.debug({ component: 'EmailNotificationService', context, message }),
19-
error: (message, context) => logger.error({ component: 'EmailNotificationService', context, message }),
20-
info: (message, context) => logger.info({ component: 'EmailNotificationService', context, message })
18+
debug: (message, context) => logger.debug({ component: 'NotificationSchedulerService', context, message }),
19+
error: (message, context) => logger.error({ component: 'NotificationSchedulerService', context, message }),
20+
info: (message, context) => logger.info({ component: 'NotificationSchedulerService', context, message })
2121
}
2222

2323
const emailTemplates = helper.getEmailTemplatesForKey('notificationEmailTemplates')
@@ -88,7 +88,8 @@ async function getDataForInterview (interview, jobCandidate, job) {
8888

8989
const interviewLink = `${config.TAAS_APP_URL}/${job.projectId}/positions/${job.id}/candidates/interviews`
9090
const guestName = _.isEmpty(interview.guestNames) ? '' : interview.guestNames[0]
91-
const startTime = interview.startTimestamp ? interview.startTimestamp.toUTCString() : ''
91+
const startTime = interview.startTimestamp ? helper.formatDateTimeEDT(interview.startTimestamp) : ''
92+
const jobUrl = `${config.TAAS_APP_URL}/${job.projectId}/positions/${job.id}`
9293

9394
return {
9495
jobTitle: job.title,
@@ -99,14 +100,15 @@ async function getDataForInterview (interview, jobCandidate, job) {
99100
attendees: interview.guestNames,
100101
startTime: startTime,
101102
duration: interview.duration,
102-
interviewLink
103+
interviewLink,
104+
jobUrl
103105
}
104106
}
105107

106108
/**
107-
* Sends email notifications to all the teams which have candidates available for review
109+
* Sends notifications to all the teams which have candidates available for review
108110
*/
109-
async function sendCandidatesAvailableEmails () {
111+
async function sendCandidatesAvailableNotifications () {
110112
const jobsDao = await Job.findAll({
111113
include: [{
112114
model: JobCandidate,
@@ -121,7 +123,7 @@ async function sendCandidatesAvailableEmails () {
121123

122124
const projectIds = _.uniq(_.map(jobs, job => job.projectId))
123125

124-
localLogger.debug(`[sendCandidatesAvailableEmails]: Found ${projectIds.length} projects with Job Candidates awaiting for review.`)
126+
localLogger.debug(`[sendCandidatesAvailableNotifications]: Found ${projectIds.length} projects with Job Candidates awaiting for review.`)
125127

126128
// for each unique project id, send an email
127129
let sentCount = 0
@@ -156,15 +158,17 @@ async function sendCandidatesAvailableEmails () {
156158
}
157159
})
158160

161+
const jobUrl = `${config.TAAS_APP_URL}/${projectId}/positions/${projectJob.id}`
159162
teamJobs.push({
160163
title: projectJob.title,
161164
nResourceBookings,
162165
jobCandidates,
163-
reviewLink
166+
reviewLink,
167+
jobUrl
164168
})
165169
}
166170

167-
sendEmail({}, {
171+
sendNotification({}, {
168172
template: 'taas.notification.candidates-available-for-review',
169173
recipients: recipientEmails,
170174
data: {
@@ -179,13 +183,13 @@ async function sendCandidatesAvailableEmails () {
179183

180184
sentCount++
181185
}
182-
localLogger.debug(`[sendCandidatesAvailableEmails]: Sent notifications for ${sentCount} of ${projectIds.length} projects with Job Candidates awaiting for review.`)
186+
localLogger.debug(`[sendCandidatesAvailableNotifications]: Sent notifications for ${sentCount} of ${projectIds.length} projects with Job Candidates awaiting for review.`)
183187
}
184188

185189
/**
186-
* Sends email reminders to the hosts and guests about their upcoming interview(s)
190+
* Sends reminders to the hosts and guests about their upcoming interview(s)
187191
*/
188-
async function sendInterviewComingUpEmails () {
192+
async function sendInterviewComingUpNotifications () {
189193
const currentTime = moment.utc()
190194
const timestampFilter = {
191195
[Op.or]: []
@@ -223,7 +227,7 @@ async function sendInterviewComingUpEmails () {
223227
raw: true
224228
})
225229

226-
localLogger.debug(`[sendInterviewComingUpEmails]: Found ${interviews.length} interviews which are coming soon.`)
230+
localLogger.debug(`[sendInterviewComingUpNotifications]: Found ${interviews.length} interviews which are coming soon.`)
227231

228232
let sentHostCount = 0
229233
let sentGuestCount = 0
@@ -233,7 +237,7 @@ async function sendInterviewComingUpEmails () {
233237
if (!data) { continue }
234238

235239
if (!_.isEmpty(interview.hostEmail)) {
236-
sendEmail({}, {
240+
sendNotification({}, {
237241
template: 'taas.notification.interview-coming-up-host',
238242
recipients: [interview.hostEmail],
239243
data: {
@@ -247,12 +251,12 @@ async function sendInterviewComingUpEmails () {
247251

248252
sentHostCount++
249253
} else {
250-
localLogger.error(`Interview id: ${interview.id} host email not present`, 'sendInterviewComingUpEmails')
254+
localLogger.error(`Interview id: ${interview.id} host email not present`, 'sendInterviewComingUpNotifications')
251255
}
252256

253257
if (!_.isEmpty(interview.guestEmails)) {
254258
// send guest emails
255-
sendEmail({}, {
259+
sendNotification({}, {
256260
template: 'taas.notification.interview-coming-up-guest',
257261
recipients: interview.guestEmails,
258262
data: {
@@ -266,17 +270,17 @@ async function sendInterviewComingUpEmails () {
266270

267271
sentGuestCount++
268272
} else {
269-
localLogger.error(`Interview id: ${interview.id} guest emails not present`, 'sendInterviewComingUpEmails')
273+
localLogger.error(`Interview id: ${interview.id} guest emails not present`, 'sendInterviewComingUpNotifications')
270274
}
271275
}
272276

273-
localLogger.debug(`[sendInterviewComingUpEmails]: Sent notifications for ${sentHostCount} hosts and ${sentGuestCount} guest of ${interviews.length} interviews which are coming soon.`)
277+
localLogger.debug(`[sendInterviewComingUpNotifications]: Sent notifications for ${sentHostCount} hosts and ${sentGuestCount} guest of ${interviews.length} interviews which are coming soon.`)
274278
}
275279

276280
/**
277-
* Sends email reminder to the interview host after it ends to change the interview status
281+
* Sends reminder to the interview host after it ends to change the interview status
278282
*/
279-
async function sendInterviewCompletedEmails () {
283+
async function sendInterviewCompletedNotifications () {
280284
const window = moment.duration(config.INTERVIEW_COMPLETED_MATCH_WINDOW)
281285
const rangeStart = moment.utc().subtract(moment.duration(config.INTERVIEW_COMPLETED_PAST_TIME))
282286
const rangeEnd = rangeStart.clone().add(window)
@@ -305,7 +309,7 @@ async function sendInterviewCompletedEmails () {
305309
raw: true
306310
})
307311

308-
localLogger.debug(`[sendInterviewCompletedEmails]: Found ${interviews.length} interviews which must be ended by now.`)
312+
localLogger.debug(`[sendInterviewCompletedNotifications]: Found ${interviews.length} interviews which must be ended by now.`)
309313

310314
let sentCount = 0
311315
for (const interview of interviews) {
@@ -317,7 +321,7 @@ async function sendInterviewCompletedEmails () {
317321
const data = await getDataForInterview(interview)
318322
if (!data) { continue }
319323

320-
sendEmail({}, {
324+
sendNotification({}, {
321325
template: 'taas.notification.interview-awaits-resolution',
322326
recipients: [interview.hostEmail],
323327
data: {
@@ -332,14 +336,14 @@ async function sendInterviewCompletedEmails () {
332336
sentCount++
333337
}
334338

335-
localLogger.debug(`[sendInterviewCompletedEmails]: Sent notifications for ${sentCount} of ${interviews.length} interviews which must be ended by now.`)
339+
localLogger.debug(`[sendInterviewCompletedNotifications]: Sent notifications for ${sentCount} of ${interviews.length} interviews which must be ended by now.`)
336340
}
337341

338342
/**
339-
* Sends email reminder to the all members of teams which have interview completed to take action
343+
* Sends reminder to the all members of teams which have interview completed to take action
340344
* to update the job candidate status
341345
*/
342-
async function sendPostInterviewActionEmails () {
346+
async function sendPostInterviewActionNotifications () {
343347
const completedJobCandidates = await JobCandidate.findAll({
344348
where: {
345349
status: constants.JobCandidateStatus.INTERVIEW
@@ -366,13 +370,15 @@ async function sendPostInterviewActionEmails () {
366370

367371
const projectIds = _.uniq(_.map(jobs, job => job.projectId))
368372

369-
localLogger.debug(`[sendPostInterviewActionEmails]: Found ${projectIds.length} projects with ${completedJobCandidates.length} Job Candidates with interview completed awaiting for an action.`)
373+
localLogger.debug(`[sendPostInterviewActionNotifications]: Found ${projectIds.length} projects with ${completedJobCandidates.length} Job Candidates with interview completed awaiting for an action.`)
370374

371375
let sentCount = 0
376+
const template = 'taas.notification.post-interview-action-required'
377+
372378
for (const projectId of projectIds) {
373379
const project = await getProjectWithId(projectId)
374380
if (!project) { continue }
375-
381+
const webNotifications = []
376382
const recipientEmails = getProjectMembersEmails(project)
377383
const projectJobs = _.filter(jobs, job => job.projectId === projectId)
378384
const teamInterviews = []
@@ -384,13 +390,30 @@ async function sendPostInterviewActionEmails () {
384390
for (const interview of projectJc.interviews) {
385391
const d = await getDataForInterview(interview, projectJc, projectJob)
386392
if (!d) { continue }
393+
d.jobUrl = `${config.TAAS_APP_URL}/${projectId}/positions/${projectJob.id}`
394+
webNotifications.push({
395+
serviceId: 'web',
396+
type: template,
397+
details: {
398+
recipients: _.map(_.uniq(recipientEmails), function (re) { return { email: re } }),
399+
contents: {
400+
jobTitle: d.jobTitle,
401+
teamName: project.name,
402+
projectId,
403+
jobId: projectJob.id,
404+
userHandle: d.handle
405+
},
406+
version: 1
407+
}
408+
})
409+
387410
teamInterviews.push(d)
388411
}
389412
}
390413
}
391414

392-
sendEmail({}, {
393-
template: 'taas.notification.post-interview-action-required',
415+
sendNotification({}, {
416+
template,
394417
recipients: recipientEmails,
395418
data: {
396419
teamName: project.name,
@@ -401,18 +424,18 @@ async function sendPostInterviewActionEmails () {
401424
},
402425
description: 'Post Interview Candidate Action Reminder'
403426
}
404-
})
427+
}, webNotifications)
405428

406429
sentCount++
407430
}
408431

409-
localLogger.debug(`[sendPostInterviewActionEmails]: Sent notifications for ${sentCount} of ${projectIds.length} projects with Job Candidates with interview completed awaiting for an action.`)
432+
localLogger.debug(`[sendPostInterviewActionNotifications]: Sent notifications for ${sentCount} of ${projectIds.length} projects with Job Candidates with interview completed awaiting for an action.`)
410433
}
411434

412435
/**
413-
* Sends reminder emails to all members of teams which have atleast one upcoming resource booking expiration
436+
* Sends reminders to all members of teams which have atleast one upcoming resource booking expiration
414437
*/
415-
async function sendResourceBookingExpirationEmails () {
438+
async function sendResourceBookingExpirationNotifications () {
416439
const currentTime = moment.utc()
417440
const maxEndDate = currentTime.clone().add(moment.duration(config.RESOURCE_BOOKING_EXPIRY_TIME))
418441

@@ -442,9 +465,10 @@ async function sendResourceBookingExpirationEmails () {
442465
})
443466
const projectIds = _.uniq(_.map(expiringResourceBookings, rb => rb.projectId))
444467

445-
localLogger.debug(`[sendResourceBookingExpirationEmails]: Found ${projectIds.length} projects with ${expiringResourceBookings.length} Resource Bookings expiring in less than 3 weeks.`)
468+
localLogger.debug(`[sendResourceBookingExpirationNotifications]: Found ${projectIds.length} projects with ${expiringResourceBookings.length} Resource Bookings expiring in less than 3 weeks.`)
446469

447470
let sentCount = 0
471+
const template = 'taas.notification.resource-booking-expiration'
448472
for (const projectId of projectIds) {
449473
const project = await getProjectWithId(projectId)
450474
if (!project) { continue }
@@ -461,16 +485,36 @@ async function sendResourceBookingExpirationEmails () {
461485
const user = await getUserWithId(booking.userId)
462486
if (!user) { continue }
463487

488+
const jobUrl = `${config.TAAS_APP_URL}/${projectId}/positions/${projectJob.id}`
489+
const resourceBookingUrl = `${config.TAAS_APP_URL}/${projectId}/rb/${booking.id}`
464490
teamResourceBookings.push({
465491
jobTitle: projectJob.title,
466492
handle: user.handle,
467-
endDate: booking.endDate
493+
endDate: booking.endDate,
494+
jobUrl,
495+
resourceBookingUrl
468496
})
469497
}
470498
}
471499

472-
sendEmail({}, {
473-
template: 'taas.notification.resource-booking-expiration',
500+
const webData = {
501+
serviceId: 'web',
502+
type: template,
503+
details: {
504+
recipients: _.map(_.uniq(recipientEmails), function (re) { return { email: re } }),
505+
contents: {
506+
teamName: project.name,
507+
projectId,
508+
numOfExpiringResourceBookings: numResourceBookings
509+
},
510+
version: 1
511+
}
512+
}
513+
514+
const teamUrl = `${config.TAAS_APP_URL}/${project.id}`
515+
516+
sendNotification({}, {
517+
template,
474518
recipients: recipientEmails,
475519
data: {
476520
teamName: project.name,
@@ -479,23 +523,24 @@ async function sendResourceBookingExpirationEmails () {
479523
notificationType: {
480524
upcomingResourceBookingExpiration: true
481525
},
526+
teamUrl,
482527
description: 'Upcoming Resource Booking Expiration'
483528
}
484-
})
529+
}, [webData])
485530

486531
sentCount++
487532
}
488533

489-
localLogger.debug(`[sendResourceBookingExpirationEmails]: Sent notifications for ${sentCount} of ${projectIds.length} projects with Resource Bookings expiring in less than 3 weeks.`)
534+
localLogger.debug(`[sendResourceBookingExpirationNotifications]: Sent notifications for ${sentCount} of ${projectIds.length} projects with Resource Bookings expiring in less than 3 weeks.`)
490535
}
491536

492537
/**
493-
* Send email through a particular template
538+
* Send notification through a particular template
494539
* @param {Object} currentUser the user who perform this operation
495540
* @param {Object} data the email object
496-
* @returns {undefined}
541+
* @param {Array} webNotifications the optional list of web notifications
497542
*/
498-
async function sendEmail (currentUser, data) {
543+
async function sendNotification (currentUser, data, webNotifications = []) {
499544
const template = emailTemplates[data.template]
500545
const dataCC = data.cc || []
501546
const templateCC = template.cc || []
@@ -511,28 +556,32 @@ async function sendEmail (currentUser, data) {
511556
data.data
512557
)
513558
}
559+
560+
const recipients = _.map(_.uniq([...dataRecipients, ...templateRecipients]), function (r) { return { email: r } })
514561
const emailData = {
515562
serviceId: 'email',
516563
type: data.template,
517564
details: {
518565
from: data.from || template.from,
519-
recipients: _.map(_.uniq([...dataRecipients, ...templateRecipients]), function (r) { return { email: r } }),
566+
recipients,
520567
cc: _.map(_.uniq([...dataCC, ...templateCC]), function (r) { return { email: r } }),
521568
data: { ...data.data, ...subjectBody },
522569
sendgridTemplateId: template.sendgridTemplateId,
523570
version: 'v3'
524571
}
525572
}
573+
574+
const notifications = [emailData, ...webNotifications]
526575
await helper.postEvent(config.NOTIFICATIONS_CREATE_TOPIC, {
527-
notifications: [emailData]
576+
notifications
528577
})
529578
}
530579

531580
module.exports = {
532-
sendEmail,
533-
sendCandidatesAvailableEmails,
534-
sendInterviewComingUpEmails,
535-
sendInterviewCompletedEmails,
536-
sendPostInterviewActionEmails,
537-
sendResourceBookingExpirationEmails
581+
sendNotification,
582+
sendCandidatesAvailableNotifications,
583+
sendInterviewComingUpNotifications,
584+
sendInterviewCompletedNotifications,
585+
sendPostInterviewActionNotifications,
586+
sendResourceBookingExpirationNotifications
538587
}

0 commit comments

Comments
 (0)
Please sign in to comment.