188 lines
6.7 KiB
JavaScript
188 lines
6.7 KiB
JavaScript
import React, { useContext, useEffect, useMemo, useRef } from "react";
|
|
import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import {
|
|
QUERY_EXACT_JOB_IN_PRODUCTION,
|
|
QUERY_JOBS_IN_PRODUCTION,
|
|
SUBSCRIPTION_JOBS_IN_PRODUCTION,
|
|
SUBSCRIPTION_JOBS_IN_PRODUCTION_VIEW
|
|
} from "../../graphql/jobs.queries";
|
|
import { QUERY_KANBAN_SETTINGS } from "../../graphql/user.queries";
|
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
|
import ProductionBoardKanbanComponent from "./production-board-kanban.component";
|
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
|
import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
bodyshop: selectBodyshop,
|
|
currentUser: selectCurrentUser
|
|
});
|
|
|
|
function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionType = "direct" }) {
|
|
const fired = useRef(false);
|
|
const client = useApolloClient();
|
|
const { socket } = useContext(SocketContext); // Get the socket from context
|
|
const reconnectTimeout = useRef(null); // To store the reconnect timeout
|
|
const disconnectTime = useRef(null); // To track disconnection time
|
|
const acceptableReconnectTime = 2000; // 2 seconds threshold
|
|
|
|
const {
|
|
treatments: { Websocket_Production }
|
|
} = useSplitTreatments({
|
|
attributes: {},
|
|
names: ["Websocket_Production"],
|
|
splitKey: bodyshop && bodyshop.imexshopid
|
|
});
|
|
|
|
const combinedStatuses = useMemo(
|
|
() => [
|
|
...bodyshop.md_ro_statuses.production_statuses,
|
|
...(bodyshop.md_ro_statuses.additional_board_statuses || [])
|
|
],
|
|
[bodyshop.md_ro_statuses.production_statuses, bodyshop.md_ro_statuses.additional_board_statuses]
|
|
);
|
|
|
|
const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION, {
|
|
pollInterval: 3600000,
|
|
fetchPolicy: "network-only",
|
|
nextFetchPolicy: "network-only",
|
|
onError: (error) => console.error(`Error fetching jobs in production: ${error.message}`)
|
|
});
|
|
|
|
const subscriptionEnabled = Websocket_Production?.treatment === "off";
|
|
|
|
const { data: updatedJobs } = useSubscription(
|
|
subscriptionType === "view" ? SUBSCRIPTION_JOBS_IN_PRODUCTION_VIEW : SUBSCRIPTION_JOBS_IN_PRODUCTION,
|
|
{
|
|
skip: !subscriptionEnabled,
|
|
onError: (error) => console.error(`Error subscribing to jobs in production: ${error.message}`)
|
|
}
|
|
);
|
|
|
|
const { loading: associationSettingsLoading, data: associationSettings } = useQuery(QUERY_KANBAN_SETTINGS, {
|
|
variables: { email: currentUser.email },
|
|
onError: (error) => console.error(`Error fetching Kanban settings: ${error.message}`)
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (subscriptionEnabled) {
|
|
if (!updatedJobs) {
|
|
return;
|
|
}
|
|
if (!fired.current) {
|
|
fired.current = true;
|
|
return;
|
|
}
|
|
refetch().catch((err) => console.error(`Error re-fetching jobs in production: ${err.message}`));
|
|
}
|
|
}, [updatedJobs, refetch, subscriptionEnabled]);
|
|
|
|
// Socket.IO implementation for users with Split treatment "off"
|
|
useEffect(() => {
|
|
if (subscriptionEnabled || !socket || !bodyshop || !bodyshop.id) {
|
|
return;
|
|
}
|
|
|
|
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}`);
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleDisconnect = () => {
|
|
// Capture the disconnection time
|
|
disconnectTime.current = Date.now();
|
|
};
|
|
|
|
const handleReconnect = () => {
|
|
const reconnectTime = Date.now();
|
|
const disconnectionDuration = reconnectTime - disconnectTime.current;
|
|
|
|
// Only refetch if disconnection was longer than the acceptable reconnect time
|
|
if (disconnectionDuration >= acceptableReconnectTime) {
|
|
if (!reconnectTimeout.current) {
|
|
reconnectTimeout.current = setTimeout(() => {
|
|
const randomDelay = Math.floor(Math.random() * (30000 - 10000 + 1)) + 10000; // Random delay between 10 and 30 seconds
|
|
setTimeout(() => {
|
|
if (refetch) refetch().catch((err) => console.error(`Issue `));
|
|
reconnectTimeout.current = null; // Clear the timeout reference after refetch
|
|
}, randomDelay);
|
|
}, acceptableReconnectTime);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Listen for 'job-changed', 'disconnect', and 'connect' events
|
|
socket.on("production-job-updated", handleJobUpdates);
|
|
socket.on("disconnect", handleDisconnect);
|
|
socket.on("connect", handleReconnect);
|
|
|
|
// Clean up on unmount or when dependencies change
|
|
return () => {
|
|
socket.off("production-job-updated", handleJobUpdates);
|
|
socket.off("disconnect", handleDisconnect);
|
|
socket.off("connect", handleReconnect);
|
|
if (reconnectTimeout.current) {
|
|
clearTimeout(reconnectTimeout.current);
|
|
}
|
|
};
|
|
}, [subscriptionEnabled, socket, bodyshop, client, refetch]);
|
|
|
|
const filteredAssociationSettings = useMemo(() => {
|
|
return associationSettings?.associations[0] || null;
|
|
}, [associationSettings?.associations]);
|
|
|
|
return (
|
|
<ProductionBoardKanbanComponent
|
|
loading={loading || associationSettingsLoading}
|
|
data={data ? data.jobs : []}
|
|
refetch={refetch}
|
|
associationSettings={filteredAssociationSettings}
|
|
bodyshop={bodyshop}
|
|
statuses={combinedStatuses}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export default connect(mapStateToProps)(ProductionBoardKanbanContainer);
|