329 lines
11 KiB
JavaScript
329 lines
11 KiB
JavaScript
const path = require("path");
|
|
require("dotenv").config({
|
|
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
|
});
|
|
const InstanceManager = require("../utils/instanceMgr").default;
|
|
const logger = require("../utils/logger");
|
|
const client = require("../graphql-client/graphql-client").client;
|
|
const queries = require("../graphql-client/queries");
|
|
const generateEmailTemplate = require("./generateTemplate");
|
|
const moment = require("moment-timezone");
|
|
const { taskEmailQueue } = require("./tasksEmailsQueue");
|
|
const mailer = require("./mailer");
|
|
const { InstanceEndpoints } = require("../utils/instanceMgr");
|
|
const { formatTaskPriority } = require("../notifications/stringHelpers");
|
|
|
|
// Initialize the Tasks Email Queue
|
|
const tasksEmailQueue = taskEmailQueue();
|
|
|
|
// Cleanup function for the Tasks Email Queue
|
|
// eslint-disable-next-line no-unused-vars
|
|
const tasksEmailQueueCleanup = async () => {
|
|
try {
|
|
// Example async operation
|
|
// console.log("Performing Tasks Email Reminder process cleanup...");
|
|
await new Promise((resolve) => tasksEmailQueue.destroy(() => resolve()));
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (err) {
|
|
// console.error("Tasks Email Reminder process cleanup failed:", err);
|
|
}
|
|
};
|
|
|
|
// if (process.env.NODE_ENV !== "development") {
|
|
// // Handling SIGINT (e.g., Ctrl+C)
|
|
// process.on("SIGINT", async () => {
|
|
// console.log("Handling SIGNIT For Tasks Cleanup");
|
|
// await tasksEmailQueueCleanup();
|
|
// process.exit(0);
|
|
// });
|
|
// // Handling SIGTERM (e.g., sent by system shutdown)
|
|
// process.on("SIGTERM", async () => {
|
|
// console.log("Handling SIGTERM For Tasks Cleanup");
|
|
// await tasksEmailQueueCleanup();
|
|
// process.exit(0);
|
|
// });
|
|
// // Handling uncaught exceptions
|
|
// process.on("uncaughtException", async (err) => {
|
|
// console.log("Handling uncaughtException For Tasks Cleanup");
|
|
// await tasksEmailQueueCleanup();
|
|
// process.exit(1);
|
|
// });
|
|
// // Handling unhandled promise rejections
|
|
// process.on("unhandledRejection", async (reason, promise) => {
|
|
// console.log("Handling unhandledRejection For Tasks Cleanup");
|
|
// await tasksEmailQueueCleanup();
|
|
// process.exit(1);
|
|
// });
|
|
// }
|
|
|
|
/**
|
|
* Format the date for the email.
|
|
* @param date
|
|
* @returns {string|string}
|
|
*/
|
|
const formatDate = (date) => {
|
|
return date ? `| Due on: ${moment(date).format("MM/DD/YYYY")}` : "";
|
|
};
|
|
|
|
/**
|
|
* Generate the email template arguments.
|
|
* @param title
|
|
* @param priority
|
|
* @param description
|
|
* @param dueDate
|
|
* @param createdBy
|
|
* @param bodyshop
|
|
* @param job
|
|
* @param taskId
|
|
* @returns {{header, body: string, subHeader: string}}
|
|
*/
|
|
const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId, dateLine, createdBy) => {
|
|
const endPoints = InstanceEndpoints();
|
|
return {
|
|
header: title,
|
|
subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatTaskPriority(priority)} ${formatDate(dueDate)} | Created By: ${createdBy || "N/A"}`,
|
|
body: `Reference: ${job.ro_number || "N/A"} | ${job.ownr_co_nm ? job.ownr_co_nm : `${job.ownr_fn || ""} ${job.ownr_ln || ""}`.trim()} | ${`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`.trim()}<br>${description ? description.concat("<br>") : ""}<a href="${endPoints}/manage/tasks/alltasks?taskid=${taskId}">View this task.</a>`,
|
|
dateLine
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Send the email.
|
|
* @param type
|
|
* @param to
|
|
* @param subject
|
|
* @param html
|
|
* @param taskIds
|
|
* @param successCallback
|
|
*/
|
|
const sendMail = (type, to, subject, html, taskIds, successCallback) => {
|
|
const fromEmails = InstanceManager({
|
|
imex: "ImEX Online <noreply@imex.online>",
|
|
rome: "Rome Online <noreply@romeonline.io>"
|
|
});
|
|
|
|
mailer.sendMail(
|
|
{
|
|
from: fromEmails,
|
|
to,
|
|
subject,
|
|
html
|
|
},
|
|
(error, info) => {
|
|
if (info) {
|
|
if (typeof successCallback === "function" && taskIds && taskIds.length) {
|
|
successCallback(taskIds);
|
|
}
|
|
} else {
|
|
logger.log(`task-${type}-email-failure`, "error", null, null, error);
|
|
}
|
|
}
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Email the assigned user.
|
|
* @param req
|
|
* @param res
|
|
* @returns {Promise<*>}
|
|
*/
|
|
const taskAssignedEmail = async (req, res) => {
|
|
// We have no event Data, bail
|
|
if (!req?.body?.event?.data?.new) {
|
|
return res.status(400).json({ message: "No data in the event body" });
|
|
}
|
|
|
|
const { new: newTask } = req.body.event.data;
|
|
|
|
// This is not a new task, but a reassignment.
|
|
const dirty = req.body.event.data?.old && req.body.event.data?.old?.assigned_to;
|
|
|
|
//Query to get the employee assigned currently.
|
|
const { tasks_by_pk } = await client.request(queries.QUERY_TASK_BY_ID, {
|
|
id: newTask.id
|
|
});
|
|
|
|
const dateLine = moment().tz(tasks_by_pk.bodyshop.timezone).format("M/DD/YYYY @ hh:mm a");
|
|
|
|
sendMail(
|
|
"assigned",
|
|
tasks_by_pk.assigned_to_employee.user_email,
|
|
`A ${formatTaskPriority(newTask.priority)} priority task has been ${dirty ? "reassigned to" : "created for"} you - ${newTask.title}`,
|
|
generateEmailTemplate(
|
|
generateTemplateArgs(
|
|
newTask.title,
|
|
newTask.priority,
|
|
newTask.description,
|
|
newTask.due_date,
|
|
tasks_by_pk.bodyshop,
|
|
tasks_by_pk.job,
|
|
newTask.id,
|
|
dateLine,
|
|
newTask.created_by
|
|
)
|
|
),
|
|
null,
|
|
null,
|
|
tasks_by_pk.bodyshop.convenient_company
|
|
);
|
|
|
|
// We return success regardless because we don't want to block the event trigger.
|
|
res.status(200).json({ success: true });
|
|
};
|
|
|
|
/**
|
|
* Email remind the user of their tasks.
|
|
* @param req
|
|
* @param res
|
|
* @returns {Promise<*>}
|
|
*/
|
|
const tasksRemindEmail = async (req, res) => {
|
|
try {
|
|
const tasksRequest = await client.request(queries.QUERY_REMIND_TASKS, {
|
|
time: moment().add(1, "minutes").toISOString()
|
|
});
|
|
|
|
// No tasks present in the database, bail.
|
|
if (!tasksRequest?.tasks || !tasksRequest?.tasks.length) {
|
|
return res.status(200).json({ message: "No tasks to remind" });
|
|
}
|
|
|
|
// Group tasks by assigned_to, to avoid sending multiple emails to the same recipient.
|
|
const groupedTasks = tasksRequest.tasks.reduce((acc, task) => {
|
|
const key = task.assigned_to_employee.user_email;
|
|
if (!acc[key]) {
|
|
acc[key] = [];
|
|
}
|
|
acc[key].push(task);
|
|
return acc;
|
|
}, {});
|
|
|
|
// No grouped tasks, bail.
|
|
if (Object.keys(groupedTasks).length === 0) {
|
|
return res.status(200).json({ message: "No tasks to remind" });
|
|
}
|
|
|
|
// Build an aggregate data object containing email and the count of tasks assigned to them.
|
|
const recipientCounts = Object.keys(groupedTasks).map((key) => {
|
|
return {
|
|
email: key,
|
|
count: groupedTasks[key].length
|
|
};
|
|
});
|
|
|
|
// Iterate over all recipients and send the email.
|
|
recipientCounts.forEach((recipient) => {
|
|
const fromEmails = InstanceManager({
|
|
imex: "ImEX Online <noreply@imex.online>",
|
|
rome: "Rome Online <noreply@romeonline.io>"
|
|
});
|
|
|
|
const emailData = {
|
|
from: fromEmails,
|
|
to: recipient.email
|
|
};
|
|
|
|
const taskIds = groupedTasks[recipient.email].map((task) => task.id);
|
|
|
|
const dateLine = moment().tz(tasksRequest?.tasks[0].bodyshop.timezone).format("M/DD/YYYY @ hh:mm a");
|
|
|
|
// There is only the one email to send to this author.
|
|
if (recipient.count === 1) {
|
|
const onlyTask = groupedTasks[recipient.email][0];
|
|
|
|
emailData.subject =
|
|
`New ${formatTaskPriority(onlyTask.priority)} Priority Task Reminder - ${onlyTask.title} ${onlyTask.due_date ? `- ${formatDate(onlyTask.due_date)}` : ""}`.trim();
|
|
|
|
emailData.html = generateEmailTemplate(
|
|
generateTemplateArgs(
|
|
onlyTask.title,
|
|
onlyTask.priority,
|
|
onlyTask.description,
|
|
onlyTask.due_date,
|
|
onlyTask.bodyshop,
|
|
onlyTask.job,
|
|
onlyTask.id,
|
|
dateLine,
|
|
onlyTask.created_by
|
|
)
|
|
);
|
|
}
|
|
// There are multiple emails to send to this author.
|
|
else {
|
|
const allTasks = groupedTasks[recipient.email];
|
|
emailData.subject = `New Tasks Reminder - ${allTasks.length} Tasks require your attention`;
|
|
emailData.html = generateEmailTemplate({
|
|
header: `${allTasks.length} Tasks require your attention`,
|
|
subHeader: `Please click on the Tasks below to view the Task.`,
|
|
dateLine,
|
|
body: `
|
|
<ul style="font-family: 'Montserrat', 'Montserrat Alternates', sans-serif; font-size: 90%; margin: 1%; padding-left: 30px;">
|
|
${allTasks
|
|
.map((task) =>
|
|
`
|
|
<li style="font-family: 'Montserrat', 'Montserrat Alternates', sans-serif; font-size: 90%;">
|
|
<a href="${InstanceEndpoints()}/manage/tasks/alltasks?taskid=${task.id}" style="color: #2199e8; text-decoration: none; font-weight: normal; padding: 0; text-align: left; font-family: 'Montserrat', 'Montserrat Alternates', sans-serif; font-size: 90%; line-height: 1.2;">${task.title} - Priority: ${formatTaskPriority(task.priority)} ${task.due_date ? `${formatDate(task.due_date)}` : ""} | Bodyshop: ${task.bodyshop.shopname}</a>
|
|
</li>
|
|
`.trim()
|
|
)
|
|
.join("")}
|
|
</ul>`
|
|
});
|
|
}
|
|
|
|
if (emailData?.subject && emailData?.html) {
|
|
// Send Email
|
|
sendMail(
|
|
"remind",
|
|
emailData.to,
|
|
emailData.subject,
|
|
emailData.html,
|
|
taskIds,
|
|
(taskIds) => {
|
|
for (const taskId of taskIds) {
|
|
tasksEmailQueue.push(taskId);
|
|
}
|
|
},
|
|
tasksRequest?.tasks[0].bodyshop.convenient_company
|
|
);
|
|
}
|
|
});
|
|
|
|
// Sixth step would be to set the remind_at_sent to the current time.
|
|
res.status(200).json({ status: "success" });
|
|
} catch (err) {
|
|
res.status(500).json({
|
|
status: "error",
|
|
message: `Something went wrong sending Task Reminders: ${err.message || "An error occurred"}`
|
|
});
|
|
}
|
|
};
|
|
|
|
// Note: Uncomment this to test locally, it will call the remind_at email check every 20 seconds
|
|
// const callTaskRemindEmailInternally = () => {
|
|
// const req = {
|
|
// body: {
|
|
// // You can mock any request data here if needed
|
|
// }
|
|
// };
|
|
//
|
|
// const res = {
|
|
// status: (code) => {
|
|
// return {
|
|
// json: (data) => {
|
|
// console.log(`Response Status: ${code}`, data);
|
|
// }
|
|
// };
|
|
// }
|
|
// };
|
|
//
|
|
// // Call the taskRemindEmail function with mock req and res
|
|
// tasksRemindEmail(req, res);
|
|
// };
|
|
// setInterval(callTaskRemindEmailInternally, 20000);
|
|
|
|
module.exports = {
|
|
taskAssignedEmail,
|
|
tasksRemindEmail
|
|
};
|