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",
|
key: "recent",
|
||||||
id: "header-recent",
|
id: "header-recent",
|
||||||
icon: <ClockCircleFilled />,
|
icon: <ClockCircleFilled />,
|
||||||
|
label: t("menus.header.recent"),
|
||||||
children: recentItems.map((i, idx) => ({
|
children: recentItems.map((i, idx) => ({
|
||||||
key: idx,
|
key: idx,
|
||||||
id: `header-recent-${idx}`,
|
id: `header-recent-${idx}`,
|
||||||
@@ -633,6 +634,7 @@ function Header({
|
|||||||
key: "user",
|
key: "user",
|
||||||
id: "header-user",
|
id: "header-user",
|
||||||
icon: <UserOutlined />,
|
icon: <UserOutlined />,
|
||||||
|
label: t("menus.currentuser.profile"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "signout",
|
key: "signout",
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export default function JobWatcherToggleComponent({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
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")}>
|
<Tooltip title={t("notifications.tooltips.job-watchers")}>
|
||||||
<Button
|
<Button
|
||||||
shape="circle"
|
shape="circle"
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const NotificationCenterComponent = forwardRef(
|
|||||||
<div className="notification-content">
|
<div className="notification-content">
|
||||||
<Title level={5} className="notification-title">
|
<Title level={5} className="notification-title">
|
||||||
<span className="ro-number">
|
<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>
|
</span>
|
||||||
<Text type="secondary" className="relative-time" title={DateTimeFormat(notification.created_at)}>
|
<Text type="secondary" className="relative-time" title={DateTimeFormat(notification.created_at)}>
|
||||||
{day(notification.created_at).fromNow()}
|
{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 || ""}`));
|
.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>
|
</div>
|
||||||
),
|
),
|
||||||
description: (
|
description: (
|
||||||
|
|||||||
@@ -3303,7 +3303,7 @@
|
|||||||
request_transform:
|
request_transform:
|
||||||
body:
|
body:
|
||||||
action: transform
|
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
|
method: POST
|
||||||
query_params: {}
|
query_params: {}
|
||||||
template_engine: Kriti
|
template_engine: Kriti
|
||||||
@@ -4573,7 +4573,7 @@
|
|||||||
request_transform:
|
request_transform:
|
||||||
body:
|
body:
|
||||||
action: transform
|
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
|
method: POST
|
||||||
query_params: {}
|
query_params: {}
|
||||||
template_engine: Kriti
|
template_engine: Kriti
|
||||||
|
|||||||
@@ -118,12 +118,12 @@ const loadEmailQueue = async ({ pubClient, logger }) => {
|
|||||||
const details = await pubClient.hgetall(detailsKey);
|
const details = await pubClient.hgetall(detailsKey);
|
||||||
const firstName = details.firstName || "User";
|
const firstName = details.firstName || "User";
|
||||||
const multipleUpdateString = messages.length > 1 ? "Updates" : "Update";
|
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({
|
const emailBody = generateEmailTemplate({
|
||||||
header: `${multipleUpdateString} for Job ${jobRoNumber}`,
|
header: `${multipleUpdateString} for Job ${jobRoNumber || "N/A"}`,
|
||||||
subHeader: `Dear ${firstName},`,
|
subHeader: `Dear ${firstName},`,
|
||||||
body: `
|
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>
|
<ul>
|
||||||
${messages.map((msg) => `<li>${msg}</li>`).join("")}
|
${messages.map((msg) => `<li>${msg}</li>`).join("")}
|
||||||
</ul><br/><br/>
|
</ul><br/><br/>
|
||||||
@@ -224,7 +224,7 @@ const dispatchEmailsToQueue = async ({ emailsToDispatch, logger }) => {
|
|||||||
if (!jobId || !jobRoNumber || !bodyShopName || !body || !recipients.length) {
|
if (!jobId || !jobRoNumber || !bodyShopName || !body || !recipients.length) {
|
||||||
logger.logger.warn(
|
logger.logger.warn(
|
||||||
`Skipping email dispatch for jobId ${jobId} due to missing data: ` +
|
`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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
const { getJobAssignmentType, formatTaskPriority } = require("./stringHelpers");
|
const { getJobAssignmentType, formatTaskPriority } = require("./stringHelpers");
|
||||||
const moment = require("moment-timezone");
|
const moment = require("moment-timezone");
|
||||||
const { startCase } = require("lodash");
|
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.
|
* 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.
|
* Builds notification data for changes to alternate transport.
|
||||||
*/
|
*/
|
||||||
// Verified
|
|
||||||
const alternateTransportChangedBuilder = (data) => {
|
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 body = `The Alternate Transport status has been updated from ${data.changedFields.alt_transport?.old || "unset"} to ${data?.changedFields?.alt_transport?.new || "unset"}.`;
|
||||||
const result = {
|
const result = {
|
||||||
@@ -59,7 +61,6 @@ const alternateTransportChangedBuilder = (data) => {
|
|||||||
/**
|
/**
|
||||||
* Builds notification data for bill posted events.
|
* Builds notification data for bill posted events.
|
||||||
*/
|
*/
|
||||||
//verified
|
|
||||||
const billPostedHandler = (data) => {
|
const billPostedHandler = (data) => {
|
||||||
const facing = data?.data?.isinhouse ? "In-House" : "External";
|
const facing = data?.data?.isinhouse ? "In-House" : "External";
|
||||||
const body = `An ${facing} Bill has been posted${data?.data?.is_credit_memo ? " (Credit Memo)" : ""}.`.trim();
|
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.
|
* Builds notification data for changes to critical parts status.
|
||||||
*/
|
*/
|
||||||
// TODO: Needs change
|
//
|
||||||
const criticalPartsStatusChangedBuilder = (data) => {
|
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 = {
|
const result = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
@@ -105,8 +107,7 @@ const criticalPartsStatusChangedBuilder = (data) => {
|
|||||||
key: "notifications.job.criticalPartsStatusChanged",
|
key: "notifications.job.criticalPartsStatusChanged",
|
||||||
body,
|
body,
|
||||||
variables: {
|
variables: {
|
||||||
queuedForParts: data?.data?.queued_for_parts,
|
joblineId: data?.data?.id // If we want to deeplink to the jobline
|
||||||
oldQueuedForParts: data?.changedFields?.queued_for_parts?.old
|
|
||||||
},
|
},
|
||||||
recipients: []
|
recipients: []
|
||||||
},
|
},
|
||||||
@@ -127,7 +128,6 @@ const criticalPartsStatusChangedBuilder = (data) => {
|
|||||||
/**
|
/**
|
||||||
* Builds notification data for completed intake or delivery checklists.
|
* Builds notification data for completed intake or delivery checklists.
|
||||||
*/
|
*/
|
||||||
// Verified
|
|
||||||
const intakeDeliveryChecklistCompletedBuilder = (data) => {
|
const intakeDeliveryChecklistCompletedBuilder = (data) => {
|
||||||
const checklistType = data?.changedFields?.intakechecklist ? "Intake" : "Delivery";
|
const checklistType = data?.changedFields?.intakechecklist ? "Intake" : "Delivery";
|
||||||
const body = `The ${checklistType.charAt(0).toUpperCase() + checklistType.slice(1)} checklist has been completed.`;
|
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.
|
* Builds notification data for job assignment events.
|
||||||
*/
|
*/
|
||||||
// Verified
|
|
||||||
const jobAssignedToMeBuilder = (data) => {
|
const jobAssignedToMeBuilder = (data) => {
|
||||||
const body = `You have been assigned to ${getJobAssignmentType(data.scenarioFields?.[0])}.`;
|
const body = `You have been assigned to ${getJobAssignmentType(data.scenarioFields?.[0])}.`;
|
||||||
const result = {
|
const result = {
|
||||||
@@ -193,7 +192,6 @@ const jobAssignedToMeBuilder = (data) => {
|
|||||||
/**
|
/**
|
||||||
* Builds notification data for jobs added to production.
|
* Builds notification data for jobs added to production.
|
||||||
*/
|
*/
|
||||||
// Verified
|
|
||||||
const jobsAddedToProductionBuilder = (data) => {
|
const jobsAddedToProductionBuilder = (data) => {
|
||||||
const body = `Has been added to Production.`;
|
const body = `Has been added to Production.`;
|
||||||
const result = {
|
const result = {
|
||||||
@@ -223,7 +221,6 @@ const jobsAddedToProductionBuilder = (data) => {
|
|||||||
/**
|
/**
|
||||||
* Builds notification data for job status changes.
|
* Builds notification data for job status changes.
|
||||||
*/
|
*/
|
||||||
// Verified
|
|
||||||
const jobStatusChangeBuilder = (data) => {
|
const jobStatusChangeBuilder = (data) => {
|
||||||
const body = `The status has changed from ${data?.changedFields?.status?.old || "unset"} to ${data?.changedFields?.status?.new || "unset"}`;
|
const body = `The status has changed from ${data?.changedFields?.status?.old || "unset"} to ${data?.changedFields?.status?.new || "unset"}`;
|
||||||
const result = {
|
const result = {
|
||||||
@@ -256,7 +253,6 @@ const jobStatusChangeBuilder = (data) => {
|
|||||||
/**
|
/**
|
||||||
* Builds notification data for new media added or reassigned events.
|
* Builds notification data for new media added or reassigned events.
|
||||||
*/
|
*/
|
||||||
// Verified
|
|
||||||
const newMediaAddedReassignedBuilder = (data) => {
|
const newMediaAddedReassignedBuilder = (data) => {
|
||||||
// Determine if it's an image or document
|
// Determine if it's an image or document
|
||||||
const mediaType = data?.data?.type?.startsWith("image") ? "Image" : "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.
|
* Builds notification data for new notes added to a job.
|
||||||
*/
|
*/
|
||||||
// verified
|
|
||||||
const newNoteAddedBuilder = (data) => {
|
const newNoteAddedBuilder = (data) => {
|
||||||
const body = [
|
const body = [
|
||||||
"A",
|
"A",
|
||||||
@@ -340,7 +335,6 @@ const newNoteAddedBuilder = (data) => {
|
|||||||
/**
|
/**
|
||||||
* Builds notification data for new time tickets posted.
|
* Builds notification data for new time tickets posted.
|
||||||
*/
|
*/
|
||||||
// Verified
|
|
||||||
const newTimeTicketPostedBuilder = (data) => {
|
const newTimeTicketPostedBuilder = (data) => {
|
||||||
const type = data?.data?.cost_center;
|
const type = data?.data?.cost_center;
|
||||||
const body =
|
const body =
|
||||||
@@ -407,7 +401,22 @@ const partMarkedBackOrderedBuilder = (data) => {
|
|||||||
* Builds notification data for payment collection events.
|
* Builds notification data for payment collection events.
|
||||||
*/
|
*/
|
||||||
const paymentCollectedCompletedBuilder = (data) => {
|
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 = {
|
const result = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
@@ -416,7 +425,10 @@ const paymentCollectedCompletedBuilder = (data) => {
|
|||||||
key: "notifications.job.paymentCollected",
|
key: "notifications.job.paymentCollected",
|
||||||
body,
|
body,
|
||||||
variables: {
|
variables: {
|
||||||
clmTotal: data.data.clm_total
|
amount: data.data.amount,
|
||||||
|
payer: data.data.payer,
|
||||||
|
type: data.data.type,
|
||||||
|
date: data.data.date
|
||||||
},
|
},
|
||||||
recipients: []
|
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 tasksUpdatedCreatedBuilder = (data) => {
|
||||||
const body = `A supplement of $${data.data.cieca_ttl?.data?.supp_amt || 0} has been imported.`;
|
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 = {
|
const result = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
jobRoNumber: data.jobRoNumber,
|
jobRoNumber: data.jobRoNumber,
|
||||||
bodyShopId: data.bodyShopId,
|
bodyShopId: data.bodyShopId,
|
||||||
key: "notifications.job.supplementImported",
|
key: data.isNew ? "notifications.job.taskCreated" : "notifications.job.taskUpdated",
|
||||||
body,
|
body,
|
||||||
variables: {
|
variables,
|
||||||
suppAmt: data.data.cieca_ttl?.data?.supp_amt
|
|
||||||
},
|
|
||||||
recipients: []
|
recipients: []
|
||||||
},
|
},
|
||||||
email: {
|
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 supplementImportedBuilder = (data) => {
|
||||||
const momentFormat = "MM/DD/YYYY hh:mm a";
|
const body = `A supplement has been imported.`;
|
||||||
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 result = {
|
const result = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
jobRoNumber: data.jobRoNumber,
|
jobRoNumber: data.jobRoNumber,
|
||||||
bodyShopId: data.bodyShopId,
|
bodyShopId: data.bodyShopId,
|
||||||
key: data.isNew ? "notifications.job.taskCreated" : "notifications.job.taskUpdated",
|
key: "notifications.job.supplementImported",
|
||||||
body,
|
body,
|
||||||
variables,
|
variables: {},
|
||||||
recipients: []
|
recipients: []
|
||||||
},
|
},
|
||||||
email: {
|
email: {
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ const notificationScenarios = [
|
|||||||
builder: intakeDeliveryChecklistCompletedBuilder
|
builder: intakeDeliveryChecklistCompletedBuilder
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "payment-added",
|
key: "payment-collected-completed",
|
||||||
table: "payments",
|
table: "payments",
|
||||||
onNew: true,
|
onNew: true,
|
||||||
builder: paymentCollectedCompletedBuilder
|
builder: paymentCollectedCompletedBuilder
|
||||||
@@ -110,9 +110,10 @@ const notificationScenarios = [
|
|||||||
{
|
{
|
||||||
key: "critical-parts-status-changed",
|
key: "critical-parts-status-changed",
|
||||||
table: "joblines",
|
table: "joblines",
|
||||||
fields: ["critical"],
|
fields: ["status"],
|
||||||
onlyTruthyValues: ["critical"],
|
onlyTruthyValues: ["status"],
|
||||||
builder: criticalPartsStatusChangedBuilder
|
builder: criticalPartsStatusChangedBuilder,
|
||||||
|
filterCallback: ({ eventData }) => !eventData?.data?.critical
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "part-marked-back-ordered",
|
key: "part-marked-back-ordered",
|
||||||
|
|||||||
Reference in New Issue
Block a user