Merged in feature/IO-3166-Global-Notifications-Part-2 (pull request #2159)
Feature/IO-3166 Global Notifications Part 2
This commit is contained in:
@@ -623,6 +623,7 @@ function Header({
|
||||
key: "recent",
|
||||
id: "header-recent",
|
||||
icon: <ClockCircleFilled />,
|
||||
label: t("menus.header.recent"),
|
||||
children: recentItems.map((i, idx) => ({
|
||||
key: idx,
|
||||
id: `header-recent-${idx}`,
|
||||
@@ -633,6 +634,7 @@ function Header({
|
||||
key: "user",
|
||||
id: "header-user",
|
||||
icon: <UserOutlined />,
|
||||
label: t("menus.currentuser.profile"),
|
||||
children: [
|
||||
{
|
||||
key: "signout",
|
||||
|
||||
@@ -140,7 +140,7 @@ export default function JobWatcherToggleComponent({
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover content={popoverContent} trigger="click" open={open} onOpenChange={setOpen}>
|
||||
<Popover placement="topRight" content={popoverContent} trigger="click" open={open} onOpenChange={setOpen}>
|
||||
<Tooltip title={t("notifications.tooltips.job-watchers")}>
|
||||
<Button
|
||||
shape="circle"
|
||||
|
||||
@@ -51,7 +51,7 @@ const NotificationCenterComponent = forwardRef(
|
||||
<div className="notification-content">
|
||||
<Title level={5} className="notification-title">
|
||||
<span className="ro-number">
|
||||
{t("notifications.labels.ro-number", { ro_number: notification.roNumber })}
|
||||
{t("notifications.labels.ro-number", { ro_number: notification.roNumber || t("general.labels.na") })}
|
||||
</span>
|
||||
<Text type="secondary" className="relative-time" title={DateTimeFormat(notification.created_at)}>
|
||||
{day(notification.created_at).fromNow()}
|
||||
|
||||
@@ -299,7 +299,9 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser, scenarioNot
|
||||
.catch((e) => console.error(`Error marking notification read: ${e?.message || ""}`));
|
||||
}}
|
||||
>
|
||||
{t("notifications.labels.notification-popup-title", { ro_number: jobRoNumber })}
|
||||
{t("notifications.labels.notification-popup-title", {
|
||||
ro_number: jobRoNumber || t("general.labels.na")
|
||||
})}
|
||||
</div>
|
||||
),
|
||||
description: (
|
||||
|
||||
@@ -3303,7 +3303,7 @@
|
||||
request_transform:
|
||||
body:
|
||||
action: transform
|
||||
template: "{\r\n \"event\": {\r\n \"session_variables\": {\r\n \"x-hasura-user-id\": {{$body.event.session_variables.x-hasura-user-id}}\r\n }, \r\n \"op\": \"UPDATE\",\r\n \"data\": {\r\n \"old\": {\r\n \"id\": {{$body.event.data.old.id}},\r\n \"jobid\": {{$body.event.data.old.jobid}},\r\n \"critical\": {{$body.event.data.old.critical}},\r\n \"status\": {{$body.event.data.old.status}}\r\n },\r\n \"new\": {\r\n \"id\": {{$body.event.data.new.id}},\r\n \"jobid\": {{$body.event.data.new.jobid}},\r\n \"critical\": {{$body.event.data.old.critical}},\r\n \"status\": {{$body.event.data.new.status}}\r\n }\r\n }\r\n },\r\n \"trigger\": {\r\n \"name\": \"notifications_joblines\"\r\n },\r\n \"table\": {\r\n \"schema\": \"public\",\r\n \"name\": \"joblines\"\r\n }\r\n}\r\n"
|
||||
template: "{\r\n \"event\": {\r\n \"session_variables\": {\r\n \"x-hasura-user-id\": {{$body?.event?.session_variables?.x-hasura-user-id ?? \"Internal\"}}\r\n }, \r\n \"op\": \"UPDATE\",\r\n \"data\": {\r\n \"old\": {\r\n \"id\": {{$body.event.data.old.id}},\r\n \"jobid\": {{$body.event.data.old.jobid}},\r\n \"critical\": {{$body.event.data.old.critical}},\r\n \"status\": {{$body.event.data.old.status}}\r\n },\r\n \"new\": {\r\n \"id\": {{$body.event.data.new.id}},\r\n \"jobid\": {{$body.event.data.new.jobid}},\r\n \"critical\": {{$body.event.data.new.critical}},\r\n \"status\": {{$body.event.data.new.status}}\r\n }\r\n }\r\n },\r\n \"trigger\": {\r\n \"name\": \"notifications_joblines\"\r\n },\r\n \"table\": {\r\n \"schema\": \"public\",\r\n \"name\": \"joblines\"\r\n }\r\n}\r\n"
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
@@ -4573,7 +4573,7 @@
|
||||
request_transform:
|
||||
body:
|
||||
action: transform
|
||||
template: "{\r\n \"event\": {\r\n \"session_variables\": {\r\n \"x-hasura-user-id\": {{$body.event.session_variables.x-hasura-user-id}}\r\n }, \r\n \"op\": \"UPDATE\",\r\n \"data\": {\r\n \"old\": {\r\n \"id\": {{$body.event.data.old.id}},\r\n \"ro_number\": {{$body.event.data.old.ro_number}},\r\n \"queued_for_parts\": {{$body.event.data.old.queued_for_parts}},\r\n \"employee_prep\": {{$body.event.data.old.employee_prep}},\r\n \"clm_total\": {{$body.event.data.old.clm_total}},\r\n \"towin\": {{$body.event.data.old.towin}},\r\n \"employee_body\": {{$body.event.data.old.employee_body}},\r\n \"converted\": {{$body.event.data.old.converted}},\r\n \"scheduled_in\": {{$body.event.data.old.scheduled_in}},\r\n \"scheduled_completion\": {{$body.event.data.old.scheduled_completion}},\r\n \"scheduled_delivery\": {{$body.event.data.old.scheduled_delivery}},\r\n \"actual_delivery\": {{$body.event.data.old.actual_delivery}},\r\n \"actual_completion\": {{$body.event.data.old.actual_completion}},\r\n \"alt_transport\": {{$body.event.data.old.alt_transport}},\r\n \"date_exported\": {{$body.event.data.old.date_exported}},\r\n \"status\": {{$body.event.data.old.status}},\r\n \"employee_csr\": {{$body.event.data.old.employee_csr}},\r\n \"actual_in\": {{$body.event.data.old.actual_in}},\r\n \"deliverchecklist\": {{$body.event.data.old.deliverchecklist}},\r\n \"comment\": {{$body.event.data.old.comment}},\r\n \"employee_refinish\": {{$body.event.data.old.employee_refinish}},\r\n \"inproduction\": {{$body.event.data.old.inproduction}},\r\n \"production_vars\": {{$body.event.data.old.production_vars}},\r\n \"intakechecklist\": {{$body.event.data.old.intakechecklist}},\r\n \"cieca_ttl\": {{$body.event.data.old.cieca_ttl}},\r\n \"date_invoiced\": {{$body.event.data.old.date_invoiced}}\r\n },\r\n \"new\": {\r\n \"id\": {{$body.event.data.new.id}},\r\n \"ro_number\": {{$body.event.data.old.ro_number}},\r\n \"queued_for_parts\": {{$body.event.data.new.queued_for_parts}},\r\n \"employee_prep\": {{$body.event.data.new.employee_prep}},\r\n \"clm_total\": {{$body.event.data.new.clm_total}},\r\n \"towin\": {{$body.event.data.new.towin}},\r\n \"employee_body\": {{$body.event.data.new.employee_body}},\r\n \"converted\": {{$body.event.data.new.converted}},\r\n \"scheduled_in\": {{$body.event.data.new.scheduled_in}},\r\n \"scheduled_completion\": {{$body.event.data.new.scheduled_completion}},\r\n \"scheduled_delivery\": {{$body.event.data.new.scheduled_delivery}},\r\n \"actual_delivery\": {{$body.event.data.new.actual_delivery}},\r\n \"actual_completion\": {{$body.event.data.new.actual_completion}},\r\n \"alt_transport\": {{$body.event.data.new.alt_transport}},\r\n \"date_exported\": {{$body.event.data.new.date_exported}},\r\n \"status\": {{$body.event.data.new.status}},\r\n \"employee_csr\": {{$body.event.data.new.employee_csr}},\r\n \"actual_in\": {{$body.event.data.new.actual_in}},\r\n \"deliverchecklist\": {{$body.event.data.new.deliverchecklist}},\r\n \"comment\": {{$body.event.data.new.comment}},\r\n \"employee_refinish\": {{$body.event.data.new.employee_refinish}},\r\n \"inproduction\": {{$body.event.data.new.inproduction}},\r\n \"production_vars\": {{$body.event.data.new.production_vars}},\r\n \"intakechecklist\": {{$body.event.data.new.intakechecklist}},\r\n \"cieca_ttl\": {{$body.event.data.new.cieca_ttl}},\r\n \"date_invoiced\": {{$body.event.data.new.date_invoiced}}\r\n }\r\n }\r\n },\r\n \"trigger\": {\r\n \"name\": \"notifications_jobs\"\r\n },\r\n \"table\": {\r\n \"schema\": \"public\",\r\n \"name\": \"jobs\"\r\n }\r\n}\r\n"
|
||||
template: "{\r\n \"event\": {\r\n \"session_variables\": {\r\n \"x-hasura-user-id\": {{$body?.event?.session_variables?.x-hasura-user-id ?? \"Internal\"}}\r\n }, \r\n \"op\": \"UPDATE\",\r\n \"data\": {\r\n \"old\": {\r\n \"id\": {{$body.event.data.old.id}},\r\n \"ro_number\": {{$body.event.data.old.ro_number}},\r\n \"queued_for_parts\": {{$body.event.data.old.queued_for_parts}},\r\n \"employee_prep\": {{$body.event.data.old.employee_prep}},\r\n \"clm_total\": {{$body.event.data.old.clm_total}},\r\n \"towin\": {{$body.event.data.old.towin}},\r\n \"employee_body\": {{$body.event.data.old.employee_body}},\r\n \"converted\": {{$body.event.data.old.converted}},\r\n \"scheduled_in\": {{$body.event.data.old.scheduled_in}},\r\n \"scheduled_completion\": {{$body.event.data.old.scheduled_completion}},\r\n \"scheduled_delivery\": {{$body.event.data.old.scheduled_delivery}},\r\n \"actual_delivery\": {{$body.event.data.old.actual_delivery}},\r\n \"actual_completion\": {{$body.event.data.old.actual_completion}},\r\n \"alt_transport\": {{$body.event.data.old.alt_transport}},\r\n \"date_exported\": {{$body.event.data.old.date_exported}},\r\n \"status\": {{$body.event.data.old.status}},\r\n \"employee_csr\": {{$body.event.data.old.employee_csr}},\r\n \"actual_in\": {{$body.event.data.old.actual_in}},\r\n \"deliverchecklist\": {{$body.event.data.old.deliverchecklist}},\r\n \"comment\": {{$body.event.data.old.comment}},\r\n \"employee_refinish\": {{$body.event.data.old.employee_refinish}},\r\n \"inproduction\": {{$body.event.data.old.inproduction}},\r\n \"production_vars\": {{$body.event.data.old.production_vars}},\r\n \"intakechecklist\": {{$body.event.data.old.intakechecklist}},\r\n \"cieca_ttl\": {{$body.event.data.old.cieca_ttl}},\r\n \"date_invoiced\": {{$body.event.data.old.date_invoiced}}\r\n },\r\n \"new\": {\r\n \"id\": {{$body.event.data.new.id}},\r\n \"ro_number\": {{$body.event.data.old.ro_number}},\r\n \"queued_for_parts\": {{$body.event.data.new.queued_for_parts}},\r\n \"employee_prep\": {{$body.event.data.new.employee_prep}},\r\n \"clm_total\": {{$body.event.data.new.clm_total}},\r\n \"towin\": {{$body.event.data.new.towin}},\r\n \"employee_body\": {{$body.event.data.new.employee_body}},\r\n \"converted\": {{$body.event.data.new.converted}},\r\n \"scheduled_in\": {{$body.event.data.new.scheduled_in}},\r\n \"scheduled_completion\": {{$body.event.data.new.scheduled_completion}},\r\n \"scheduled_delivery\": {{$body.event.data.new.scheduled_delivery}},\r\n \"actual_delivery\": {{$body.event.data.new.actual_delivery}},\r\n \"actual_completion\": {{$body.event.data.new.actual_completion}},\r\n \"alt_transport\": {{$body.event.data.new.alt_transport}},\r\n \"date_exported\": {{$body.event.data.new.date_exported}},\r\n \"status\": {{$body.event.data.new.status}},\r\n \"employee_csr\": {{$body.event.data.new.employee_csr}},\r\n \"actual_in\": {{$body.event.data.new.actual_in}},\r\n \"deliverchecklist\": {{$body.event.data.new.deliverchecklist}},\r\n \"comment\": {{$body.event.data.new.comment}},\r\n \"employee_refinish\": {{$body.event.data.new.employee_refinish}},\r\n \"inproduction\": {{$body.event.data.new.inproduction}},\r\n \"production_vars\": {{$body.event.data.new.production_vars}},\r\n \"intakechecklist\": {{$body.event.data.new.intakechecklist}},\r\n \"cieca_ttl\": {{$body.event.data.new.cieca_ttl}},\r\n \"date_invoiced\": {{$body.event.data.new.date_invoiced}}\r\n }\r\n }\r\n },\r\n \"trigger\": {\r\n \"name\": \"notifications_jobs\"\r\n },\r\n \"table\": {\r\n \"schema\": \"public\",\r\n \"name\": \"jobs\"\r\n }\r\n}\r\n"
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
|
||||
@@ -118,12 +118,12 @@ const loadEmailQueue = async ({ pubClient, logger }) => {
|
||||
const details = await pubClient.hgetall(detailsKey);
|
||||
const firstName = details.firstName || "User";
|
||||
const multipleUpdateString = messages.length > 1 ? "Updates" : "Update";
|
||||
const subject = `${multipleUpdateString} for job ${jobRoNumber} at ${bodyShopName}`;
|
||||
const subject = `${multipleUpdateString} for job ${jobRoNumber || "N/A"} at ${bodyShopName}`;
|
||||
const emailBody = generateEmailTemplate({
|
||||
header: `${multipleUpdateString} for Job ${jobRoNumber}`,
|
||||
header: `${multipleUpdateString} for Job ${jobRoNumber || "N/A"}`,
|
||||
subHeader: `Dear ${firstName},`,
|
||||
body: `
|
||||
<p>There have been updates to job ${jobRoNumber} at ${bodyShopName}:</p><br/>
|
||||
<p>There have been updates to job ${jobRoNumber || "N/A"} at ${bodyShopName}:</p><br/>
|
||||
<ul>
|
||||
${messages.map((msg) => `<li>${msg}</li>`).join("")}
|
||||
</ul><br/><br/>
|
||||
@@ -224,7 +224,7 @@ const dispatchEmailsToQueue = async ({ emailsToDispatch, logger }) => {
|
||||
if (!jobId || !jobRoNumber || !bodyShopName || !body || !recipients.length) {
|
||||
logger.logger.warn(
|
||||
`Skipping email dispatch for jobId ${jobId} due to missing data: ` +
|
||||
`jobRoNumber=${jobRoNumber}, bodyShopName=${bodyShopName}, body=${body}, recipients=${recipients.length}`
|
||||
`jobRoNumber=${jobRoNumber || "N/A"}, bodyShopName=${bodyShopName}, body=${body}, recipients=${recipients.length}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const { getJobAssignmentType, formatTaskPriority } = require("./stringHelpers");
|
||||
const moment = require("moment-timezone");
|
||||
const { startCase } = require("lodash");
|
||||
const Dinero = require("dinero.js");
|
||||
|
||||
Dinero.globalRoundingMode = "HALF_EVEN";
|
||||
|
||||
/**
|
||||
* Populates the recipients for app, email, and FCM notifications based on scenario watchers.
|
||||
@@ -26,7 +29,6 @@ const populateWatchers = (data, result) => {
|
||||
/**
|
||||
* Builds notification data for changes to alternate transport.
|
||||
*/
|
||||
// Verified
|
||||
const alternateTransportChangedBuilder = (data) => {
|
||||
const body = `The Alternate Transport status has been updated from ${data.changedFields.alt_transport?.old || "unset"} to ${data?.changedFields?.alt_transport?.new || "unset"}.`;
|
||||
const result = {
|
||||
@@ -59,7 +61,6 @@ const alternateTransportChangedBuilder = (data) => {
|
||||
/**
|
||||
* Builds notification data for bill posted events.
|
||||
*/
|
||||
//verified
|
||||
const billPostedHandler = (data) => {
|
||||
const facing = data?.data?.isinhouse ? "In-House" : "External";
|
||||
const body = `An ${facing} Bill has been posted${data?.data?.is_credit_memo ? " (Credit Memo)" : ""}.`.trim();
|
||||
@@ -94,9 +95,10 @@ const billPostedHandler = (data) => {
|
||||
/**
|
||||
* Builds notification data for changes to critical parts status.
|
||||
*/
|
||||
// TODO: Needs change
|
||||
//
|
||||
const criticalPartsStatusChangedBuilder = (data) => {
|
||||
const body = `The critical parts status has changed to ${data?.data?.queued_for_parts ? "queued" : "not queued"}.`;
|
||||
const body = `A Critical Job Line status has been changed."`;
|
||||
|
||||
const result = {
|
||||
app: {
|
||||
jobId: data.jobId,
|
||||
@@ -105,8 +107,7 @@ const criticalPartsStatusChangedBuilder = (data) => {
|
||||
key: "notifications.job.criticalPartsStatusChanged",
|
||||
body,
|
||||
variables: {
|
||||
queuedForParts: data?.data?.queued_for_parts,
|
||||
oldQueuedForParts: data?.changedFields?.queued_for_parts?.old
|
||||
joblineId: data?.data?.id // If we want to deeplink to the jobline
|
||||
},
|
||||
recipients: []
|
||||
},
|
||||
@@ -127,7 +128,6 @@ const criticalPartsStatusChangedBuilder = (data) => {
|
||||
/**
|
||||
* Builds notification data for completed intake or delivery checklists.
|
||||
*/
|
||||
// Verified
|
||||
const intakeDeliveryChecklistCompletedBuilder = (data) => {
|
||||
const checklistType = data?.changedFields?.intakechecklist ? "Intake" : "Delivery";
|
||||
const body = `The ${checklistType.charAt(0).toUpperCase() + checklistType.slice(1)} checklist has been completed.`;
|
||||
@@ -161,7 +161,6 @@ const intakeDeliveryChecklistCompletedBuilder = (data) => {
|
||||
/**
|
||||
* Builds notification data for job assignment events.
|
||||
*/
|
||||
// Verified
|
||||
const jobAssignedToMeBuilder = (data) => {
|
||||
const body = `You have been assigned to ${getJobAssignmentType(data.scenarioFields?.[0])}.`;
|
||||
const result = {
|
||||
@@ -193,7 +192,6 @@ const jobAssignedToMeBuilder = (data) => {
|
||||
/**
|
||||
* Builds notification data for jobs added to production.
|
||||
*/
|
||||
// Verified
|
||||
const jobsAddedToProductionBuilder = (data) => {
|
||||
const body = `Has been added to Production.`;
|
||||
const result = {
|
||||
@@ -223,7 +221,6 @@ const jobsAddedToProductionBuilder = (data) => {
|
||||
/**
|
||||
* Builds notification data for job status changes.
|
||||
*/
|
||||
// Verified
|
||||
const jobStatusChangeBuilder = (data) => {
|
||||
const body = `The status has changed from ${data?.changedFields?.status?.old || "unset"} to ${data?.changedFields?.status?.new || "unset"}`;
|
||||
const result = {
|
||||
@@ -256,7 +253,6 @@ const jobStatusChangeBuilder = (data) => {
|
||||
/**
|
||||
* Builds notification data for new media added or reassigned events.
|
||||
*/
|
||||
// Verified
|
||||
const newMediaAddedReassignedBuilder = (data) => {
|
||||
// Determine if it's an image or document
|
||||
const mediaType = data?.data?.type?.startsWith("image") ? "Image" : "Document";
|
||||
@@ -295,7 +291,6 @@ const newMediaAddedReassignedBuilder = (data) => {
|
||||
/**
|
||||
* Builds notification data for new notes added to a job.
|
||||
*/
|
||||
// verified
|
||||
const newNoteAddedBuilder = (data) => {
|
||||
const body = [
|
||||
"A",
|
||||
@@ -340,7 +335,6 @@ const newNoteAddedBuilder = (data) => {
|
||||
/**
|
||||
* Builds notification data for new time tickets posted.
|
||||
*/
|
||||
// Verified
|
||||
const newTimeTicketPostedBuilder = (data) => {
|
||||
const type = data?.data?.cost_center;
|
||||
const body =
|
||||
@@ -407,7 +401,22 @@ const partMarkedBackOrderedBuilder = (data) => {
|
||||
* Builds notification data for payment collection events.
|
||||
*/
|
||||
const paymentCollectedCompletedBuilder = (data) => {
|
||||
const body = `Payment of $${data.data.clm_total} has been collected.`;
|
||||
const momentFormat = "MM/DD/YYYY";
|
||||
const timezone = data.bodyShopTimezone;
|
||||
|
||||
// Format amount using Dinero.js
|
||||
const amountDinero = Dinero({
|
||||
amount: Math.round((data.data.amount || 0) * 100) // Convert to cents, default to 0 if missing
|
||||
});
|
||||
|
||||
const amountFormatted = amountDinero.toFormat();
|
||||
|
||||
const payer = data.data.payer;
|
||||
const paymentType = data.data.type;
|
||||
const paymentDate = moment(data.data.date).tz(timezone).format(momentFormat);
|
||||
|
||||
const body = `Payment of ${amountFormatted} has been collected from ${payer} via ${paymentType} on ${paymentDate}`;
|
||||
|
||||
const result = {
|
||||
app: {
|
||||
jobId: data.jobId,
|
||||
@@ -416,7 +425,10 @@ const paymentCollectedCompletedBuilder = (data) => {
|
||||
key: "notifications.job.paymentCollected",
|
||||
body,
|
||||
variables: {
|
||||
clmTotal: data.data.clm_total
|
||||
amount: data.data.amount,
|
||||
payer: data.data.payer,
|
||||
type: data.data.type,
|
||||
date: data.data.date
|
||||
},
|
||||
recipients: []
|
||||
},
|
||||
@@ -491,20 +503,94 @@ const scheduledDatesChangedBuilder = (data) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds notification data for supplement imported events.
|
||||
* Builds notification data for tasks updated or created.
|
||||
*/
|
||||
const supplementImportedBuilder = (data) => {
|
||||
const body = `A supplement of $${data.data.cieca_ttl?.data?.supp_amt || 0} has been imported.`;
|
||||
const tasksUpdatedCreatedBuilder = (data) => {
|
||||
const momentFormat = "MM/DD/YYYY hh:mm a";
|
||||
const timezone = data.bodyShopTimezone;
|
||||
const taskTitle = data?.data?.title ? `"${data.data.title}"` : "Unnamed Task";
|
||||
|
||||
let body;
|
||||
let variables;
|
||||
|
||||
if (data.isNew) {
|
||||
// Created case
|
||||
const priority = formatTaskPriority(data?.data?.priority);
|
||||
const createdBy = data?.data?.created_by || "Unknown"; // Fallback for undefined created_by
|
||||
const dueDate = data.data.due_date ? ` due on ${moment(data.data.due_date).tz(timezone).format(momentFormat)}` : "";
|
||||
const completedOnCreation = data.data.completed === true;
|
||||
body = `A ${priority} Task ${taskTitle} has been created${completedOnCreation ? " and marked completed" : ""} by ${createdBy}${dueDate}`;
|
||||
variables = {
|
||||
isNew: data.isNew,
|
||||
roNumber: data.jobRoNumber,
|
||||
title: data?.data?.title,
|
||||
priority: data?.data?.priority,
|
||||
createdBy: data?.data?.created_by,
|
||||
dueDate: data?.data?.due_date,
|
||||
completed: completedOnCreation ? data?.data?.completed : undefined // Only include if true
|
||||
};
|
||||
} else {
|
||||
// Updated case
|
||||
const changedFields = data.changedFields;
|
||||
const fieldNames = Object.keys(changedFields);
|
||||
const oldTitle = changedFields.title ? `"${changedFields.title.old || "Unnamed Task"}"` : taskTitle;
|
||||
|
||||
// Special case: Only 'completed' changed
|
||||
if (fieldNames.length === 1 && changedFields.completed) {
|
||||
body = `Task ${oldTitle} was marked ${changedFields.completed.new ? "complete" : "incomplete"}`;
|
||||
variables = {
|
||||
isNew: data.isNew,
|
||||
roNumber: data.jobRoNumber,
|
||||
title: data?.data?.title,
|
||||
changedCompleted: data?.changedFields?.completed?.new
|
||||
};
|
||||
} else {
|
||||
// General update case
|
||||
const fieldMessages = [];
|
||||
|
||||
if (changedFields.title) {
|
||||
fieldMessages.push(`Task ${oldTitle} changed title to "${changedFields.title.new || "Unnamed Task"}"`);
|
||||
}
|
||||
if (changedFields.description) {
|
||||
fieldMessages.push("Description Updated");
|
||||
}
|
||||
if (changedFields.priority) {
|
||||
fieldMessages.push(`Priority changed to ${formatTaskPriority(changedFields.priority.new)}`);
|
||||
}
|
||||
if (changedFields.due_date) {
|
||||
fieldMessages.push(`Due date set to ${moment(changedFields.due_date.new).tz(timezone).format(momentFormat)}`);
|
||||
}
|
||||
if (changedFields.completed) {
|
||||
fieldMessages.push(`Status changed to ${changedFields.completed.new ? "complete" : "incomplete"}`);
|
||||
}
|
||||
|
||||
body =
|
||||
fieldMessages.length > 0
|
||||
? fieldMessages.length === 1 && changedFields.title
|
||||
? fieldMessages[0] // If only title changed, use it standalone
|
||||
: `Task ${oldTitle} updated: ${fieldMessages.join(", ")}`
|
||||
: `Task ${oldTitle} has been updated.`;
|
||||
variables = {
|
||||
isNew: data.isNew,
|
||||
roNumber: data.jobRoNumber,
|
||||
title: data?.data?.title,
|
||||
changedTitleOld: data?.changedFields?.title?.old,
|
||||
changedTitleNew: data?.changedFields?.title?.new,
|
||||
changedPriority: data?.changedFields?.priority?.new,
|
||||
changedDueDate: data?.changedFields?.due_date?.new,
|
||||
changedCompleted: data?.changedFields?.completed?.new
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const result = {
|
||||
app: {
|
||||
jobId: data.jobId,
|
||||
jobRoNumber: data.jobRoNumber,
|
||||
bodyShopId: data.bodyShopId,
|
||||
key: "notifications.job.supplementImported",
|
||||
key: data.isNew ? "notifications.job.taskCreated" : "notifications.job.taskUpdated",
|
||||
body,
|
||||
variables: {
|
||||
suppAmt: data.data.cieca_ttl?.data?.supp_amt
|
||||
},
|
||||
variables,
|
||||
recipients: []
|
||||
},
|
||||
email: {
|
||||
@@ -522,86 +608,19 @@ const supplementImportedBuilder = (data) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds notification data for tasks updated or created.
|
||||
* Builds notification data for supplement imported events.
|
||||
* TODO: This is an advanced case and will be done later
|
||||
*/
|
||||
const tasksUpdatedCreatedBuilder = (data) => {
|
||||
const momentFormat = "MM/DD/YYYY hh:mm a";
|
||||
const timezone = data.bodyShopTimezone;
|
||||
const taskTitle = data?.data?.title;
|
||||
|
||||
let body;
|
||||
let variables;
|
||||
|
||||
if (data.isNew) {
|
||||
// Created case
|
||||
const priority = formatTaskPriority(data?.data?.priority);
|
||||
const createdBy = data?.data?.created_by;
|
||||
const dueDate = data.data.due_date ? ` due on ${moment(data.data.due_date).tz(timezone).format(momentFormat)}` : "";
|
||||
const completedOnCreation = data.data.completed === true;
|
||||
body = `A ${priority} Task ${taskTitle} has been created${completedOnCreation ? " and marked completed" : ""} by ${createdBy}${dueDate}`;
|
||||
variables = {
|
||||
isNew: data.isNew,
|
||||
roNumber: data.jobRoNumber,
|
||||
title: data?.data?.title,
|
||||
priority: data?.data?.priority,
|
||||
createdBy: data?.data?.created_by,
|
||||
dueDate: data?.data?.due_date,
|
||||
completed: completedOnCreation ? data?.data?.completed : undefined // Only include if true
|
||||
};
|
||||
} else {
|
||||
// Updated case
|
||||
const changedFields = data.changedFields;
|
||||
const fieldNames = Object.keys(changedFields);
|
||||
|
||||
// Special case: Only 'completed' changed
|
||||
if (fieldNames.length === 1 && changedFields.completed) {
|
||||
body = `Task ${taskTitle} was marked ${changedFields.completed.new ? "complete" : "incomplete"}`;
|
||||
variables = {
|
||||
isNew: data.isNew,
|
||||
roNumber: data.jobRoNumber,
|
||||
title: data?.data?.title,
|
||||
changedCompleted: data?.changedFields?.completed?.new
|
||||
};
|
||||
} else {
|
||||
// General update case
|
||||
const fieldMessages = [];
|
||||
|
||||
if (changedFields.description) {
|
||||
fieldMessages.push("Description Updated");
|
||||
}
|
||||
if (changedFields.priority) {
|
||||
fieldMessages.push(`Priority changed to ${formatTaskPriority(changedFields.priority.new)}`);
|
||||
}
|
||||
if (changedFields.due_date) {
|
||||
fieldMessages.push(`Due date set to ${moment(changedFields.due_date.new).tz(timezone).format(momentFormat)}`);
|
||||
}
|
||||
if (changedFields.completed) {
|
||||
fieldMessages.push(`Status changed to ${changedFields.completed.new ? "complete" : "incomplete"}`);
|
||||
}
|
||||
|
||||
body =
|
||||
fieldMessages.length > 0
|
||||
? `Task ${taskTitle} updated: ${fieldMessages.join(", ")}`
|
||||
: `Task ${taskTitle} has been updated.`;
|
||||
variables = {
|
||||
isNew: data.isNew,
|
||||
roNumber: data.jobRoNumber,
|
||||
title: data?.data?.title,
|
||||
changedPriority: data?.changedFields?.priority?.new,
|
||||
changedDueDate: data?.changedFields?.due_date?.new,
|
||||
changedCompleted: data?.changedFields?.completed?.new
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const supplementImportedBuilder = (data) => {
|
||||
const body = `A supplement has been imported.`;
|
||||
const result = {
|
||||
app: {
|
||||
jobId: data.jobId,
|
||||
jobRoNumber: data.jobRoNumber,
|
||||
bodyShopId: data.bodyShopId,
|
||||
key: data.isNew ? "notifications.job.taskCreated" : "notifications.job.taskUpdated",
|
||||
key: "notifications.job.supplementImported",
|
||||
body,
|
||||
variables,
|
||||
variables: {},
|
||||
recipients: []
|
||||
},
|
||||
email: {
|
||||
|
||||
@@ -95,7 +95,7 @@ const notificationScenarios = [
|
||||
builder: intakeDeliveryChecklistCompletedBuilder
|
||||
},
|
||||
{
|
||||
key: "payment-added",
|
||||
key: "payment-collected-completed",
|
||||
table: "payments",
|
||||
onNew: true,
|
||||
builder: paymentCollectedCompletedBuilder
|
||||
@@ -110,9 +110,10 @@ const notificationScenarios = [
|
||||
{
|
||||
key: "critical-parts-status-changed",
|
||||
table: "joblines",
|
||||
fields: ["critical"],
|
||||
onlyTruthyValues: ["critical"],
|
||||
builder: criticalPartsStatusChangedBuilder
|
||||
fields: ["status"],
|
||||
onlyTruthyValues: ["status"],
|
||||
builder: criticalPartsStatusChangedBuilder,
|
||||
filterCallback: ({ eventData }) => !eventData?.data?.critical
|
||||
},
|
||||
{
|
||||
key: "part-marked-back-ordered",
|
||||
|
||||
Reference in New Issue
Block a user