IO-2924-Refactor-Production-board-to-use-Socket-Provider: Finalize

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-09-26 14:03:27 -04:00
parent b286ab2439
commit db0c16f31d
4 changed files with 119 additions and 31 deletions

View File

@@ -3,7 +3,7 @@ import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
GET_JOB_BY_PK,
QUERY_EXACT_JOB_IN_PRODUCTION,
QUERY_JOBS_IN_PRODUCTION,
SUBSCRIPTION_JOBS_IN_PRODUCTION,
SUBSCRIPTION_JOBS_IN_PRODUCTION_VIEW
@@ -100,7 +100,7 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionTyp
query: QUERY_JOBS_IN_PRODUCTION,
data: {
jobs: existingJobs.map((job) =>
job.id === jobId ? { ...existingJob, ...jobChangedData, __typename: "Job" } : job
job.id === jobId ? { ...existingJob, ...jobChangedData, __typename: "jobs" } : job
)
}
});
@@ -108,7 +108,7 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionTyp
// If the job doesn't exist, fetch it from the server and then add it to the cache
try {
const { data: jobData } = await client.query({
query: GET_JOB_BY_PK,
query: QUERY_EXACT_JOB_IN_PRODUCTION,
variables: { id: jobId },
fetchPolicy: "network-only"
});
@@ -117,7 +117,7 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionTyp
client.writeQuery({
query: QUERY_JOBS_IN_PRODUCTION,
data: {
jobs: [...existingJobs, { ...jobData.job, __typename: "Job" }]
jobs: [...existingJobs, { ...jobData.job, __typename: "jobs" }]
}
});
} catch (error) {
@@ -127,11 +127,11 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionTyp
};
// Listen for 'job-changed' events
socket.on("job-updated", handleJobUpdates);
socket.on("production-job-updated", handleJobUpdates);
// Clean up on unmount or when dependencies change
return () => {
socket.off("job-updated", handleJobUpdates);
socket.off("production-job-updated", handleJobUpdates);
};
}, [subscriptionEnabled, socket, bodyshop, data, client]);

View File

