Merged in feature/IO-3096-GlobalNotifications (pull request #2184)
Feature/IO-3096 GlobalNotifications
This commit is contained in:
@@ -50,13 +50,69 @@ const handleBillsChange = async (req, res) =>
|
||||
|
||||
/**
|
||||
* Handle documents change notifications.
|
||||
* Processes both old and new job IDs if the document was moved between jobs.
|
||||
*
|
||||
* @param {Object} req - Express request object.
|
||||
* @param {Object} res - Express response object.
|
||||
* @returns {Promise<Object>} JSON response with a success message.
|
||||
*/
|
||||
const handleDocumentsChange = async (req, res) =>
|
||||
processNotificationEvent(req, res, "req.body.event.new.jobid", "Documents Change Notifications Event Handled.");
|
||||
const handleDocumentsChange = async (req, res) => {
|
||||
const { logger } = req;
|
||||
const newJobId = req.body?.event?.data?.new?.jobid;
|
||||
const oldJobId = req.body?.event?.data?.old?.jobid;
|
||||
|
||||
// If jobid changed (document moved between jobs), we need to notify both jobs
|
||||
if (oldJobId && newJobId && oldJobId !== newJobId) {
|
||||
// Process notification for new job ID
|
||||
scenarioParser(req, "req.body.event.new.jobid").catch((error) => {
|
||||
logger.log("notifications-error", "error", "notifications", null, {
|
||||
message: error?.message,
|
||||
stack: error?.stack
|
||||
});
|
||||
});
|
||||
|
||||
// Create a modified request for old job ID
|
||||
const oldJobReq = {
|
||||
body: {
|
||||
...req.body,
|
||||
event: {
|
||||
...req.body.event,
|
||||
data: {
|
||||
new: {
|
||||
...req.body.event.data.old,
|
||||
// Add a flag to indicate this document was moved away
|
||||
_documentMoved: true,
|
||||
_movedToJob: newJobId
|
||||
},
|
||||
old: null
|
||||
}
|
||||
}
|
||||
},
|
||||
logger,
|
||||
sessionUtils: req.sessionUtils
|
||||
};
|
||||
|
||||
// Process notification for old job ID using the modified request
|
||||
scenarioParser(oldJobReq, "req.body.event.new.jobid").catch((error) => {
|
||||
logger.log("notifications-error", "error", "notifications", null, {
|
||||
message: error?.message,
|
||||
stack: error?.stack
|
||||
});
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Documents Change Notifications Event Handled for both jobs." });
|
||||
}
|
||||
|
||||
// Otherwise just process the new job ID
|
||||
scenarioParser(req, "req.body.event.new.jobid").catch((error) => {
|
||||
logger.log("notifications-error", "error", "notifications", null, {
|
||||
message: error?.message,
|
||||
stack: error?.stack
|
||||
});
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Documents Change Notifications Event Handled." });
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle job lines change notifications.
|
||||
|
||||
@@ -258,8 +258,19 @@ const jobStatusChangeBuilder = (data) => {
|
||||
const newMediaAddedReassignedBuilder = (data) => {
|
||||
// Determine if it's an image or document
|
||||
const mediaType = data?.data?.type?.startsWith("image") ? "Image" : "Document";
|
||||
// Determine if it's added or updated
|
||||
const action = data.isNew ? "added" : "updated";
|
||||
|
||||
// Determine the action
|
||||
let action;
|
||||
if (data?.data?._documentMoved) {
|
||||
action = "moved to another Job"; // Special case for document moved from this job
|
||||
} else if (data.isNew) {
|
||||
action = "added"; // New media
|
||||
} else if (data.changedFields?.jobid && data.changedFields.jobid.old !== data.changedFields.jobid.new) {
|
||||
action = "moved to this Job";
|
||||
} else {
|
||||
action = "updated";
|
||||
}
|
||||
|
||||
// Construct the body string
|
||||
const body = `An ${mediaType} has been ${action}.`;
|
||||
|
||||
@@ -272,7 +283,8 @@ const newMediaAddedReassignedBuilder = (data) => {
|
||||
body,
|
||||
variables: {
|
||||
mediaType,
|
||||
action
|
||||
action,
|
||||
movedToJob: data?.data?._movedToJob
|
||||
},
|
||||
recipients: []
|
||||
},
|
||||
@@ -452,7 +464,6 @@ const paymentCollectedCompletedBuilder = (data) => {
|
||||
* Builds notification data for changes to scheduled dates.
|
||||
*/
|
||||
const scheduledDatesChangedBuilder = (data) => {
|
||||
const momentFormat = "MM/DD/YYYY hh:mm a";
|
||||
const changedFields = data.changedFields;
|
||||
|
||||
// Define field configurations
|
||||
@@ -462,16 +473,38 @@ const scheduledDatesChangedBuilder = (data) => {
|
||||
scheduled_delivery: "Scheduled Delivery"
|
||||
};
|
||||
|
||||
// Helper function to format date and time with "at"
|
||||
const formatDateTime = (date) => {
|
||||
if (!date) return "unset";
|
||||
const formatted = moment(date).tz(data.bodyShopTimezone);
|
||||
const datePart = formatted.format("MM/DD/YYYY");
|
||||
const timePart = formatted.format("hh:mm a");
|
||||
return `${datePart} at ${timePart}`;
|
||||
};
|
||||
|
||||
// Build field messages dynamically
|
||||
const fieldMessages = Object.entries(fieldConfigs)
|
||||
.filter(([field]) => changedFields[field]) // Only include changed fields
|
||||
.map(([field, label]) => {
|
||||
const { old, new: newValue } = changedFields[field];
|
||||
const formatDate = (date) => (date ? moment(date).tz(data.bodyShopTimezone).format(momentFormat) : "unset");
|
||||
return `${label} changed from ${formatDate(old)} to ${formatDate(newValue)}`;
|
||||
});
|
||||
|
||||
const body = fieldMessages.length > 0 ? fieldMessages.join(", ") + "." : "Scheduled dates have been updated.";
|
||||
// Case 1: Scheduled date cancelled (from value to null)
|
||||
if (old && !newValue) {
|
||||
return `${label} was cancelled (previously ${formatDateTime(old)}).`;
|
||||
}
|
||||
// Case 2: Scheduled date set (from null to value)
|
||||
else if (!old && newValue) {
|
||||
return `${label} was set to ${formatDateTime(newValue)}.`;
|
||||
}
|
||||
// Case 3: Scheduled date changed (from value to value)
|
||||
else if (old && newValue) {
|
||||
return `${label} changed from ${formatDateTime(old)} to ${formatDateTime(newValue)}.`;
|
||||
}
|
||||
return ""; // Fallback, though this shouldn't happen with the filter
|
||||
})
|
||||
.filter(Boolean); // Remove any empty strings
|
||||
|
||||
const body = fieldMessages.length > 0 ? fieldMessages.join(" ") : "Scheduled dates have been updated.";
|
||||
|
||||
const result = {
|
||||
app: {
|
||||
|
||||
@@ -37,10 +37,11 @@ const scenarioParser = async (req, jobIdField) => {
|
||||
// Step 1: Validate we know what user committed the action that fired the parser
|
||||
// console.log("Step 1");
|
||||
|
||||
const hasuraUserRole = event?.session_variables?.["x-hasura-role"];
|
||||
const hasuraUserId = event?.session_variables?.["x-hasura-user-id"];
|
||||
|
||||
// Bail if we don't know who started the scenario
|
||||
if (!hasuraUserId) {
|
||||
if (hasuraUserRole === "user" && !hasuraUserId) {
|
||||
logger.log("No Hasura user ID found, skipping notification parsing", "info", "notifications");
|
||||
return;
|
||||
}
|
||||
@@ -84,7 +85,7 @@ const scenarioParser = async (req, jobIdField) => {
|
||||
authId: watcher?.user?.authid
|
||||
}));
|
||||
|
||||
if (FILTER_SELF_FROM_WATCHERS) {
|
||||
if (FILTER_SELF_FROM_WATCHERS && hasuraUserRole === "user") {
|
||||
jobWatchers = jobWatchers.filter((watcher) => watcher.authId !== hasuraUserId);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user