Merged in feature/IO-3166-Global-Notifications-Part-2 (pull request #2156)
Feature/IO-3166 Global Notifications Part 2
This commit is contained in:
@@ -4,6 +4,7 @@ import { Avatar, Button, Divider, List, Popover, Select, Tooltip, Typography } f
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import EmployeeSearchSelectComponent from "../../components/employee-search-select/employee-search-select.component.jsx";
|
import EmployeeSearchSelectComponent from "../../components/employee-search-select/employee-search-select.component.jsx";
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component.jsx";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component.jsx";
|
||||||
|
import { BiSolidTrash } from "react-icons/bi";
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
@@ -42,7 +43,8 @@ export default function JobWatcherToggleComponent({
|
|||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
danger
|
danger
|
||||||
size="small"
|
size="medium"
|
||||||
|
icon={<BiSolidTrash />}
|
||||||
onClick={() => handleRemoveWatcher(watcher.user_email)}
|
onClick={() => handleRemoveWatcher(watcher.user_email)}
|
||||||
disabled={adding || removing} // Optional: Disable button during mutations
|
disabled={adding || removing} // Optional: Disable button during mutations
|
||||||
>
|
>
|
||||||
@@ -61,20 +63,28 @@ export default function JobWatcherToggleComponent({
|
|||||||
|
|
||||||
const popoverContent = (
|
const popoverContent = (
|
||||||
<div style={{ width: 600 }}>
|
<div style={{ width: 600 }}>
|
||||||
<Button
|
<List>
|
||||||
block
|
<List.Item
|
||||||
type={isWatching ? "primary" : "default"}
|
actions={[
|
||||||
icon={isWatching ? <EyeOutlined /> : <EyeFilled />}
|
<Button
|
||||||
onClick={handleToggleSelf}
|
type={isWatching ? "primary" : "default"}
|
||||||
loading={adding || removing}
|
danger={!isWatching}
|
||||||
>
|
icon={isWatching ? <EyeOutlined /> : <EyeFilled />}
|
||||||
{isWatching ? t("notifications.tooltips.unwatch") : t("notifications.tooltips.watch")}
|
size="medium"
|
||||||
</Button>
|
onClick={handleToggleSelf}
|
||||||
<Divider />
|
loading={adding || removing}
|
||||||
|
>
|
||||||
<Text type="secondary" style={{ marginBottom: 8, display: "block" }}>
|
{isWatching ? t("notifications.labels.unwatch") : t("notifications.labels.watch")}
|
||||||
{t("notifications.labels.watching-issue")}
|
</Button>
|
||||||
</Text>
|
]}
|
||||||
|
>
|
||||||
|
<List.Item.Meta>
|
||||||
|
<Text type="secondary" style={{ marginBottom: 8, display: "block" }}>
|
||||||
|
{t("notifications.labels.watching-issue")}
|
||||||
|
</Text>
|
||||||
|
</List.Item.Meta>
|
||||||
|
</List.Item>
|
||||||
|
</List>
|
||||||
{watcherLoading ? (
|
{watcherLoading ? (
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
) : jobWatchers && jobWatchers.length > 0 ? (
|
) : jobWatchers && jobWatchers.length > 0 ? (
|
||||||
@@ -82,12 +92,16 @@ export default function JobWatcherToggleComponent({
|
|||||||
) : (
|
) : (
|
||||||
<Text type="secondary">{t("notifications.labels.no-watchers")}</Text>
|
<Text type="secondary">{t("notifications.labels.no-watchers")}</Text>
|
||||||
)}
|
)}
|
||||||
<Divider />
|
|
||||||
|
|
||||||
|
<Divider />
|
||||||
<Text type="secondary">{t("notifications.labels.add-watchers")}</Text>
|
<Text type="secondary">{t("notifications.labels.add-watchers")}</Text>
|
||||||
<EmployeeSearchSelectComponent
|
<EmployeeSearchSelectComponent
|
||||||
style={{ minWidth: "100%" }}
|
style={{ minWidth: "100%" }}
|
||||||
options={bodyshop?.employees?.filter((e) => jobWatchers.every((w) => w.user_email !== e.user_email)) || []}
|
options={
|
||||||
|
bodyshop?.employees?.filter((e) =>
|
||||||
|
jobWatchers.every((w) => w.user_email !== e.user_email && e.active && e.user_email)
|
||||||
|
) || []
|
||||||
|
}
|
||||||
placeholder={t("notifications.labels.employee-search")}
|
placeholder={t("notifications.labels.employee-search")}
|
||||||
value={selectedWatcher}
|
value={selectedWatcher}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
@@ -110,10 +124,9 @@ export default function JobWatcherToggleComponent({
|
|||||||
const teamMembers = team.employee_team_members
|
const teamMembers = team.employee_team_members
|
||||||
.map((member) => {
|
.map((member) => {
|
||||||
const employee = bodyshop?.employees?.find((e) => e.id === member.employeeid);
|
const employee = bodyshop?.employees?.find((e) => e.id === member.employeeid);
|
||||||
return employee ? employee.user_email : null;
|
return employee?.user_email && employee?.active ? employee.user_email : null;
|
||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: JSON.stringify(teamMembers),
|
value: JSON.stringify(teamMembers),
|
||||||
label: team.name
|
label: team.name
|
||||||
@@ -128,7 +141,7 @@ export default function JobWatcherToggleComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover content={popoverContent} trigger="click" open={open} onOpenChange={setOpen}>
|
<Popover content={popoverContent} trigger="click" open={open} onOpenChange={setOpen}>
|
||||||
<Tooltip title={isWatching ? t("notifications.tooltips.unwatch") : t("notifications.tooltips.watch")}>
|
<Tooltip title={t("notifications.tooltips.job-watchers")}>
|
||||||
<Button
|
<Button
|
||||||
shape="circle"
|
shape="circle"
|
||||||
type={isWatching ? "primary" : "default"}
|
type={isWatching ? "primary" : "default"}
|
||||||
|
|||||||
@@ -211,6 +211,7 @@ function JobWatcherToggleContainer({ job, currentUser, bodyshop }) {
|
|||||||
handleRemoveWatcher={handleRemoveWatcher}
|
handleRemoveWatcher={handleRemoveWatcher}
|
||||||
handleWatcherSelect={handleWatcherSelect}
|
handleWatcherSelect={handleWatcherSelect}
|
||||||
handleTeamSelect={handleTeamSelect}
|
handleTeamSelect={handleTeamSelect}
|
||||||
|
currentUser={currentUser}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3785,17 +3785,18 @@
|
|||||||
"ro-number": "RO #{{ro_number}}",
|
"ro-number": "RO #{{ro_number}}",
|
||||||
"no-watchers": "No Watchers",
|
"no-watchers": "No Watchers",
|
||||||
"notification-settings-success": "Notification Settings saved successfully.",
|
"notification-settings-success": "Notification Settings saved successfully.",
|
||||||
"notification-settings-failure": "Error saving Notification Settings. {{error}}"
|
"notification-settings-failure": "Error saving Notification Settings. {{error}}",
|
||||||
|
"watch": "Watch",
|
||||||
|
"unwatch": "Unwatch"
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"remove": "remove"
|
"remove": "Remove"
|
||||||
},
|
},
|
||||||
"aria": {
|
"aria": {
|
||||||
"toggle": "Toggle Watching Job"
|
"toggle": "Toggle Watching Job"
|
||||||
},
|
},
|
||||||
"tooltips": {
|
"tooltips": {
|
||||||
"watch": "Watch Job",
|
"job-watchers": "Job Watchers"
|
||||||
"unwatch": "Unwatch Job"
|
|
||||||
},
|
},
|
||||||
"scenarios": {
|
"scenarios": {
|
||||||
"job-assigned-to-me": "Job Assigned to Me",
|
"job-assigned-to-me": "Job Assigned to Me",
|
||||||
|
|||||||
@@ -3785,7 +3785,9 @@
|
|||||||
"ro-number": "",
|
"ro-number": "",
|
||||||
"no-watchers": "",
|
"no-watchers": "",
|
||||||
"notification-settings-success": "",
|
"notification-settings-success": "",
|
||||||
"notification-settings-failure": ""
|
"notification-settings-failure": "",
|
||||||
|
"watch": "",
|
||||||
|
"unwatch": ""
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"remove": ""
|
"remove": ""
|
||||||
@@ -3794,8 +3796,7 @@
|
|||||||
"toggle": ""
|
"toggle": ""
|
||||||
},
|
},
|
||||||
"tooltips": {
|
"tooltips": {
|
||||||
"watch": "",
|
"job-watchers": ""
|
||||||
"unwatch": ""
|
|
||||||
},
|
},
|
||||||
"scenarios": {
|
"scenarios": {
|
||||||
"job-assigned-to-me": "",
|
"job-assigned-to-me": "",
|
||||||
|
|||||||
@@ -3785,7 +3785,9 @@
|
|||||||
"ro-number": "",
|
"ro-number": "",
|
||||||
"no-watchers": "",
|
"no-watchers": "",
|
||||||
"notification-settings-success": "",
|
"notification-settings-success": "",
|
||||||
"notification-settings-failure": ""
|
"notification-settings-failure": "",
|
||||||
|
"watch": "",
|
||||||
|
"unwatch": ""
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"remove": ""
|
"remove": ""
|
||||||
@@ -3794,8 +3796,7 @@
|
|||||||
"toggle": ""
|
"toggle": ""
|
||||||
},
|
},
|
||||||
"tooltips": {
|
"tooltips": {
|
||||||
"watch": "",
|
"job-watchers": ""
|
||||||
"unwatch": ""
|
|
||||||
},
|
},
|
||||||
"scenarios": {
|
"scenarios": {
|
||||||
"job-assigned-to-me": "",
|
"job-assigned-to-me": "",
|
||||||
|
|||||||
@@ -6290,10 +6290,12 @@
|
|||||||
columns:
|
columns:
|
||||||
- joblineid
|
- joblineid
|
||||||
- assigned_to
|
- assigned_to
|
||||||
|
- due_date
|
||||||
- partsorderid
|
- partsorderid
|
||||||
- completed
|
- completed
|
||||||
- description
|
- description
|
||||||
- billid
|
- billid
|
||||||
|
- title
|
||||||
- priority
|
- priority
|
||||||
retry_conf:
|
retry_conf:
|
||||||
interval_sec: 10
|
interval_sec: 10
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const moment = require("moment-timezone");
|
|||||||
const { taskEmailQueue } = require("./tasksEmailsQueue");
|
const { taskEmailQueue } = require("./tasksEmailsQueue");
|
||||||
const mailer = require("./mailer");
|
const mailer = require("./mailer");
|
||||||
const { InstanceEndpoints } = require("../utils/instanceMgr");
|
const { InstanceEndpoints } = require("../utils/instanceMgr");
|
||||||
|
const { formatTaskPriority } = require("../notifications/stringHelpers");
|
||||||
|
|
||||||
// Initialize the Tasks Email Queue
|
// Initialize the Tasks Email Queue
|
||||||
const tasksEmailQueue = taskEmailQueue();
|
const tasksEmailQueue = taskEmailQueue();
|
||||||
@@ -62,16 +63,6 @@ const formatDate = (date) => {
|
|||||||
return date ? `| Due on: ${moment(date).format("MM/DD/YYYY")}` : "";
|
return date ? `| Due on: ${moment(date).format("MM/DD/YYYY")}` : "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatPriority = (priority) => {
|
|
||||||
if (priority === 1) {
|
|
||||||
return "High";
|
|
||||||
} else if (priority === 3) {
|
|
||||||
return "Low";
|
|
||||||
} else {
|
|
||||||
return "Medium";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the email template arguments.
|
* Generate the email template arguments.
|
||||||
* @param title
|
* @param title
|
||||||
@@ -88,7 +79,7 @@ const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, j
|
|||||||
const endPoints = InstanceEndpoints();
|
const endPoints = InstanceEndpoints();
|
||||||
return {
|
return {
|
||||||
header: title,
|
header: title,
|
||||||
subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)} | Created By: ${createdBy || "N/A"}`,
|
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>`,
|
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
|
dateLine
|
||||||
};
|
};
|
||||||
@@ -155,7 +146,7 @@ const taskAssignedEmail = async (req, res) => {
|
|||||||
sendMail(
|
sendMail(
|
||||||
"assigned",
|
"assigned",
|
||||||
tasks_by_pk.assigned_to_employee.user_email,
|
tasks_by_pk.assigned_to_employee.user_email,
|
||||||
`A ${formatPriority(newTask.priority)} priority task has been ${dirty ? "reassigned to" : "created for"} you - ${newTask.title}`,
|
`A ${formatTaskPriority(newTask.priority)} priority task has been ${dirty ? "reassigned to" : "created for"} you - ${newTask.title}`,
|
||||||
generateEmailTemplate(
|
generateEmailTemplate(
|
||||||
generateTemplateArgs(
|
generateTemplateArgs(
|
||||||
newTask.title,
|
newTask.title,
|
||||||
@@ -239,7 +230,7 @@ const tasksRemindEmail = async (req, res) => {
|
|||||||
const onlyTask = groupedTasks[recipient.email][0];
|
const onlyTask = groupedTasks[recipient.email][0];
|
||||||
|
|
||||||
emailData.subject =
|
emailData.subject =
|
||||||
`New ${formatPriority(onlyTask.priority)} Priority Task Reminder - ${onlyTask.title} ${onlyTask.due_date ? `- ${formatDate(onlyTask.due_date)}` : ""}`.trim();
|
`New ${formatTaskPriority(onlyTask.priority)} Priority Task Reminder - ${onlyTask.title} ${onlyTask.due_date ? `- ${formatDate(onlyTask.due_date)}` : ""}`.trim();
|
||||||
|
|
||||||
emailData.html = generateEmailTemplate(
|
emailData.html = generateEmailTemplate(
|
||||||
generateTemplateArgs(
|
generateTemplateArgs(
|
||||||
@@ -266,7 +257,7 @@ const tasksRemindEmail = async (req, res) => {
|
|||||||
body: `<ul>
|
body: `<ul>
|
||||||
${allTasks
|
${allTasks
|
||||||
.map((task) =>
|
.map((task) =>
|
||||||
`<li><a href="${InstanceEndpoints()}/manage/tasks/alltasks?taskid=${task.id}">${task.title} - Priority: ${formatPriority(task.priority)} ${task.due_date ? `${formatDate(task.due_date)}` : ""} | Bodyshop: ${task.bodyshop.shopname}</a></li>`.trim()
|
`<li><a href="${InstanceEndpoints()}/manage/tasks/alltasks?taskid=${task.id}">${task.title} - Priority: ${formatTaskPriority(task.priority)} ${task.due_date ? `${formatDate(task.due_date)}` : ""} | Bodyshop: ${task.bodyshop.shopname}</a></li>`.trim()
|
||||||
)
|
)
|
||||||
.join("")}
|
.join("")}
|
||||||
</ul>`
|
</ul>`
|
||||||
|
|||||||
@@ -2726,6 +2726,7 @@ query GET_JOB_WATCHERS($jobid: uuid!) {
|
|||||||
bodyshop {
|
bodyshop {
|
||||||
id
|
id
|
||||||
shopname
|
shopname
|
||||||
|
timezone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const graphQLClient = require("../../graphql-client/graphql-client").client;
|
|||||||
const APP_CONSOLIDATION_DELAY_IN_MINS = (() => {
|
const APP_CONSOLIDATION_DELAY_IN_MINS = (() => {
|
||||||
const envValue = process.env?.APP_CONSOLIDATION_DELAY_IN_MINS;
|
const envValue = process.env?.APP_CONSOLIDATION_DELAY_IN_MINS;
|
||||||
const parsedValue = envValue ? parseInt(envValue, 10) : NaN;
|
const parsedValue = envValue ? parseInt(envValue, 10) : NaN;
|
||||||
return isNaN(parsedValue) ? 1 : Math.max(1, parsedValue); // Default to 1, ensure at least 1
|
return isNaN(parsedValue) ? 3 : Math.max(1, parsedValue); // Default to 3, ensure at least 1
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Base time-related constant (in milliseconds) / DO NOT TOUCH
|
// Base time-related constant (in milliseconds) / DO NOT TOUCH
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const { registerCleanupTask } = require("../../utils/cleanupManager");
|
|||||||
const EMAIL_CONSOLIDATION_DELAY_IN_MINS = (() => {
|
const EMAIL_CONSOLIDATION_DELAY_IN_MINS = (() => {
|
||||||
const envValue = process.env?.EMAIL_CONSOLIDATION_DELAY_IN_MINS;
|
const envValue = process.env?.EMAIL_CONSOLIDATION_DELAY_IN_MINS;
|
||||||
const parsedValue = envValue ? parseInt(envValue, 10) : NaN;
|
const parsedValue = envValue ? parseInt(envValue, 10) : NaN;
|
||||||
return isNaN(parsedValue) ? 1 : Math.max(1, parsedValue); // Default to 1, ensure at least 1
|
return isNaN(parsedValue) ? 3 : Math.max(1, parsedValue); // Default to 3, ensure at least 1
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Base time-related constant (in milliseconds) / DO NOT TOUCH
|
// Base time-related constant (in milliseconds) / DO NOT TOUCH
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
const { getJobAssignmentType } = require("./stringHelpers");
|
const { getJobAssignmentType, formatTaskPriority } = require("./stringHelpers");
|
||||||
|
const moment = require("moment-timezone");
|
||||||
|
const { startCase } = require("lodash");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,17 +28,17 @@ const populateWatchers = (data, result) => {
|
|||||||
*/
|
*/
|
||||||
// Verified
|
// Verified
|
||||||
const alternateTransportChangedBuilder = (data) => {
|
const alternateTransportChangedBuilder = (data) => {
|
||||||
const body = `The Alternate Transport status has been updated to ${data?.data?.alt_transport}.`;
|
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 = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
bodyShopId: data.bodyShopId,
|
bodyShopId: data.bodyShopId,
|
||||||
jobRoNumber: data.jobRoNumber,
|
jobRoNumber: data.jobRoNumber,
|
||||||
key: "notifications.job.alternateTransportChanged",
|
key: "notifications.job.alternateTransportChanged",
|
||||||
body, // Same as email body
|
body,
|
||||||
variables: {
|
variables: {
|
||||||
alternateTransport: data.changedFields.alt_transport?.new,
|
alternateTransport: data?.changedFields?.alt_transport?.new,
|
||||||
oldAlternateTransport: data.changedFields.alt_transport?.old
|
oldAlternateTransport: data?.changedFields?.alt_transport?.old
|
||||||
},
|
},
|
||||||
recipients: []
|
recipients: []
|
||||||
},
|
},
|
||||||
@@ -60,7 +62,6 @@ const alternateTransportChangedBuilder = (data) => {
|
|||||||
//verified
|
//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();
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
@@ -71,8 +72,8 @@ const billPostedHandler = (data) => {
|
|||||||
key: "notifications.job.billPosted",
|
key: "notifications.job.billPosted",
|
||||||
body,
|
body,
|
||||||
variables: {
|
variables: {
|
||||||
facing,
|
isInHouse: data?.data?.isinhouse,
|
||||||
is_credit_memo: data?.data?.is_credit_memo
|
isCreditMemo: data?.data?.is_credit_memo
|
||||||
},
|
},
|
||||||
recipients: []
|
recipients: []
|
||||||
},
|
},
|
||||||
@@ -95,7 +96,7 @@ const billPostedHandler = (data) => {
|
|||||||
*/
|
*/
|
||||||
// TODO: Needs change
|
// 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 = `The critical parts status has changed to ${data?.data?.queued_for_parts ? "queued" : "not queued"}.`;
|
||||||
const result = {
|
const result = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
@@ -104,8 +105,8 @@ const criticalPartsStatusChangedBuilder = (data) => {
|
|||||||
key: "notifications.job.criticalPartsStatusChanged",
|
key: "notifications.job.criticalPartsStatusChanged",
|
||||||
body,
|
body,
|
||||||
variables: {
|
variables: {
|
||||||
queuedForParts: data.data.queued_for_parts,
|
queuedForParts: data?.data?.queued_for_parts,
|
||||||
oldQueuedForParts: data.changedFields.queued_for_parts?.old
|
oldQueuedForParts: data?.changedFields?.queued_for_parts?.old
|
||||||
},
|
},
|
||||||
recipients: []
|
recipients: []
|
||||||
},
|
},
|
||||||
@@ -162,7 +163,7 @@ const intakeDeliveryChecklistCompletedBuilder = (data) => {
|
|||||||
*/
|
*/
|
||||||
// Verified
|
// 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 = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
@@ -224,7 +225,7 @@ const jobsAddedToProductionBuilder = (data) => {
|
|||||||
*/
|
*/
|
||||||
// Verified
|
// Verified
|
||||||
const jobStatusChangeBuilder = (data) => {
|
const jobStatusChangeBuilder = (data) => {
|
||||||
const body = `The status has changed from ${data.changedFields.status.old} to ${data.changedFields.status.new}`;
|
const body = `The status has changed from ${data?.changedFields?.status?.old || "unset"} to ${data?.changedFields?.status?.new || "unset"}`;
|
||||||
const result = {
|
const result = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
@@ -294,8 +295,19 @@ 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 = `An Note has been added: "${data.data.text}"`;
|
const body = [
|
||||||
|
"A",
|
||||||
|
data?.data?.critical && "critical",
|
||||||
|
data?.data?.private && "private",
|
||||||
|
data?.data?.type,
|
||||||
|
"Note has been added by",
|
||||||
|
`${data.data.created_by}`
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
@@ -304,7 +316,10 @@ const newNoteAddedBuilder = (data) => {
|
|||||||
key: "notifications.job.newNoteAdded",
|
key: "notifications.job.newNoteAdded",
|
||||||
body,
|
body,
|
||||||
variables: {
|
variables: {
|
||||||
text: data.data.text
|
createdBy: data?.data?.created_by,
|
||||||
|
critical: data?.data?.critical,
|
||||||
|
type: data?.data?.type,
|
||||||
|
private: data?.data?.private
|
||||||
},
|
},
|
||||||
recipients: []
|
recipients: []
|
||||||
},
|
},
|
||||||
@@ -325,9 +340,11 @@ 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 = `An ${type} time ticket has been posted${data?.data?.flat_rate ? " (Flat Rate)" : ""}.`.trim();
|
const body =
|
||||||
|
`A ${startCase(type.toLowerCase())} Time Ticket for ${data?.data?.date} has been posted${data?.data?.flat_rate ? " (Flat Rate)" : ""}.`.trim();
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
app: {
|
app: {
|
||||||
@@ -337,7 +354,9 @@ const newTimeTicketPostedBuilder = (data) => {
|
|||||||
key: "notifications.job.newTimeTicketPosted",
|
key: "notifications.job.newTimeTicketPosted",
|
||||||
body,
|
body,
|
||||||
variables: {
|
variables: {
|
||||||
type
|
type,
|
||||||
|
date: data?.data?.date,
|
||||||
|
flatRate: data?.data?.flat_rate
|
||||||
},
|
},
|
||||||
recipients: []
|
recipients: []
|
||||||
},
|
},
|
||||||
@@ -419,7 +438,27 @@ const paymentCollectedCompletedBuilder = (data) => {
|
|||||||
* Builds notification data for changes to scheduled dates.
|
* Builds notification data for changes to scheduled dates.
|
||||||
*/
|
*/
|
||||||
const scheduledDatesChangedBuilder = (data) => {
|
const scheduledDatesChangedBuilder = (data) => {
|
||||||
const body = `Scheduled dates have been updated.`;
|
const momentFormat = "MM/DD/YYYY hh:mm a";
|
||||||
|
const changedFields = data.changedFields;
|
||||||
|
|
||||||
|
// Define field configurations
|
||||||
|
const fieldConfigs = {
|
||||||
|
scheduled_in: "Scheduled In",
|
||||||
|
scheduled_completion: "Scheduled Completion",
|
||||||
|
scheduled_delivery: "Scheduled Delivery"
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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.";
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
@@ -428,12 +467,12 @@ const scheduledDatesChangedBuilder = (data) => {
|
|||||||
key: "notifications.job.scheduledDatesChanged",
|
key: "notifications.job.scheduledDatesChanged",
|
||||||
body,
|
body,
|
||||||
variables: {
|
variables: {
|
||||||
scheduledIn: data.changedFields.scheduled_in?.new,
|
scheduledIn: changedFields.scheduled_in?.new,
|
||||||
oldScheduledIn: data.changedFields.scheduled_in?.old,
|
oldScheduledIn: changedFields.scheduled_in?.old,
|
||||||
scheduledCompletion: data.changedFields.scheduled_completion?.new,
|
scheduledCompletion: changedFields.scheduled_completion?.new,
|
||||||
oldScheduledCompletion: data.changedFields.scheduled_completion?.old,
|
oldScheduledCompletion: changedFields.scheduled_completion?.old,
|
||||||
scheduledDelivery: data.changedFields.scheduled_delivery?.new,
|
scheduledDelivery: changedFields.scheduled_delivery?.new,
|
||||||
oldScheduledDelivery: data.changedFields.scheduled_delivery?.old
|
oldScheduledDelivery: changedFields.scheduled_delivery?.old
|
||||||
},
|
},
|
||||||
recipients: []
|
recipients: []
|
||||||
},
|
},
|
||||||
@@ -486,7 +525,75 @@ const supplementImportedBuilder = (data) => {
|
|||||||
* Builds notification data for tasks updated or created.
|
* Builds notification data for tasks updated or created.
|
||||||
*/
|
*/
|
||||||
const tasksUpdatedCreatedBuilder = (data) => {
|
const tasksUpdatedCreatedBuilder = (data) => {
|
||||||
const body = `Tasks have been ${data.isNew ? "created" : "updated"}.`;
|
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 result = {
|
const result = {
|
||||||
app: {
|
app: {
|
||||||
jobId: data.jobId,
|
jobId: data.jobId,
|
||||||
@@ -494,10 +601,7 @@ const tasksUpdatedCreatedBuilder = (data) => {
|
|||||||
bodyShopId: data.bodyShopId,
|
bodyShopId: data.bodyShopId,
|
||||||
key: data.isNew ? "notifications.job.taskCreated" : "notifications.job.taskUpdated",
|
key: data.isNew ? "notifications.job.taskCreated" : "notifications.job.taskUpdated",
|
||||||
body,
|
body,
|
||||||
variables: {
|
variables,
|
||||||
isNew: data.isNew,
|
|
||||||
roNumber: data.jobRoNumber
|
|
||||||
},
|
|
||||||
recipients: []
|
recipients: []
|
||||||
},
|
},
|
||||||
email: {
|
email: {
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ const scenarioParser = async (req, jobIdField) => {
|
|||||||
|
|
||||||
const bodyShopId = watcherData?.job?.bodyshop?.id;
|
const bodyShopId = watcherData?.job?.bodyshop?.id;
|
||||||
const bodyShopName = watcherData?.job?.bodyshop?.shopname;
|
const bodyShopName = watcherData?.job?.bodyshop?.shopname;
|
||||||
|
const bodyShopTimezone = watcherData?.job?.bodyshop?.timezone;
|
||||||
|
|
||||||
const jobRoNumber = watcherData?.job?.ro_number;
|
const jobRoNumber = watcherData?.job?.ro_number;
|
||||||
const jobClaimNumber = watcherData?.job?.clm_no;
|
const jobClaimNumber = watcherData?.job?.clm_no;
|
||||||
|
|
||||||
@@ -147,6 +149,7 @@ const scenarioParser = async (req, jobIdField) => {
|
|||||||
jobWatchers,
|
jobWatchers,
|
||||||
bodyShopId,
|
bodyShopId,
|
||||||
bodyShopName,
|
bodyShopName,
|
||||||
|
bodyShopTimezone,
|
||||||
matchingScenarios
|
matchingScenarios
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -247,6 +250,7 @@ const scenarioParser = async (req, jobIdField) => {
|
|||||||
trigger: finalScenarioData.trigger.name,
|
trigger: finalScenarioData.trigger.name,
|
||||||
bodyShopId: finalScenarioData.bodyShopId,
|
bodyShopId: finalScenarioData.bodyShopId,
|
||||||
bodyShopName: finalScenarioData.bodyShopName,
|
bodyShopName: finalScenarioData.bodyShopName,
|
||||||
|
bodyShopTimezone: finalScenarioData.bodyShopTimezone,
|
||||||
scenarioKey: scenario.key,
|
scenarioKey: scenario.key,
|
||||||
scenarioTable: scenario.table,
|
scenarioTable: scenario.table,
|
||||||
scenarioFields: filteredScenarioFields,
|
scenarioFields: filteredScenarioFields,
|
||||||
|
|||||||
@@ -26,6 +26,17 @@ const getJobAssignmentType = (data) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
const formatTaskPriority = (priority) => {
|
||||||
getJobAssignmentType
|
if (priority === 1) {
|
||||||
|
return "High";
|
||||||
|
} else if (priority === 3) {
|
||||||
|
return "Low";
|
||||||
|
} else {
|
||||||
|
return "Medium";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getJobAssignmentType,
|
||||||
|
formatTaskPriority
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user