From 69da6bccf7c70e8caeb70f798039e71f1691c5b8 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Thu, 13 Mar 2025 17:37:36 -0400 Subject: [PATCH 1/3] IO-3096-GlobalNotifications - Adjust splits --- client/src/App/App.jsx | 16 ++-------------- client/src/contexts/SocketIO/useSocket.jsx | 22 +++++++++++++++------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/client/src/App/App.jsx b/client/src/App/App.jsx index af3c906a8..1cf4a8a06 100644 --- a/client/src/App/App.jsx +++ b/client/src/App/App.jsx @@ -48,8 +48,6 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline const { t } = useTranslation(); const navigate = useNavigate(); - const scenarioNotificationsOn = client?.getTreatment("Realtime_Notifications_UI") === "on"; - useEffect(() => { if (!navigator.onLine) { setOnline(false); @@ -203,12 +201,7 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline path="/manage/*" element={ - + @@ -220,12 +213,7 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline path="/tech/*" element={ - + diff --git a/client/src/contexts/SocketIO/useSocket.jsx b/client/src/contexts/SocketIO/useSocket.jsx index ee51def17..70f52311f 100644 --- a/client/src/contexts/SocketIO/useSocket.jsx +++ b/client/src/contexts/SocketIO/useSocket.jsx @@ -14,6 +14,7 @@ import { } from "../../graphql/notifications.queries.js"; import { useMutation } from "@apollo/client"; import { useTranslation } from "react-i18next"; +import { useSplitTreatments } from "@splitsoftware/splitio-react"; const SocketContext = createContext(null); @@ -25,11 +26,10 @@ const INITIAL_NOTIFICATIONS = 10; * @param bodyshop * @param navigate * @param currentUser - * @param scenarioNotificationsOn * @returns {JSX.Element} * @constructor */ -const SocketProvider = ({ children, bodyshop, navigate, currentUser, scenarioNotificationsOn }) => { +const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => { const socketRef = useRef(null); const [clientId, setClientId] = useState(null); const [isConnected, setIsConnected] = useState(false); @@ -37,6 +37,14 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser, scenarioNot const userAssociationId = bodyshop?.associations?.[0]?.id; const { t } = useTranslation(); + const { + treatments: { Realtime_Notifications_UI } + } = useSplitTreatments({ + attributes: {}, + names: ["Realtime_Notifications_UI"], + splitKey: bodyshop?.imexshopid + }); + const [markNotificationRead] = useMutation(MARK_NOTIFICATION_READ, { update: (cache, { data: { update_notifications } }) => { const timestamp = new Date().toISOString(); @@ -209,7 +217,7 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser, scenarioNot const handleNotification = (data) => { // Scenario Notifications have been disabled, bail. - if (!scenarioNotificationsOn) { + if (Realtime_Notifications_UI?.treatment !== "on") { return; } @@ -329,7 +337,7 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser, scenarioNot const handleSyncNotificationRead = ({ notificationId, timestamp }) => { // Scenario Notifications have been disabled, bail. - if (!scenarioNotificationsOn) { + if (Realtime_Notifications_UI?.treatment !== "on") { return; } @@ -371,7 +379,7 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser, scenarioNot const handleSyncAllNotificationsRead = ({ timestamp }) => { // Scenario Notifications have been disabled, bail. - if (!scenarioNotificationsOn) { + if (Realtime_Notifications_UI?.treatment !== "on") { return; } @@ -462,7 +470,7 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser, scenarioNot markAllNotificationsRead, navigate, currentUser, - scenarioNotificationsOn, + Realtime_Notifications_UI, t ]); @@ -474,7 +482,7 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser, scenarioNot isConnected, markNotificationRead, markAllNotificationsRead, - scenarioNotificationsOn + scenarioNotificationsOn: Realtime_Notifications_UI?.treatment === "on" }} > {children} From b831d8ca8a0fd2eb241facdca4efca22a15040b0 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Thu, 13 Mar 2025 15:27:20 -0700 Subject: [PATCH 2/3] IO-3096 Add indexes for notifications. --- .../down.sql | 1 + .../1741904378259_create_index_notificiations_idx_jobs/up.sql | 2 ++ .../down.sql | 1 + .../up.sql | 2 ++ hasura/migrations/1741904614090_run_sql_migration/down.sql | 3 +++ hasura/migrations/1741904614090_run_sql_migration/up.sql | 1 + hasura/migrations/1741904805838_run_sql_migration/down.sql | 3 +++ hasura/migrations/1741904805838_run_sql_migration/up.sql | 1 + 8 files changed, 14 insertions(+) create mode 100644 hasura/migrations/1741904378259_create_index_notificiations_idx_jobs/down.sql create mode 100644 hasura/migrations/1741904378259_create_index_notificiations_idx_jobs/up.sql create mode 100644 hasura/migrations/1741904395934_create_index_notifications_idx_associations/down.sql create mode 100644 hasura/migrations/1741904395934_create_index_notifications_idx_associations/up.sql create mode 100644 hasura/migrations/1741904614090_run_sql_migration/down.sql create mode 100644 hasura/migrations/1741904614090_run_sql_migration/up.sql create mode 100644 hasura/migrations/1741904805838_run_sql_migration/down.sql create mode 100644 hasura/migrations/1741904805838_run_sql_migration/up.sql diff --git a/hasura/migrations/1741904378259_create_index_notificiations_idx_jobs/down.sql b/hasura/migrations/1741904378259_create_index_notificiations_idx_jobs/down.sql new file mode 100644 index 000000000..a4029e3f4 --- /dev/null +++ b/hasura/migrations/1741904378259_create_index_notificiations_idx_jobs/down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "public"."notificiations_idx_jobs"; diff --git a/hasura/migrations/1741904378259_create_index_notificiations_idx_jobs/up.sql b/hasura/migrations/1741904378259_create_index_notificiations_idx_jobs/up.sql new file mode 100644 index 000000000..8fd3f3f34 --- /dev/null +++ b/hasura/migrations/1741904378259_create_index_notificiations_idx_jobs/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX "notificiations_idx_jobs" on + "public"."notifications" using btree ("jobid"); diff --git a/hasura/migrations/1741904395934_create_index_notifications_idx_associations/down.sql b/hasura/migrations/1741904395934_create_index_notifications_idx_associations/down.sql new file mode 100644 index 000000000..17b162d35 --- /dev/null +++ b/hasura/migrations/1741904395934_create_index_notifications_idx_associations/down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "public"."notifications_idx_associations"; diff --git a/hasura/migrations/1741904395934_create_index_notifications_idx_associations/up.sql b/hasura/migrations/1741904395934_create_index_notifications_idx_associations/up.sql new file mode 100644 index 000000000..9d131b3b2 --- /dev/null +++ b/hasura/migrations/1741904395934_create_index_notifications_idx_associations/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX "notifications_idx_associations" on + "public"."notifications" using btree ("associationid"); diff --git a/hasura/migrations/1741904614090_run_sql_migration/down.sql b/hasura/migrations/1741904614090_run_sql_migration/down.sql new file mode 100644 index 000000000..90dce4b23 --- /dev/null +++ b/hasura/migrations/1741904614090_run_sql_migration/down.sql @@ -0,0 +1,3 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- CREATE INDEX idx_notifications_created_at_not_read ON notifications(created_at desc, read) where read is null; diff --git a/hasura/migrations/1741904614090_run_sql_migration/up.sql b/hasura/migrations/1741904614090_run_sql_migration/up.sql new file mode 100644 index 000000000..ecca4610e --- /dev/null +++ b/hasura/migrations/1741904614090_run_sql_migration/up.sql @@ -0,0 +1 @@ +CREATE INDEX idx_notifications_created_at_not_read ON notifications(created_at desc, read) where read is null; diff --git a/hasura/migrations/1741904805838_run_sql_migration/down.sql b/hasura/migrations/1741904805838_run_sql_migration/down.sql new file mode 100644 index 000000000..f6b9c6e6e --- /dev/null +++ b/hasura/migrations/1741904805838_run_sql_migration/down.sql @@ -0,0 +1,3 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- CREATE INDEX idx_notifications_associations_not_read ON notifications(associationid, read) where read is null; diff --git a/hasura/migrations/1741904805838_run_sql_migration/up.sql b/hasura/migrations/1741904805838_run_sql_migration/up.sql new file mode 100644 index 000000000..7950b3bd9 --- /dev/null +++ b/hasura/migrations/1741904805838_run_sql_migration/up.sql @@ -0,0 +1 @@ +CREATE INDEX idx_notifications_associations_not_read ON notifications(associationid, read) where read is null; From 2c0eab9366f5463354cb1a424edc277ed9187d13 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Fri, 14 Mar 2025 11:27:28 -0400 Subject: [PATCH 3/3] IO-3096-GlobalNotifications - Correct time zone from footer in notification email --- server/email/generateTemplate.js | 5 +++++ server/notifications/queues/emailQueue.js | 14 +++++++++----- server/notifications/scenarioBuilders.js | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/server/email/generateTemplate.js b/server/email/generateTemplate.js index 9d2815851..2887b2fd2 100644 --- a/server/email/generateTemplate.js +++ b/server/email/generateTemplate.js @@ -20,6 +20,11 @@ const defaultFooter = () => { const now = () => moment().format("MM/DD/YYYY @ hh:mm a"); +/** + * Generate the email template + * @param strings + * @returns {string} + */ const generateEmailTemplate = (strings) => { return ( ` diff --git a/server/notifications/queues/emailQueue.js b/server/notifications/queues/emailQueue.js index 823dff5ea..ff6702635 100644 --- a/server/notifications/queues/emailQueue.js +++ b/server/notifications/queues/emailQueue.js @@ -5,6 +5,7 @@ const { InstanceEndpoints } = require("../../utils/instanceMgr"); const { registerCleanupTask } = require("../../utils/cleanupManager"); const getBullMQPrefix = require("../../utils/getBullMQPrefix"); const devDebugLogger = require("../../utils/devDebugLogger"); +const moment = require("moment-timezone"); const EMAIL_CONSOLIDATION_DELAY_IN_MINS = (() => { const envValue = process.env?.EMAIL_CONSOLIDATION_DELAY_IN_MINS; @@ -59,7 +60,7 @@ const loadEmailQueue = async ({ pubClient, logger }) => { emailAddWorker = new Worker( "emailAdd", async (job) => { - const { jobId, jobRoNumber, bodyShopName, body, recipients } = job.data; + const { jobId, jobRoNumber, bodyShopName, bodyShopTimezone, body, recipients } = job.data; devDebugLogger(`Adding email notifications for jobId ${jobId}`); const redisKeyPrefix = `email:${devKey}:notifications:${jobId}`; @@ -72,6 +73,7 @@ const loadEmailQueue = async ({ pubClient, logger }) => { const detailsKey = `email:${devKey}:recipientDetails:${jobId}:${user}`; await pubClient.hsetnx(detailsKey, "firstName", firstName || ""); await pubClient.hsetnx(detailsKey, "lastName", lastName || ""); + await pubClient.hsetnx(detailsKey, "bodyShopTimezone", bodyShopTimezone); await pubClient.expire(detailsKey, NOTIFICATION_EXPIRATION / 1000); await pubClient.sadd(`email:${devKey}:recipients:${jobId}`, user); devDebugLogger(`Stored message for ${user} under ${userKey}: ${body}`); @@ -82,7 +84,7 @@ const loadEmailQueue = async ({ pubClient, logger }) => { if (flagSet) { await emailConsolidateQueue.add( "consolidate-emails", - { jobId, jobRoNumber, bodyShopName }, + { jobId, jobRoNumber, bodyShopName, bodyShopTimezone }, { jobId: `consolidate:${jobId}`, delay: EMAIL_CONSOLIDATION_DELAY, @@ -125,9 +127,11 @@ const loadEmailQueue = async ({ pubClient, logger }) => { const firstName = details.firstName || "User"; const multipleUpdateString = messages.length > 1 ? "Updates" : "Update"; const subject = `${multipleUpdateString} for job ${jobRoNumber || "N/A"} at ${bodyShopName}`; + const timezone = moment.tz.zone(details?.bodyShopTimezone) ? details.bodyShopTimezone : "UTC"; const emailBody = generateEmailTemplate({ header: `${multipleUpdateString} for Job ${jobRoNumber || "N/A"}`, subHeader: `Dear ${firstName},`, + dateLine: moment().tz(timezone).format("MM/DD/YYYY hh:mm a"), body: `

There have been updates to job ${jobRoNumber || "N/A"} at ${bodyShopName}:


    @@ -226,10 +230,10 @@ const dispatchEmailsToQueue = async ({ emailsToDispatch, logger }) => { const emailAddQueue = getQueue(); for (const email of emailsToDispatch) { - const { jobId, jobRoNumber, bodyShopName, body, recipients } = email; + const { jobId, jobRoNumber, bodyShopName, bodyShopTimezone, body, recipients } = email; if (!jobId || !jobRoNumber || !bodyShopName || !body || !recipients.length) { - logger.logger.warn( + devDebugLogger( `Skipping email dispatch for jobId ${jobId} due to missing data: ` + `jobRoNumber=${jobRoNumber || "N/A"}, bodyShopName=${bodyShopName}, body=${body}, recipients=${recipients.length}` ); @@ -238,7 +242,7 @@ const dispatchEmailsToQueue = async ({ emailsToDispatch, logger }) => { await emailAddQueue.add( "add-email-notification", - { jobId, jobRoNumber, bodyShopName, body, recipients }, + { jobId, jobRoNumber, bodyShopName, bodyShopTimezone, body, recipients }, { jobId: `${jobId}:${Date.now()}` } ); devDebugLogger(`Added email notification to queue for jobId ${jobId} with ${recipients.length} recipients`); diff --git a/server/notifications/scenarioBuilders.js b/server/notifications/scenarioBuilders.js index 19b1d9f72..b3f4d0fd2 100644 --- a/server/notifications/scenarioBuilders.js +++ b/server/notifications/scenarioBuilders.js @@ -28,6 +28,7 @@ const buildNotification = (data, key, body, variables = {}) => { jobId: data.jobId, jobRoNumber: data.jobRoNumber, bodyShopName: data.bodyShopName, + bodyShopTimezone: data.bodyShopTimezone, body, recipients: [] },