feature/IO-3291-Tasks-Notifications: Checkpoint

This commit is contained in:
Dave Richer
2025-07-10 16:40:55 -04:00
parent 1127864ba9
commit cf181dfd0a
2 changed files with 135 additions and 86 deletions

View File

@@ -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);

View File

@@ -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<Object>} 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<Object>} 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.
*