diff --git a/client/src/contexts/SocketIO/socketProvider.jsx b/client/src/contexts/SocketIO/socketProvider.jsx index ea65c031c..33ac756a3 100644 --- a/client/src/contexts/SocketIO/socketProvider.jsx +++ b/client/src/contexts/SocketIO/socketProvider.jsx @@ -22,7 +22,7 @@ import { QUERY_TASKS_WITH_DUE_DATES } from "../../graphql/tasks.queries"; -const LIMIT = 50; // Tasks per page for no-due-date tasks +const LIMIT = INITIAL_NOTIFICATIONS; /** * Socket Provider - Scenario Notifications / Web Socket related items @@ -181,101 +181,142 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => { const assignedToId = bodyshop?.employees?.find((e) => e.user_email === currentUser?.email)?.id; if (!assignedToId || payload.assigned_to !== assignedToId) return; - // Handle due date tasks cache update const dueVars = { bodyshop: bodyshop?.id, assigned_to: assignedToId, order: [{ due_date: "asc" }, { created_at: "desc" }] }; - let hasDueCache = false; - try { - client.readQuery({ query: QUERY_TASKS_WITH_DUE_DATES, variables: dueVars }); - hasDueCache = true; - } catch (e) {} - if (hasDueCache) { - client - .query({ query: QUERY_TASKS_WITH_DUE_DATES, variables: dueVars, fetchPolicy: "network-only" }) - .then(({ data }) => { - client.writeQuery({ query: QUERY_TASKS_WITH_DUE_DATES, variables: dueVars, data }); - }) - .catch((e) => console.error("Task due refetch error:", e)); - } - - // Handle no due date tasks cache update - const originalNoDueVars = { + const noDueVars = { bodyshop: bodyshop?.id, assigned_to: assignedToId, order: [{ created_at: "desc" }], limit: LIMIT, offset: 0 }; - let currentLength = 0; - try { - const existing = client.readQuery({ - query: QUERY_TASKS_NO_DUE_DATE_PAGINATED, - variables: originalNoDueVars - }); - currentLength = existing?.tasks?.length || 0; - } catch (e) {} - if (currentLength > 0) { - let fetchLimit = currentLength; - if (message.type === "task-created") { - fetchLimit += 1; - } else if (message.type === "task-deleted") { - fetchLimit = Math.max(currentLength - 1, 0); - } - if (fetchLimit > 0) { - const fetchVars = { ...originalNoDueVars, limit: fetchLimit }; - client - .query({ - query: QUERY_TASKS_NO_DUE_DATE_PAGINATED, - variables: fetchVars, - fetchPolicy: "network-only" - }) - .then(({ data }) => { - client.writeQuery({ - query: QUERY_TASKS_NO_DUE_DATE_PAGINATED, - variables: originalNoDueVars, - data: { - tasks: data.tasks, - tasks_aggregate: data.tasks_aggregate - } - }); - }) - .catch((e) => console.error("Task no due refetch error:", e)); - } else { - client.writeQuery({ - query: QUERY_TASKS_NO_DUE_DATE_PAGINATED, - variables: originalNoDueVars, - data: { tasks: [], tasks_aggregate: { aggregate: { count: 0 } } } - }); - } - } - - // Handle task count cache update const countVars = { assigned_to: assignedToId, bodyshopid: bodyshop?.id }; - let hasCountCache = false; - try { - client.readQuery({ query: QUERY_MY_TASKS_COUNT, variables: countVars }); - hasCountCache = true; - } catch (e) {} - if (hasCountCache) { - client - .query({ query: QUERY_MY_TASKS_COUNT, variables: countVars, fetchPolicy: "network-only" }) - .then(({ data }) => { - client.writeQuery({ query: QUERY_MY_TASKS_COUNT, variables: countVars, data }); - }) - .catch((e) => console.error("Task count refetch error:", e)); + + // Helper to update a list in cache locally + const updateListCache = (query, vars, action) => { + // action: 'add' or 'remove' + try { + const current = client.readQuery({ query, variables: vars }); + if (!current) return; // List not loaded yet, skip + + let updatedTasks = [...current.tasks]; + let delta = 0; + + if (action === "remove") { + const prevLength = updatedTasks.length; + updatedTasks = updatedTasks.filter((task) => task.id !== payload.id); + if (updatedTasks.length < prevLength) delta -= 1; + } + + if (action === "add") { + const exists = updatedTasks.some((task) => task.id === payload.id); + if (!exists) { + const newTask = { ...payload, __typename: "tasks" }; + updatedTasks.push(newTask); + delta += 1; + } + } + + // Sort the array based on the query type if delta !== 0 + if (delta !== 0) { + if (query === QUERY_TASKS_WITH_DUE_DATES) { + updatedTasks.sort((a, b) => { + const da = new Date(a.due_date); + const db = new Date(b.due_date); + if (da < db) return -1; + if (da > db) return 1; + const ca = new Date(a.created_at); + const cb = new Date(b.created_at); + return cb - ca; // desc + }); + } else if (query === QUERY_TASKS_NO_DUE_DATE_PAGINATED) { + updatedTasks.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); + } + } + + let data = { ...current, tasks: updatedTasks }; + + // Adjust aggregate count for no due date query + if (query === QUERY_TASKS_NO_DUE_DATE_PAGINATED && delta !== 0 && current.tasks_aggregate) { + const newCount = Math.max(0, current.tasks_aggregate.aggregate.count + delta); + data = { + ...data, + tasks_aggregate: { + ...current.tasks_aggregate, + aggregate: { + ...current.tasks_aggregate.aggregate, + count: newCount + } + } + }; + } + + client.writeQuery({ query, variables: vars, data }); + } catch (error) { + console.error("Error updating task list cache:", error); + } + }; + + // Update lists based on event type + if (message.type === "task-deleted") { + updateListCache(QUERY_TASKS_WITH_DUE_DATES, dueVars, "remove"); + updateListCache(QUERY_TASKS_NO_DUE_DATE_PAGINATED, noDueVars, "remove"); + } else { + const targetQuery = payload.due_date ? QUERY_TASKS_WITH_DUE_DATES : QUERY_TASKS_NO_DUE_DATE_PAGINATED; + const targetVars = payload.due_date ? dueVars : noDueVars; + + if (message.type === "task-updated") { + // Remove from both in case of due_date change + updateListCache(QUERY_TASKS_WITH_DUE_DATES, dueVars, "remove"); + updateListCache(QUERY_TASKS_NO_DUE_DATE_PAGINATED, noDueVars, "remove"); + } + + // Add to the target list + updateListCache(targetQuery, targetVars, "add"); } + + // Locally update the total count (no network call) + try { + const currentCountQuery = client.readQuery({ query: QUERY_MY_TASKS_COUNT, variables: countVars }); + if (currentCountQuery) { + let countDelta = 0; + if (message.type === "task-created") countDelta = 1; + else if (message.type === "task-deleted") countDelta = -1; + // For "task-updated", assume no change to count unless specific logic (e.g., completion) + + if (countDelta !== 0) { + const newCount = Math.max(0, currentCountQuery.tasks_aggregate.aggregate.count + countDelta); + client.writeQuery({ + query: QUERY_MY_TASKS_COUNT, + variables: countVars, + data: { + tasks_aggregate: { + __typename: "tasks_aggregate", + aggregate: { + __typename: "tasks_aggregate_fields", + count: newCount + } + } + } + }); + } + } + } catch (error) { + console.error("Error updating task count cache:", error); + } + break; default: break; } }; - + const handleConnect = () => { socketInstance.emit("join-bodyshop-room", bodyshop.id); setClientId(socketInstance.id); diff --git a/server/notifications/eventHandlers.js b/server/notifications/eventHandlers.js index 750755995..87a1dceed 100644 --- a/server/notifications/eventHandlers.js +++ b/server/notifications/eventHandlers.js @@ -146,17 +146,10 @@ const handlePaymentsChange = async (req, res) => processNotificationEvent(req, res, "req.body.event.new.jobid", "Payments Changed Notification Event Handled."); /** - * Handle tasks change notifications. - * - * @param {Object} req - Express request object. - * @param {Object} res - Express response object. - * @returns {Promise} JSON response with a success message. + * Handle task socket emit. + * @param req */ -const handleTasksChange = async (req, res) => { - console.log("Handling tasks change notification...!!!!!!!!!!!!"); - // Handle Notification Event - processNotificationEvent(req, res, "req.body.event.new.jobid", "Tasks Notifications Event Handled."); - +const handleTaskSocketEmit = (req) => { const { logger, ioRedis, @@ -184,7 +177,7 @@ const handleTasksChange = async (req, res) => { if (newData.deleted && !oldData.deleted) { type = "task-deleted"; - taskData = { id: newData.id }; + taskData = { id: newData.id, assigned_to: newData.assigned_to }; } else if (!newData.deleted && oldData.deleted) { type = "task-created"; } else if (!newData.deleted) { @@ -202,6 +195,21 @@ const handleTasksChange = async (req, res) => { logger.log("tasks-event-missing-data", "error", "notifications", null, { bodyshopId, hasIo: !!ioRedis, type }); } }; + +/** + * Handle tasks change notifications. + * Note: this also handles task center notifications. + * + * @param {Object} req - Express request object. + * @param {Object} res - Express response object. + * @returns {Promise} JSON response with a success message. + */ +const handleTasksChange = async (req, res) => { + // Handle Notification Event + processNotificationEvent(req, res, "req.body.event.new.jobid", "Tasks Notifications Event Handled."); + handleTaskSocketEmit(req); +}; + /** * Handle time tickets change notifications. *