@@ -266,6 +266,9 @@ export function TaskUpsertModalComponent({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
|
<Form.Item name="remind_at_sent" hidden>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("tasks.fields.remind_at")}
|
label={t("tasks.fields.remind_at")}
|
||||||
name="remind_at"
|
name="remind_at"
|
||||||
|
|||||||
@@ -168,30 +168,6 @@ export function TaskUpsertModalContainer({ bodyshop, currentUser, taskUpsert, to
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (isAssignedToDirty) {
|
|
||||||
// // TODO This is being moved serverside
|
|
||||||
// axios
|
|
||||||
// .post("/sendemail", {
|
|
||||||
// from: {
|
|
||||||
// name: bodyshop.shopname,
|
|
||||||
// address: bodyshop.email
|
|
||||||
// },
|
|
||||||
// ReplyTo: {
|
|
||||||
// Email: "noreply@imex.online"
|
|
||||||
// },
|
|
||||||
// to: values.assigned_to,
|
|
||||||
// subject: `A Task has been re-assigned to you on ${bodyshop.shopname} - ${values.title}`,
|
|
||||||
// templateStrings: {
|
|
||||||
// header: values.title,
|
|
||||||
// subHeader: `Assigned by ${currentUser.email} ${values.due_at ? `| Due on ${dayjs(values.due_at).format("MM/DD/YYYY")}` : ""}`,
|
|
||||||
// body: `<a href="${window.location.protocol}//${window.location.host}/manage/tasks/alltasks?taskid=${existingTask.id}">Please sign in to your account to view the task details.</a>`
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .catch((e) =>
|
|
||||||
// console.error(`Something went wrong sending email to Assigned party on Task creation. ${e.message || ""}`)
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("tasks.successes.updated")
|
message: t("tasks.successes.updated")
|
||||||
});
|
});
|
||||||
@@ -233,30 +209,6 @@ export function TaskUpsertModalContainer({ bodyshop, currentUser, taskUpsert, to
|
|||||||
form.resetFields();
|
form.resetFields();
|
||||||
toggleModalVisible();
|
toggleModalVisible();
|
||||||
|
|
||||||
// send notification to the assigned user
|
|
||||||
// TODO: This is being moved serverside
|
|
||||||
// axios
|
|
||||||
// .post("/sendemail", {
|
|
||||||
// from: {
|
|
||||||
// name: bodyshop.shopname,
|
|
||||||
// address: bodyshop.email
|
|
||||||
// },
|
|
||||||
// replyTo: {
|
|
||||||
// Email: "noreply@imex.online"
|
|
||||||
// },
|
|
||||||
// to: values.assigned_to,
|
|
||||||
// subject: `A new Task has been assigned to you on ${bodyshop.shopname} - ${values.title}`,
|
|
||||||
// templateName: "taskAssigned",
|
|
||||||
// templateStrings: {
|
|
||||||
// header: values.title,
|
|
||||||
// subHeader: `Assigned by ${currentUser.email} ${values.due_at ? `| Due on ${dayjs(values.due_at).format("MM/DD/YYYY")}` : ""}`,
|
|
||||||
// body: `<a href="${window.location.protocol}//${window.location.host}/manage/tasks/alltasks?taskid=${newTaskID}">Please sign to your account to view the task details.</a>`
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .catch((e) =>
|
|
||||||
// console.error(`Something went wrong sending email to Assigned party on Task edit. ${e.message || ""}`)
|
|
||||||
// );
|
|
||||||
|
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("tasks.successes.created")
|
message: t("tasks.successes.created")
|
||||||
});
|
});
|
||||||
@@ -276,6 +228,7 @@ export function TaskUpsertModalContainer({ bodyshop, currentUser, taskUpsert, to
|
|||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
console.dir(dirtyValues?.remind_at_sent);
|
||||||
try {
|
try {
|
||||||
await handleExistingTask(taskSource.id, taskSource.jobid, dirtyValues);
|
await handleExistingTask(taskSource.id, taskSource.jobid, dirtyValues);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const PARTIAL_TASK_FIELDS = gql`
|
|||||||
completed
|
completed
|
||||||
completed_at
|
completed_at
|
||||||
remind_at
|
remind_at
|
||||||
|
remind_at_sent
|
||||||
priority
|
priority
|
||||||
job {
|
job {
|
||||||
id
|
id
|
||||||
@@ -63,10 +64,6 @@ export const PARTIAL_TASK_FIELDS = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const PARTIAL_TASK_FIELDS_WRAPPER = gql`
|
|
||||||
${PARTIAL_TASK_FIELDS}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const QUERY_GET_TASK_BY_ID = gql`
|
export const QUERY_GET_TASK_BY_ID = gql`
|
||||||
${PARTIAL_TASK_FIELDS}
|
${PARTIAL_TASK_FIELDS}
|
||||||
query QUERY_GET_TASK_BY_ID($id: uuid!) {
|
query QUERY_GET_TASK_BY_ID($id: uuid!) {
|
||||||
|
|||||||
@@ -1 +1,8 @@
|
|||||||
[]
|
- name: Task Reminders
|
||||||
|
webhook: https://worktest.home.irony.online/tasks-remind-handler
|
||||||
|
schedule: '*/1 * * * *'
|
||||||
|
include_in_metadata: true
|
||||||
|
payload: {}
|
||||||
|
headers:
|
||||||
|
- name: event-secret
|
||||||
|
value: DevelopmentEventSecret
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const client = require("../graphql-client/graphql-client").client;
|
|||||||
const queries = require("../graphql-client/queries");
|
const queries = require("../graphql-client/queries");
|
||||||
const { isObject } = require("lodash");
|
const { isObject } = require("lodash");
|
||||||
const generateEmailTemplate = require("./generateTemplate");
|
const generateEmailTemplate = require("./generateTemplate");
|
||||||
|
const moment = require("moment");
|
||||||
|
|
||||||
const ses = new aws.SES({
|
const ses = new aws.SES({
|
||||||
// The key apiVersion is no longer supported in v3, and can be removed.
|
// The key apiVersion is no longer supported in v3, and can be removed.
|
||||||
@@ -23,12 +24,46 @@ const ses = new aws.SES({
|
|||||||
rome: "us-east-2"
|
rome: "us-east-2"
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let transporter = nodemailer.createTransport({
|
let transporter = nodemailer.createTransport({
|
||||||
SES: { ses, aws }
|
SES: { ses, aws }
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.sendServerEmail = async function ({ subject, text }) {
|
// Get the image from the URL and return it as a base64 string
|
||||||
|
const getImage = async (imageUrl) => {
|
||||||
|
let image = await axios.get(imageUrl, { responseType: "arraybuffer" });
|
||||||
|
let raw = Buffer.from(image.data).toString("base64");
|
||||||
|
return "data:" + image.headers["content-type"] + ";base64," + raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Log the email in the database
|
||||||
|
const logEmail = async (req, email) => {
|
||||||
|
try {
|
||||||
|
const insertresult = await client.request(queries.INSERT_EMAIL_AUDIT, {
|
||||||
|
email: {
|
||||||
|
to: email.to,
|
||||||
|
cc: email.cc,
|
||||||
|
subject: email.subject,
|
||||||
|
bodyshopid: req.body.bodyshopid,
|
||||||
|
useremail: req.user.email,
|
||||||
|
contents: req.body.html,
|
||||||
|
jobid: req.body.jobid,
|
||||||
|
sesmessageid: email.messageId,
|
||||||
|
status: "Sent"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(insertresult);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("email-log-error", "error", req.user.email, null, {
|
||||||
|
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||||
|
to: req.body.to,
|
||||||
|
cc: req.body.cc,
|
||||||
|
subject: req.body.subject
|
||||||
|
// info,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendServerEmail = async ({ subject, text }) => {
|
||||||
if (process.env.NODE_ENV === undefined) return;
|
if (process.env.NODE_ENV === undefined) return;
|
||||||
try {
|
try {
|
||||||
transporter.sendMail(
|
transporter.sendMail(
|
||||||
@@ -60,7 +95,8 @@ exports.sendServerEmail = async function ({ subject, text }) {
|
|||||||
logger.log("server-email-failure", "error", null, null, error);
|
logger.log("server-email-failure", "error", null, null, error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
exports.sendTaskEmail = async function ({ to, subject, text, attachments }) {
|
|
||||||
|
const sendTaskEmail = async ({ to, subject, text, attachments }) => {
|
||||||
try {
|
try {
|
||||||
transporter.sendMail(
|
transporter.sendMail(
|
||||||
{
|
{
|
||||||
@@ -84,19 +120,8 @@ exports.sendTaskEmail = async function ({ to, subject, text, attachments }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This will be called by a Hasura event trigger
|
// Send an email
|
||||||
exports.taskAssignedEmail = async function (req, res) {
|
const sendEmail = async (req, res) => {
|
||||||
console.dir(req, { depth: null });
|
|
||||||
return res.status(200).json(req);
|
|
||||||
};
|
|
||||||
|
|
||||||
// This will be called by a Hasura event trigger
|
|
||||||
exports.tasksRemindEmail = async function (req, res) {
|
|
||||||
console.dir(req, { depth: null });
|
|
||||||
return res.status(200).json(req);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.sendEmail = async (req, res) => {
|
|
||||||
logger.log("send-email", "DEBUG", req.user.email, null, {
|
logger.log("send-email", "DEBUG", req.user.email, null, {
|
||||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||||
replyTo: req.body.ReplyTo.Email,
|
replyTo: req.body.ReplyTo.Email,
|
||||||
@@ -204,40 +229,8 @@ exports.sendEmail = async (req, res) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getImage(imageUrl) {
|
// This will be called by an SNS event trigger
|
||||||
let image = await axios.get(imageUrl, { responseType: "arraybuffer" });
|
const emailBounce = async (req, res) => {
|
||||||
let raw = Buffer.from(image.data).toString("base64");
|
|
||||||
return "data:" + image.headers["content-type"] + ";base64," + raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function logEmail(req, email) {
|
|
||||||
try {
|
|
||||||
const insertresult = await client.request(queries.INSERT_EMAIL_AUDIT, {
|
|
||||||
email: {
|
|
||||||
to: email.to,
|
|
||||||
cc: email.cc,
|
|
||||||
subject: email.subject,
|
|
||||||
bodyshopid: req.body.bodyshopid,
|
|
||||||
useremail: req.user.email,
|
|
||||||
contents: req.body.html,
|
|
||||||
jobid: req.body.jobid,
|
|
||||||
sesmessageid: email.messageId,
|
|
||||||
status: "Sent"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log(insertresult);
|
|
||||||
} catch (error) {
|
|
||||||
logger.log("email-log-error", "error", req.user.email, null, {
|
|
||||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
|
||||||
to: req.body.to,
|
|
||||||
cc: req.body.cc,
|
|
||||||
subject: req.body.subject
|
|
||||||
// info,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.emailBounce = async function (req, res) {
|
|
||||||
try {
|
try {
|
||||||
const body = JSON.parse(req.body);
|
const body = JSON.parse(req.body);
|
||||||
if (body.Type === "SubscriptionConfirmation") {
|
if (body.Type === "SubscriptionConfirmation") {
|
||||||
@@ -311,3 +304,10 @@ ${body.bounce?.bouncedRecipients.map(
|
|||||||
}
|
}
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sendEmail,
|
||||||
|
sendServerEmail,
|
||||||
|
sendTaskEmail,
|
||||||
|
emailBounce
|
||||||
|
};
|
||||||
|
|||||||
223
server/email/tasksEmails.js
Normal file
223
server/email/tasksEmails.js
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
const path = require("path");
|
||||||
|
require("dotenv").config({
|
||||||
|
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||||
|
});
|
||||||
|
let nodemailer = require("nodemailer");
|
||||||
|
let aws = require("@aws-sdk/client-ses");
|
||||||
|
let { defaultProvider } = require("@aws-sdk/credential-provider-node");
|
||||||
|
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");
|
||||||
|
const { UPDATE_TASKS_REMIND_AT_SENT } = require("../graphql-client/queries");
|
||||||
|
|
||||||
|
const ses = new aws.SES({
|
||||||
|
apiVersion: "latest",
|
||||||
|
defaultProvider,
|
||||||
|
region: InstanceManager({
|
||||||
|
imex: "ca-central-1",
|
||||||
|
rome: "us-east-2"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
SES: { ses, aws },
|
||||||
|
sendingRate: 40 // 40 emails per second.
|
||||||
|
});
|
||||||
|
|
||||||
|
const fromEmails = InstanceManager({
|
||||||
|
imex: `ImEX Online <noreply@imex.online>`,
|
||||||
|
rome: `Rome Online <noreply@romeonline.io>`,
|
||||||
|
promanager: `ProManager <noreply@promanager.web-est.com>`
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 createdBy
|
||||||
|
* @param dueDate
|
||||||
|
* @param taskId
|
||||||
|
* @returns {{header, body: string, subHeader: string}}
|
||||||
|
*/
|
||||||
|
const generateTemplateArgs = (title, createdBy, dueDate, taskId) => {
|
||||||
|
return {
|
||||||
|
header: title,
|
||||||
|
subHeader: `Assigned by ${createdBy} ${formatDate(dueDate)}`,
|
||||||
|
body: `<a href="${fromEmails}/manage/tasks/alltasks?taskid=${taskId}">Please sign in to your account to view the Task details.</a>`
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the email.
|
||||||
|
* @param type
|
||||||
|
* @param to
|
||||||
|
* @param subject
|
||||||
|
* @param html
|
||||||
|
* @param taskIds
|
||||||
|
* @param successCallback
|
||||||
|
*/
|
||||||
|
const sendMail = (type, to, subject, html, taskIds, successCallback) => {
|
||||||
|
// Push next messages to Nodemailer
|
||||||
|
transporter.once("idle", () => {
|
||||||
|
if (transporter.isIdle()) {
|
||||||
|
transporter.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an email to the assigned user.
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
const taskAssignedEmail = async (req, res) => {
|
||||||
|
// We have no event Data, bail
|
||||||
|
if (!req?.payload?.event?.data?.new) {
|
||||||
|
return res.status(400).json({ message: "No data in the event payload" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { new: newTask } = req.payload.event.data;
|
||||||
|
|
||||||
|
// This is not a new task, but a reassignment.
|
||||||
|
const dirty = req.payload.event.data?.old && req.payload.event.data?.old?.assigned_to;
|
||||||
|
|
||||||
|
sendMail(
|
||||||
|
"assigned",
|
||||||
|
newTask.assigned_to,
|
||||||
|
`A Task has been ${dirty ? "reassigned" : "created"} for you - ${newTask.title}`,
|
||||||
|
generateEmailTemplate(generateTemplateArgs(newTask.title, newTask.created_by, newTask.due_date, newTask.id))
|
||||||
|
);
|
||||||
|
|
||||||
|
// We return success regardless because we don't want to block the event trigger.
|
||||||
|
res.status(200).json({ success: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an email to 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;
|
||||||
|
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 emailData = {
|
||||||
|
from: fromEmails,
|
||||||
|
to: recipient.email
|
||||||
|
};
|
||||||
|
|
||||||
|
const taskIds = groupedTasks[recipient.email].map((task) => task.id);
|
||||||
|
|
||||||
|
// There is only the one email to send to this author.
|
||||||
|
if (recipient.count === 1) {
|
||||||
|
const onlyTask = groupedTasks[recipient.email][0];
|
||||||
|
|
||||||
|
emailData.subject = `New Task Reminder - ${onlyTask.title} - ${formatDate(onlyTask.due_date)}`;
|
||||||
|
|
||||||
|
emailData.html = generateEmailTemplate(
|
||||||
|
generateTemplateArgs(onlyTask.title, onlyTask.created_by, onlyTask.due_date, onlyTask.id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// There are multiple emails to send to this author.
|
||||||
|
else {
|
||||||
|
const allTasks = groupedTasks[recipient.email];
|
||||||
|
emailData.subject = `New Task Reminder - ${allTasks.length} Tasks require your attention`;
|
||||||
|
emailData.html = generateEmailTemplate({
|
||||||
|
header: `${allTasks.length} Tasks require your attention`,
|
||||||
|
subHeader: `Please sign in to your account to view the Task details.`,
|
||||||
|
body: `<ul>
|
||||||
|
${allTasks
|
||||||
|
.map(
|
||||||
|
(task) =>
|
||||||
|
`<li><a href="${fromEmails}/manage/tasks/alltasks?taskid=${task.id}">${task.title} - ${formatDate(task.due_date)}</a></li>`
|
||||||
|
)
|
||||||
|
.join("")}
|
||||||
|
</ul>`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emailData?.subject && emailData?.html) {
|
||||||
|
// Send Email
|
||||||
|
sendMail("remind", emailData.to, emailData.subject, emailData.html, taskIds, (taskIds) => {});
|
||||||
|
client.request(UPDATE_TASKS_REMIND_AT_SENT, {
|
||||||
|
taskIds,
|
||||||
|
now: moment().toISOString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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"}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
taskAssignedEmail,
|
||||||
|
tasksRemindEmail
|
||||||
|
};
|
||||||
@@ -2393,3 +2393,37 @@ exports.COMPLETE_SURVEY = `mutation COMPLETE_SURVEY($surveyId: uuid!, $survey: c
|
|||||||
affected_rows
|
affected_rows
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
exports.QUERY_REMIND_TASKS = `
|
||||||
|
query QUERY_REMIND_TASKS($time: timestamptz!) {
|
||||||
|
tasks(
|
||||||
|
where: {
|
||||||
|
_and: [
|
||||||
|
{ remind_at: { _is_null: false } }
|
||||||
|
{ remind_at: { _lte: $time } }
|
||||||
|
{ remind_at_sent: { _is_null: true } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
due_date
|
||||||
|
created_by
|
||||||
|
assigned_to
|
||||||
|
remind_at
|
||||||
|
remind_at_sent
|
||||||
|
priority
|
||||||
|
job {
|
||||||
|
id
|
||||||
|
ro_number
|
||||||
|
}
|
||||||
|
jobid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports.UPDATE_TASKS_REMIND_AT_SENT = `mutation UPDATE_TASK_REMIND_AT_SENT($taskIds: [uuid!]!, $now: timestamptz!) {
|
||||||
|
update_tasks_many(updates: {where: {id: {_in: $taskIds}}, _set: {remind_at_sent: $now}}) {
|
||||||
|
affected_rows
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const os = require("../opensearch/os-handler");
|
|||||||
const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMIddleware");
|
const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMIddleware");
|
||||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||||
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
|
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
|
||||||
|
const { taskAssignedEmail, tasksRemindEmail } = require("../email/tasksEmails");
|
||||||
|
|
||||||
//Test route to ensure Express is responding.
|
//Test route to ensure Express is responding.
|
||||||
router.get("/test", async function (req, res) {
|
router.get("/test", async function (req, res) {
|
||||||
@@ -41,8 +42,8 @@ router.post("/sendemail", validateFirebaseIdTokenMiddleware, sendEmail.sendEmail
|
|||||||
router.post("/emailbounce", bodyParser.text(), sendEmail.emailBounce);
|
router.post("/emailbounce", bodyParser.text(), sendEmail.emailBounce);
|
||||||
|
|
||||||
// Tasks Email Handler
|
// Tasks Email Handler
|
||||||
router.post("/tasks-assigned-handler", eventAuthorizationMiddleware, sendEmail.taskAssignedEmail);
|
router.post("/tasks-assigned-handler", eventAuthorizationMiddleware, taskAssignedEmail);
|
||||||
router.post("/tasks-remind-handler", eventAuthorizationMiddleware, sendEmail.tasksRemindEmail);
|
router.post("/tasks-remind-handler", eventAuthorizationMiddleware, tasksRemindEmail);
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
router.post("/record-handler/arms", data.arms);
|
router.post("/record-handler/arms", data.arms);
|
||||||
|
|||||||
Reference in New Issue
Block a user