feature/IO-3096-GlobalNotifications - Checkpoint - Fix user getting all bodyshop notifications (now by associationId), fix regression in 'Assigned To' scenario.
This commit is contained in:
@@ -49,7 +49,7 @@ import { useState, useEffect } from "react";
|
|||||||
import { debounce } from "lodash";
|
import { debounce } from "lodash";
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { GET_UNREAD_COUNT } from "../../graphql/notifications.queries.js";
|
import { GET_UNREAD_COUNT } from "../../graphql/notifications.queries.js";
|
||||||
import { NotificationCenterContainer } from "../notification-center/notification-center.container.jsx";
|
import NotificationCenterContainer from "../notification-center/notification-center.container.jsx";
|
||||||
import { useSocket } from "../../contexts/SocketIO/socketContext.jsx";
|
import { useSocket } from "../../contexts/SocketIO/socketContext.jsx";
|
||||||
|
|
||||||
// Used to Determine if the Header is in Mobile Mode, and to toggle the multiple menus
|
// Used to Determine if the Header is in Mobile Mode, and to toggle the multiple menus
|
||||||
@@ -129,27 +129,33 @@ function Header({
|
|||||||
const { isConnected } = useSocket(bodyshop);
|
const { isConnected } = useSocket(bodyshop);
|
||||||
const [notificationVisible, setNotificationVisible] = useState(false);
|
const [notificationVisible, setNotificationVisible] = useState(false);
|
||||||
|
|
||||||
|
const userAssociationId = bodyshop?.associations?.[0]?.id;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: unreadData,
|
data: unreadData,
|
||||||
refetch: refetchUnread,
|
refetch: refetchUnread,
|
||||||
loading: unreadLoading
|
loading: unreadLoading
|
||||||
} = useQuery(GET_UNREAD_COUNT, {
|
} = useQuery(GET_UNREAD_COUNT, {
|
||||||
|
variables: { associationid: userAssociationId },
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
pollInterval: isConnected ? 0 : 30000 // Poll only if socket is down
|
pollInterval: isConnected ? 0 : 30000, // Poll only if socket is down
|
||||||
|
skip: !userAssociationId // Skip query if no userAssociationId
|
||||||
});
|
});
|
||||||
|
|
||||||
const unreadCount = unreadData?.notifications_aggregate?.aggregate?.count ?? 0;
|
const unreadCount = unreadData?.notifications_aggregate?.aggregate?.count ?? 0;
|
||||||
|
|
||||||
// Initial fetch and socket status handling
|
// Initial fetch and socket status handling
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refetchUnread();
|
if (userAssociationId) {
|
||||||
}, [refetchUnread]);
|
refetchUnread().catch((e) => console.error(`Something went wrong fetching unread notifications: ${e?.message}`));
|
||||||
|
}
|
||||||
|
}, [refetchUnread, userAssociationId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isConnected && !unreadLoading) {
|
if (!isConnected && !unreadLoading && userAssociationId) {
|
||||||
refetchUnread();
|
refetchUnread().catch((e) => console.error(`Something went wrong fetching unread notifications: ${e?.message}`));
|
||||||
}
|
}
|
||||||
}, [isConnected, unreadLoading, refetchUnread]);
|
}, [isConnected, unreadLoading, refetchUnread, userAssociationId]);
|
||||||
|
|
||||||
const handleNotificationClick = (e) => {
|
const handleNotificationClick = (e) => {
|
||||||
setNotificationVisible(!notificationVisible);
|
setNotificationVisible(!notificationVisible);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
// notification-center.container.jsx
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useState, useEffect, useCallback } from "react";
|
import { useMutation, useQuery } from "@apollo/client";
|
||||||
import { useQuery, useMutation } from "@apollo/client";
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import NotificationCenterComponent from "./notification-center.component";
|
import NotificationCenterComponent from "./notification-center.component";
|
||||||
import { GET_NOTIFICATIONS, MARK_ALL_NOTIFICATIONS_READ } from "../../graphql/notifications.queries";
|
import { GET_NOTIFICATIONS, MARK_ALL_NOTIFICATIONS_READ } from "../../graphql/notifications.queries";
|
||||||
import { useSocket } from "../../contexts/SocketIO/socketContext.jsx";
|
import { useSocket } from "../../contexts/SocketIO/socketContext.jsx";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors.js";
|
||||||
|
|
||||||
export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
||||||
const [showUnreadOnly, setShowUnreadOnly] = useState(false);
|
const [showUnreadOnly, setShowUnreadOnly] = useState(false);
|
||||||
@@ -12,6 +13,16 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const { isConnected } = useSocket();
|
const { isConnected } = useSocket();
|
||||||
|
|
||||||
|
const userAssociationId = bodyshop?.associations?.[0]?.id;
|
||||||
|
|
||||||
|
const baseWhereClause = useMemo(() => {
|
||||||
|
return { associationid: { _eq: userAssociationId } };
|
||||||
|
}, [userAssociationId]);
|
||||||
|
|
||||||
|
const whereClause = useMemo(() => {
|
||||||
|
return showUnreadOnly ? { ...baseWhereClause, read: { _is_null: true } } : baseWhereClause;
|
||||||
|
}, [baseWhereClause, showUnreadOnly]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
fetchMore,
|
fetchMore,
|
||||||
@@ -22,12 +33,12 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
variables: {
|
variables: {
|
||||||
limit: 20,
|
limit: 20,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
where: showUnreadOnly ? { read: { _is_null: true } } : {}
|
where: whereClause
|
||||||
},
|
},
|
||||||
fetchPolicy: "cache-and-network",
|
fetchPolicy: "cache-and-network",
|
||||||
notifyOnNetworkStatusChange: true,
|
notifyOnNetworkStatusChange: true,
|
||||||
pollInterval: isConnected ? 0 : 30000,
|
pollInterval: isConnected ? 0 : 30000,
|
||||||
skip: false,
|
skip: !userAssociationId, // Skip query if no userAssociationId
|
||||||
onError: (err) => {
|
onError: (err) => {
|
||||||
setError(err.message);
|
setError(err.message);
|
||||||
console.error("GET_NOTIFICATIONS error:", err);
|
console.error("GET_NOTIFICATIONS error:", err);
|
||||||
@@ -36,13 +47,14 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [markAllReadMutation, { error: mutationError }] = useMutation(MARK_ALL_NOTIFICATIONS_READ, {
|
const [markAllReadMutation, { error: mutationError }] = useMutation(MARK_ALL_NOTIFICATIONS_READ, {
|
||||||
|
variables: { associationid: userAssociationId },
|
||||||
update: (cache, { data: mutationData }) => {
|
update: (cache, { data: mutationData }) => {
|
||||||
const timestamp = new Date().toISOString();
|
const timestamp = new Date().toISOString();
|
||||||
cache.modify({
|
cache.modify({
|
||||||
fields: {
|
fields: {
|
||||||
notifications(existing = [], { readField }) {
|
notifications(existing = [], { readField }) {
|
||||||
return existing.map((notif) => {
|
return existing.map((notif) => {
|
||||||
if (readField("read", notif) === null) {
|
if (readField("read", notif) === null && readField("associationid", notif) === userAssociationId) {
|
||||||
return { ...notif, read: timestamp };
|
return { ...notif, read: timestamp };
|
||||||
}
|
}
|
||||||
return notif;
|
return notif;
|
||||||
@@ -60,7 +72,7 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
variables: {
|
variables: {
|
||||||
limit: 20,
|
limit: 20,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
where: showUnreadOnly ? { read: { _is_null: true } } : {}
|
where: whereClause
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -70,7 +82,7 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
variables: {
|
variables: {
|
||||||
limit: 20,
|
limit: 20,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
where: showUnreadOnly ? { read: { _is_null: true } } : {}
|
where: whereClause
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
notifications: cachedNotifications.notifications.map((notif) =>
|
notifications: cachedNotifications.notifications.map((notif) =>
|
||||||
@@ -102,21 +114,19 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
scenarioMeta = {};
|
scenarioMeta = {};
|
||||||
}
|
}
|
||||||
if (!Array.isArray(scenarioText)) scenarioText = [scenarioText];
|
if (!Array.isArray(scenarioText)) scenarioText = [scenarioText];
|
||||||
// Derive RO number from scenario_meta or assume it's available in notif
|
const roNumber = notif.job.ro_number;
|
||||||
const roNumber = notif.job.ro_number || "RO Not Found"; // Adjust based on your data structure
|
|
||||||
if (!Array.isArray(scenarioMeta)) scenarioMeta = [scenarioMeta];
|
if (!Array.isArray(scenarioMeta)) scenarioMeta = [scenarioMeta];
|
||||||
const processed = {
|
return {
|
||||||
id: notif.id,
|
id: notif.id,
|
||||||
jobid: notif.jobid,
|
jobid: notif.jobid,
|
||||||
associationid: notif.associationid,
|
associationid: notif.associationid,
|
||||||
scenarioText,
|
scenarioText,
|
||||||
scenarioMeta,
|
scenarioMeta,
|
||||||
roNumber, // Add RO number to notification object
|
roNumber,
|
||||||
created_at: notif.created_at,
|
created_at: notif.created_at,
|
||||||
read: notif.read,
|
read: notif.read,
|
||||||
__typename: notif.__typename
|
__typename: notif.__typename
|
||||||
};
|
};
|
||||||
return processed;
|
|
||||||
})
|
})
|
||||||
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
|
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
|
||||||
setNotifications(processedNotifications);
|
setNotifications(processedNotifications);
|
||||||
@@ -133,7 +143,7 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
const loadMore = useCallback(() => {
|
const loadMore = useCallback(() => {
|
||||||
if (!loading && data?.notifications.length) {
|
if (!loading && data?.notifications.length) {
|
||||||
fetchMore({
|
fetchMore({
|
||||||
variables: { offset: data.notifications.length },
|
variables: { offset: data.notifications.length, where: whereClause },
|
||||||
updateQuery: (prev, { fetchMoreResult }) => {
|
updateQuery: (prev, { fetchMoreResult }) => {
|
||||||
if (!fetchMoreResult) return prev;
|
if (!fetchMoreResult) return prev;
|
||||||
return {
|
return {
|
||||||
@@ -145,7 +155,7 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
console.error("Fetch more error:", err);
|
console.error("Fetch more error:", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [data?.notifications?.length, fetchMore, loading]);
|
}, [data?.notifications?.length, fetchMore, loading, whereClause]);
|
||||||
|
|
||||||
const handleToggleUnreadOnly = (value) => {
|
const handleToggleUnreadOnly = (value) => {
|
||||||
setShowUnreadOnly(value);
|
setShowUnreadOnly(value);
|
||||||
@@ -157,7 +167,12 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
const timestamp = new Date().toISOString();
|
const timestamp = new Date().toISOString();
|
||||||
setNotifications((prev) => {
|
setNotifications((prev) => {
|
||||||
const updatedNotifications = prev.map((notif) =>
|
const updatedNotifications = prev.map((notif) =>
|
||||||
notif.read === null ? { ...notif, read: timestamp } : notif
|
notif.read === null && notif.associationid === userAssociationId
|
||||||
|
? {
|
||||||
|
...notif,
|
||||||
|
read: timestamp
|
||||||
|
}
|
||||||
|
: notif
|
||||||
);
|
);
|
||||||
return [...updatedNotifications];
|
return [...updatedNotifications];
|
||||||
});
|
});
|
||||||
@@ -165,7 +180,6 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
.catch((e) => console.error(`Error marking all notifications read: ${e?.message || ""}`));
|
.catch((e) => console.error(`Error marking all notifications read: ${e?.message || ""}`));
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO Tinker
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible && !isConnected) {
|
if (visible && !isConnected) {
|
||||||
refetch();
|
refetch();
|
||||||
@@ -187,4 +201,8 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect((state) => ({ bodyshop: state.user.bodyshop }), null)(NotificationCenterContainer);
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(NotificationCenterContainer);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// contexts/SocketIO/socketContext.jsx
|
|
||||||
import React, { createContext, useContext, useEffect, useRef, useState } from "react";
|
import React, { createContext, useContext, useEffect, useRef, useState } from "react";
|
||||||
import SocketIO from "socket.io-client";
|
import SocketIO from "socket.io-client";
|
||||||
import { auth } from "../../firebase/firebase.utils";
|
import { auth } from "../../firebase/firebase.utils";
|
||||||
@@ -15,10 +14,11 @@ export const SocketProvider = ({ children, bodyshop }) => {
|
|||||||
const [clientId, setClientId] = useState(null);
|
const [clientId, setClientId] = useState(null);
|
||||||
const [isConnected, setIsConnected] = useState(false);
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
const notification = useNotification();
|
const notification = useNotification();
|
||||||
|
const userAssociationId = bodyshop?.associations?.[0]?.id;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initializeSocket = async (token) => {
|
const initializeSocket = async (token) => {
|
||||||
if (!bodyshop || !bodyshop.id || socketRef.current) return; // Prevent multiple instances
|
if (!bodyshop || !bodyshop.id || socketRef.current) return;
|
||||||
|
|
||||||
const endpoint = import.meta.env.PROD ? import.meta.env.VITE_APP_AXIOS_BASE_API_URL : "";
|
const endpoint = import.meta.env.PROD ? import.meta.env.VITE_APP_AXIOS_BASE_API_URL : "";
|
||||||
const socketInstance = SocketIO(endpoint, {
|
const socketInstance = SocketIO(endpoint, {
|
||||||
@@ -41,7 +41,6 @@ export const SocketProvider = ({ children, bodyshop }) => {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!import.meta.env.DEV) return;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConnect = () => {
|
const handleConnect = () => {
|
||||||
@@ -87,6 +86,10 @@ export const SocketProvider = ({ children, bodyshop }) => {
|
|||||||
const handleNotification = (data) => {
|
const handleNotification = (data) => {
|
||||||
const { jobId, jobRoNumber, notificationId, associationId, notifications } = data;
|
const { jobId, jobRoNumber, notificationId, associationId, notifications } = data;
|
||||||
|
|
||||||
|
// Filter out notifications not matching the user's associationId
|
||||||
|
// Technically not required.
|
||||||
|
if (associationId !== userAssociationId) return;
|
||||||
|
|
||||||
const newNotification = {
|
const newNotification = {
|
||||||
__typename: "notifications",
|
__typename: "notifications",
|
||||||
id: notificationId,
|
id: notificationId,
|
||||||
@@ -102,7 +105,11 @@ export const SocketProvider = ({ children, bodyshop }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const baseVariables = { limit: 20, offset: 0, where: {} };
|
const baseVariables = {
|
||||||
|
limit: 20,
|
||||||
|
offset: 0,
|
||||||
|
where: { associationid: { _eq: userAssociationId } }
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const existingNotifications =
|
const existingNotifications =
|
||||||
@@ -126,8 +133,10 @@ export const SocketProvider = ({ children, bodyshop }) => {
|
|||||||
broadcast: true
|
broadcast: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle showUnreadOnly case
|
const unreadVariables = {
|
||||||
const unreadVariables = { ...baseVariables, where: { read: { _is_null: true } } };
|
...baseVariables,
|
||||||
|
where: { ...baseVariables.where, read: { _is_null: true } }
|
||||||
|
};
|
||||||
const unreadNotifications =
|
const unreadNotifications =
|
||||||
client.cache.readQuery({
|
client.cache.readQuery({
|
||||||
query: GET_NOTIFICATIONS,
|
query: GET_NOTIFICATIONS,
|
||||||
@@ -228,7 +237,7 @@ export const SocketProvider = ({ children, bodyshop }) => {
|
|||||||
setIsConnected(false);
|
setIsConnected(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [bodyshop, notification]);
|
}, [bodyshop, notification, userAssociationId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SocketContext.Provider value={{ socket: socketRef.current, clientId, isConnected }}>
|
<SocketContext.Provider value={{ socket: socketRef.current, clientId, isConnected }}>
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ export const GET_NOTIFICATIONS = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const GET_UNREAD_COUNT = gql`
|
export const GET_UNREAD_COUNT = gql`
|
||||||
query GetUnreadCount {
|
query GetUnreadCount($associationid: uuid!) {
|
||||||
notifications_aggregate(where: { read: { _is_null: true } }) {
|
notifications_aggregate(where: { read: { _is_null: true }, associationid: { _eq: $associationid } }) {
|
||||||
aggregate {
|
aggregate {
|
||||||
count
|
count
|
||||||
}
|
}
|
||||||
@@ -29,8 +29,11 @@ export const GET_UNREAD_COUNT = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const MARK_ALL_NOTIFICATIONS_READ = gql`
|
export const MARK_ALL_NOTIFICATIONS_READ = gql`
|
||||||
mutation MarkAllNotificationsRead {
|
mutation MarkAllNotificationsRead($associationid: uuid!) {
|
||||||
update_notifications(where: { read: { _is_null: true } }, _set: { read: "now()" }) {
|
update_notifications(
|
||||||
|
where: { read: { _is_null: true }, associationid: { _eq: $associationid } }
|
||||||
|
_set: { read: "now()" }
|
||||||
|
) {
|
||||||
affected_rows
|
affected_rows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2745,3 +2745,17 @@ query GET_NOTIFICATION_ASSOCIATIONS($emails: [String!]!, $shopid: uuid!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports.INSERT_NOTIFICATIONS_MUTATION = ` mutation INSERT_NOTIFICATIONS($objects: [notifications_insert_input!]!) {
|
||||||
|
insert_notifications(objects: $objects) {
|
||||||
|
affected_rows
|
||||||
|
returning {
|
||||||
|
id
|
||||||
|
jobid
|
||||||
|
associationid
|
||||||
|
scenario_text
|
||||||
|
fcm_text
|
||||||
|
scenario_meta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const { Queue, Worker } = require("bullmq");
|
const { Queue, Worker } = require("bullmq");
|
||||||
|
const { INSERT_NOTIFICATIONS_MUTATION } = require("../../graphql-client/queries");
|
||||||
const graphQLClient = require("../../graphql-client/graphql-client").client;
|
const graphQLClient = require("../../graphql-client/graphql-client").client;
|
||||||
|
|
||||||
// Base time-related constant in minutes, sourced from environment variable or defaulting to 1
|
// Base time-related constant in minutes, sourced from environment variable or defaulting to 1
|
||||||
@@ -20,23 +21,6 @@ const RATE_LIMITER_DURATION = APP_CONSOLIDATION_DELAY * 0.1; // 6 seconds (tenth
|
|||||||
let addQueue;
|
let addQueue;
|
||||||
let consolidateQueue;
|
let consolidateQueue;
|
||||||
|
|
||||||
// Updated GraphQL mutation to insert notifications with the new schema
|
|
||||||
const INSERT_NOTIFICATIONS_MUTATION = `
|
|
||||||
mutation INSERT_NOTIFICATIONS($objects: [notifications_insert_input!]!) {
|
|
||||||
insert_notifications(objects: $objects) {
|
|
||||||
affected_rows
|
|
||||||
returning {
|
|
||||||
id
|
|
||||||
jobid
|
|
||||||
associationid
|
|
||||||
scenario_text
|
|
||||||
fcm_text
|
|
||||||
scenario_meta
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the scenario_text, fcm_text, and scenario_meta for a batch of notifications.
|
* Builds the scenario_text, fcm_text, and scenario_meta for a batch of notifications.
|
||||||
*
|
*
|
||||||
@@ -165,16 +149,16 @@ const loadAppQueue = async ({ pubClient, logger, redisHelpers, ioRedis }) => {
|
|||||||
|
|
||||||
for (const [user, bodyShopData] of Object.entries(allNotifications)) {
|
for (const [user, bodyShopData] of Object.entries(allNotifications)) {
|
||||||
const userRecipients = recipients.filter((r) => r.user === user);
|
const userRecipients = recipients.filter((r) => r.user === user);
|
||||||
const employeeId = userRecipients[0]?.employeeId;
|
const associationId = userRecipients[0]?.associationId;
|
||||||
|
|
||||||
for (const [bodyShopId, notifications] of Object.entries(bodyShopData)) {
|
for (const [bodyShopId, notifications] of Object.entries(bodyShopData)) {
|
||||||
const { scenario_text, fcm_text, scenario_meta } = buildNotificationContent(notifications);
|
const { scenario_text, fcm_text, scenario_meta } = buildNotificationContent(notifications);
|
||||||
notificationInserts.push({
|
notificationInserts.push({
|
||||||
jobid: jobId,
|
jobid: jobId,
|
||||||
associationid: employeeId || null,
|
associationid: associationId,
|
||||||
scenario_text: JSON.stringify(scenario_text), // JSONB requires stringified input
|
scenario_text: JSON.stringify(scenario_text),
|
||||||
fcm_text: fcm_text,
|
fcm_text: fcm_text,
|
||||||
scenario_meta: JSON.stringify(scenario_meta) // JSONB requires stringified input
|
scenario_meta: JSON.stringify(scenario_meta)
|
||||||
});
|
});
|
||||||
notificationIdMap.set(`${user}:${bodyShopId}`, null);
|
notificationIdMap.set(`${user}:${bodyShopId}`, null);
|
||||||
}
|
}
|
||||||
@@ -200,9 +184,8 @@ const loadAppQueue = async ({ pubClient, logger, redisHelpers, ioRedis }) => {
|
|||||||
// Emit notifications to users via Socket.io with notification ID
|
// Emit notifications to users via Socket.io with notification ID
|
||||||
for (const [user, bodyShopData] of Object.entries(allNotifications)) {
|
for (const [user, bodyShopData] of Object.entries(allNotifications)) {
|
||||||
const userMapping = await redisHelpers.getUserSocketMapping(user);
|
const userMapping = await redisHelpers.getUserSocketMapping(user);
|
||||||
// Get all recipients for the user and extract the associationId (employeeId)
|
|
||||||
const userRecipients = recipients.filter((r) => r.user === user);
|
const userRecipients = recipients.filter((r) => r.user === user);
|
||||||
const associationId = userRecipients[0]?.employeeId;
|
const associationId = userRecipients[0]?.associationId;
|
||||||
|
|
||||||
for (const [bodyShopId, notifications] of Object.entries(bodyShopData)) {
|
for (const [bodyShopId, notifications] of Object.entries(bodyShopData)) {
|
||||||
const notificationId = notificationIdMap.get(`${user}:${bodyShopId}`);
|
const notificationId = notificationIdMap.get(`${user}:${bodyShopId}`);
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ const { getJobAssignmentType } = require("./stringHelpers");
|
|||||||
*/
|
*/
|
||||||
const populateWatchers = (data, result) => {
|
const populateWatchers = (data, result) => {
|
||||||
data.scenarioWatchers.forEach((recipients) => {
|
data.scenarioWatchers.forEach((recipients) => {
|
||||||
const { user, app, fcm, email, firstName, lastName, employeeId } = recipients;
|
const { user, app, fcm, email, firstName, lastName, employeeId, associationId } = recipients;
|
||||||
if (app === true) result.app.recipients.push({ user, bodyShopId: data.bodyShopId, employeeId });
|
if (app === true) result.app.recipients.push({ user, bodyShopId: data.bodyShopId, employeeId, associationId });
|
||||||
if (fcm === true) result.fcm.recipients.push(user);
|
if (fcm === true) result.fcm.recipients.push(user);
|
||||||
if (email === true) result.email.recipients.push({ user, firstName, lastName });
|
if (email === true) result.email.recipients.push({ user, firstName, lastName });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const scenarioParser = async (req, jobIdField) => {
|
|||||||
const { event, trigger, table } = req.body;
|
const { event, trigger, table } = req.body;
|
||||||
const { logger } = req;
|
const { logger } = req;
|
||||||
|
|
||||||
// Validate we know what user commited the action that fired the parser
|
// Validate we know what user committed the action that fired the parser
|
||||||
const hasuraUserId = event?.session_variables?.["x-hasura-user-id"];
|
const hasuraUserId = event?.session_variables?.["x-hasura-user-id"];
|
||||||
|
|
||||||
// Bail if we don't know
|
// Bail if we don't know
|
||||||
@@ -140,9 +140,10 @@ const scenarioParser = async (req, jobIdField) => {
|
|||||||
email: settings.email,
|
email: settings.email,
|
||||||
app: settings.app,
|
app: settings.app,
|
||||||
fcm: settings.fcm,
|
fcm: settings.fcm,
|
||||||
firstName: matchingWatcher ? matchingWatcher.firstName : undefined,
|
firstName: matchingWatcher?.firstName,
|
||||||
lastName: matchingWatcher ? matchingWatcher.lastName : undefined,
|
lastName: matchingWatcher?.lastName,
|
||||||
employeeId: matchingWatcher ? assoc.id : undefined
|
employeeId: matchingWatcher?.employeeId,
|
||||||
|
associationId: assoc.id
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user