IO-2924-Refactor-Production-board-to-use-Socket-Provider: Finalize
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
@@ -3,7 +3,7 @@ import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import {
|
import {
|
||||||
GET_JOB_BY_PK,
|
QUERY_EXACT_JOB_IN_PRODUCTION,
|
||||||
QUERY_JOBS_IN_PRODUCTION,
|
QUERY_JOBS_IN_PRODUCTION,
|
||||||
SUBSCRIPTION_JOBS_IN_PRODUCTION,
|
SUBSCRIPTION_JOBS_IN_PRODUCTION,
|
||||||
SUBSCRIPTION_JOBS_IN_PRODUCTION_VIEW
|
SUBSCRIPTION_JOBS_IN_PRODUCTION_VIEW
|
||||||
@@ -100,7 +100,7 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionTyp
|
|||||||
query: QUERY_JOBS_IN_PRODUCTION,
|
query: QUERY_JOBS_IN_PRODUCTION,
|
||||||
data: {
|
data: {
|
||||||
jobs: existingJobs.map((job) =>
|
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
|
// If the job doesn't exist, fetch it from the server and then add it to the cache
|
||||||
try {
|
try {
|
||||||
const { data: jobData } = await client.query({
|
const { data: jobData } = await client.query({
|
||||||
query: GET_JOB_BY_PK,
|
query: QUERY_EXACT_JOB_IN_PRODUCTION,
|
||||||
variables: { id: jobId },
|
variables: { id: jobId },
|
||||||
fetchPolicy: "network-only"
|
fetchPolicy: "network-only"
|
||||||
});
|
});
|
||||||
@@ -117,7 +117,7 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionTyp
|
|||||||
client.writeQuery({
|
client.writeQuery({
|
||||||
query: QUERY_JOBS_IN_PRODUCTION,
|
query: QUERY_JOBS_IN_PRODUCTION,
|
||||||
data: {
|
data: {
|
||||||
jobs: [...existingJobs, { ...jobData.job, __typename: "Job" }]
|
jobs: [...existingJobs, { ...jobData.job, __typename: "jobs" }]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -127,11 +127,11 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionTyp
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Listen for 'job-changed' events
|
// Listen for 'job-changed' events
|
||||||
socket.on("job-updated", handleJobUpdates);
|
socket.on("production-job-updated", handleJobUpdates);
|
||||||
|
|
||||||
// Clean up on unmount or when dependencies change
|
// Clean up on unmount or when dependencies change
|
||||||
return () => {
|
return () => {
|
||||||
socket.off("job-updated", handleJobUpdates);
|
socket.off("production-job-updated", handleJobUpdates);
|
||||||
};
|
};
|
||||||
}, [subscriptionEnabled, socket, bodyshop, data, client]);
|
}, [subscriptionEnabled, socket, bodyshop, data, client]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
|
import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
QUERY_EXACT_JOB_IN_PRODUCTION,
|
QUERY_EXACT_JOB_IN_PRODUCTION,
|
||||||
QUERY_EXACT_JOBS_IN_PRODUCTION,
|
QUERY_EXACT_JOBS_IN_PRODUCTION,
|
||||||
@@ -9,19 +9,42 @@ import {
|
|||||||
} from "../../graphql/jobs.queries";
|
} from "../../graphql/jobs.queries";
|
||||||
import ProductionListTable from "./production-list-table.component";
|
import ProductionListTable from "./production-list-table.component";
|
||||||
import _ from "lodash";
|
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, {
|
const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION, {
|
||||||
pollInterval: 3600000,
|
pollInterval: 3600000,
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only"
|
nextFetchPolicy: "network-only"
|
||||||
});
|
});
|
||||||
const client = useApolloClient();
|
|
||||||
const [joblist, setJoblist] = useState([]);
|
// Use GraphQL subscription when subscription is enabled
|
||||||
const { data: updatedJobs } = useSubscription(
|
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(() => {
|
useEffect(() => {
|
||||||
if (!(data && data.jobs)) return;
|
if (!(data && data.jobs)) return;
|
||||||
setJoblist(
|
setJoblist(
|
||||||
@@ -31,34 +54,99 @@ export default function ProductionListTableContainer({ subscriptionType = "direc
|
|||||||
);
|
);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
|
// Handle updates from GraphQL subscription
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!updatedJobs || joblist.length === 0) return;
|
if (subscriptionEnabled) {
|
||||||
|
if (!updatedJobs || joblist.length === 0) return;
|
||||||
|
|
||||||
const jobDiff = _.differenceWith(
|
const jobDiff = _.differenceWith(
|
||||||
joblist,
|
joblist,
|
||||||
updatedJobs.jobs,
|
updatedJobs.jobs,
|
||||||
(a, b) => a.id === b.id && a.updated_at === b.updated_at
|
(a, b) => a.id === b.id && a.updated_at === b.updated_at
|
||||||
);
|
);
|
||||||
|
|
||||||
if (jobDiff.length > 1) {
|
if (jobDiff.length > 1) {
|
||||||
getUpdatedJobsData(jobDiff.map((j) => j.id));
|
getUpdatedJobsData(jobDiff.map((j) => j.id));
|
||||||
} else if (jobDiff.length === 1) {
|
} else if (jobDiff.length === 1) {
|
||||||
jobDiff.forEach((job) => {
|
jobDiff.forEach((job) => {
|
||||||
getUpdatedJobData(job.id);
|
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);
|
const handleJobUpdates = async (jobChangedData) => {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
const jobId = jobChangedData.id;
|
||||||
}, [updatedJobs]);
|
|
||||||
|
|
||||||
|
// 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) => {
|
const getUpdatedJobData = async (jobId) => {
|
||||||
client.query({
|
await client.query({
|
||||||
query: QUERY_EXACT_JOB_IN_PRODUCTION,
|
query: QUERY_EXACT_JOB_IN_PRODUCTION,
|
||||||
variables: { id: jobId }
|
variables: { id: jobId },
|
||||||
|
fetchPolicy: "network-only"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const getUpdatedJobsData = async (jobIds) => {
|
const getUpdatedJobsData = (jobIds) => {
|
||||||
client.query({
|
client.query({
|
||||||
query: QUERY_EXACT_JOBS_IN_PRODUCTION,
|
query: QUERY_EXACT_JOBS_IN_PRODUCTION,
|
||||||
variables: { ids: jobIds }
|
variables: { ids: jobIds }
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function ProductionListComponent({ bodyshop }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NoteUpsertModal />
|
<NoteUpsertModal />
|
||||||
<ProductionListTable subscriptionType={Production_Use_View.treatment} />
|
<ProductionListTable bodyshop={bodyshop} subscriptionType={Production_Use_View.treatment} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const jobUpdated = async (req, res) => {
|
|||||||
const bodyshopID = updatedJob.shopid;
|
const bodyshopID = updatedJob.shopid;
|
||||||
|
|
||||||
// Emit the job-updated event only to the room corresponding to the bodyshop
|
// 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" });
|
return res.json({ message: "Job updated and event emitted" });
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user