@@ -1,5 +1,5 @@
import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
import React, { useEffect, useState } from "react";
import React, { useContext, useEffect, useState } from "react";
import {
QUERY_EXACT_JOB_IN_PRODUCTION,
QUERY_EXACT_JOBS_IN_PRODUCTION,
@@ -9,19 +9,42 @@ import {
} from "../../graphql/jobs.queries";
import ProductionListTable from "./production-list-table.component";
import _ from "lodash";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
export default function ProductionListTableContainer({ subscriptionType = "direct" }) {
export default function ProductionListTableContainer({ bodyshop, subscriptionType = "direct" }) {
const client = useApolloClient();
const { socket } = useContext(SocketContext);
const [joblist, setJoblist] = useState([]);
// Get Split treatment
const {
treatments: { Websocket_Production }
} = useSplitTreatments({
attributes: {},
names: ["Websocket_Production"],
splitKey: bodyshop && bodyshop.imexshopid
});
// Determine if subscription is enabled
const subscriptionEnabled = Websocket_Production?.treatment === "on";
// Use GraphQL query
const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION, {
pollInterval: 3600000,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only"
});
const client = useApolloClient();
const [joblist, setJoblist] = useState([]);
// Use GraphQL subscription when subscription is enabled
const { data: updatedJobs } = useSubscription(
subscriptionType === "view" ? SUBSCRIPTION_JOBS_IN_PRODUCTION_VIEW : SUBSCRIPTION_JOBS_IN_PRODUCTION
subscriptionType === "view" ? SUBSCRIPTION_JOBS_IN_PRODUCTION_VIEW : SUBSCRIPTION_JOBS_IN_PRODUCTION,
{
skip: !subscriptionEnabled
}
);
// Update joblist when data changes
useEffect(() => {
if (!(data && data.jobs)) return;
setJoblist(
@@ -31,34 +54,99 @@ export default function ProductionListTableContainer({ subscriptionType = "direc
);
}, [data]);
// Handle updates from GraphQL subscription
useEffect(() => {
if (!updatedJobs || joblist.length === 0) return;
if (subscriptionEnabled) {
if (!updatedJobs || joblist.length === 0) return;
const jobDiff = _.differenceWith(
joblist,
updatedJobs.jobs,
(a, b) => a.id === b.id && a.updated_at === b.updated_at
);
const jobDiff = _.differenceWith(
joblist,
updatedJobs.jobs,
(a, b) => a.id === b.id && a.updated_at === b.updated_at
);
if (jobDiff.length > 1) {
getUpdatedJobsData(jobDiff.map((j) => j.id));
} else if (jobDiff.length === 1) {
jobDiff.forEach((job) => {
getUpdatedJobData(job.id);
});
if (jobDiff.length > 1) {
getUpdatedJobsData(jobDiff.map((j) => j.id));
} else if (jobDiff.length === 1) {
jobDiff.forEach((job) => {
getUpdatedJobData(job.id);
});
}
setJoblist(updatedJobs.jobs);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [updatedJobs, subscriptionEnabled]);
// Handle updates from Socket.IO when subscription is disabled
useEffect(() => {
if (subscriptionEnabled || !socket || !bodyshop || !bodyshop.id) {
return;
}
setJoblist(updatedJobs.jobs);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [updatedJobs]);
const handleJobUpdates = async (jobChangedData) => {
const jobId = jobChangedData.id;
// Access the existing cache for QUERY_JOBS_IN_PRODUCTION
const existingJobsCache = client.readQuery({
query: QUERY_JOBS_IN_PRODUCTION
});
const existingJobs = existingJobsCache?.jobs || [];
// Check if the job already exists in the cached jobs
const existingJob = existingJobs.find((job) => job.id === jobId);
if (existingJob) {
// If the job exists, we update the cache without making any additional queries
client.writeQuery({
query: QUERY_JOBS_IN_PRODUCTION,
data: {
jobs: existingJobs.map((job) =>
job.id === jobId ? { ...existingJob, ...jobChangedData, __typename: "jobs" } : job
)
}
});
} else {
// If the job doesn't exist, fetch it from the server and then add it to the cache
try {
const { data: jobData } = await client.query({
query: QUERY_EXACT_JOB_IN_PRODUCTION,
variables: { id: jobId },
fetchPolicy: "network-only"
});
// Add the job to the existing cached jobs
client.writeQuery({
query: QUERY_JOBS_IN_PRODUCTION,
data: {
jobs: [...existingJobs, { ...jobData.job, __typename: "jobs" }]
}
});
} catch (error) {
console.error(`Error fetching job ${jobId}: ${error.message}`);
}
}
};
// Listen for 'production-job-updated' events
socket.on("production-job-updated", handleJobUpdates);
// Clean up on unmount or when dependencies change
return () => {
socket.off("production-job-updated", handleJobUpdates);
};
}, [subscriptionEnabled, socket, bodyshop, client]);
// Functions to fetch updated job data
const getUpdatedJobData = async (jobId) => {
client.query({
await client.query({
query: QUERY_EXACT_JOB_IN_PRODUCTION,
variables: { id: jobId }
variables: { id: jobId },
fetchPolicy: "network-only"
});
};
const getUpdatedJobsData = async (jobIds) => {
const getUpdatedJobsData = (jobIds) => {
client.query({
query: QUERY_EXACT_JOBS_IN_PRODUCTION,
variables: { ids: jobIds }

View File

@@ -26,7 +26,7 @@ export function ProductionListComponent({ bodyshop }) {
return (
<>
<NoteUpsertModal />
<ProductionListTable subscriptionType={Production_Use_View.treatment} />
<ProductionListTable bodyshop={bodyshop} subscriptionType={Production_Use_View.treatment} />
</>
);
}

View File

@@ -24,7 +24,7 @@ const jobUpdated = async (req, res) => {
const bodyshopID = updatedJob.shopid;
// Emit the job-updated event only to the room corresponding to the bodyshop
ioRedis.to(ioHelpers.getBodyshopRoom(bodyshopID)).emit("job-updated", updatedJob);
ioRedis.to(ioHelpers.getBodyshopRoom(bodyshopID)).emit("production-job-updated", updatedJob);
return res.json({ message: "Job updated and event emitted" });
};