Merged in feature/IO-2924-Refactor-Production-board-to-use-Socket-Provider (pull request #1791)
IO-2924 add state sync on reconnect, correct treatment, and add status. Approved-by: Dave Richer
This commit is contained in:
@@ -47,7 +47,7 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionTyp
|
|||||||
onError: (error) => console.error(`Error fetching jobs in production: ${error.message}`)
|
onError: (error) => console.error(`Error fetching jobs in production: ${error.message}`)
|
||||||
});
|
});
|
||||||
|
|
||||||
const subscriptionEnabled = Websocket_Production?.treatment === "on";
|
const subscriptionEnabled = Websocket_Production?.treatment === "off";
|
||||||
|
|
||||||
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,
|
||||||
@@ -126,14 +126,19 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionTyp
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleReconnect = () => {
|
||||||
|
//If we were disconnected from the board, we missed stuff. We need to refresh it entirely.
|
||||||
|
if (refetch) refetch();
|
||||||
|
};
|
||||||
// Listen for 'job-changed' events
|
// Listen for 'job-changed' events
|
||||||
socket.on("production-job-updated", handleJobUpdates);
|
socket.on("production-job-updated", handleJobUpdates);
|
||||||
|
socket.on("reconnect", handleReconnect);
|
||||||
// Clean up on unmount or when dependencies change
|
// Clean up on unmount or when dependencies change
|
||||||
return () => {
|
return () => {
|
||||||
socket.off("production-job-updated", handleJobUpdates);
|
socket.off("production-job-updated", handleJobUpdates);
|
||||||
|
socket.off("reconnect", handleReconnect);
|
||||||
};
|
};
|
||||||
}, [subscriptionEnabled, socket, bodyshop, data, client]);
|
}, [subscriptionEnabled, socket, bodyshop, data, client, refetch]);
|
||||||
|
|
||||||
const filteredAssociationSettings = useMemo(() => {
|
const filteredAssociationSettings = useMemo(() => {
|
||||||
return associationSettings?.associations[0] || null;
|
return associationSettings?.associations[0] || null;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default function ProductionListTableContainer({ bodyshop, subscriptionTyp
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Determine if subscription is enabled
|
// Determine if subscription is enabled
|
||||||
const subscriptionEnabled = Websocket_Production?.treatment === "on";
|
const subscriptionEnabled = Websocket_Production?.treatment === "off";
|
||||||
|
|
||||||
// Use GraphQL query
|
// Use GraphQL query
|
||||||
const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION, {
|
const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION, {
|
||||||
@@ -128,15 +128,20 @@ export default function ProductionListTableContainer({ bodyshop, subscriptionTyp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const handleReconnect = () => {
|
||||||
|
//If we were disconnected from the board, we missed stuff. We need to refresh it entirely.
|
||||||
|
if (refetch) refetch();
|
||||||
|
};
|
||||||
// Listen for 'production-job-updated' events
|
// Listen for 'production-job-updated' events
|
||||||
socket.on("production-job-updated", handleJobUpdates);
|
socket.on("production-job-updated", handleJobUpdates);
|
||||||
|
socket.on("reconnect", handleReconnect);
|
||||||
|
|
||||||
// Clean up on unmount or when dependencies change
|
// Clean up on unmount or when dependencies change
|
||||||
return () => {
|
return () => {
|
||||||
socket.off("production-job-updated", handleJobUpdates);
|
socket.off("production-job-updated", handleJobUpdates);
|
||||||
|
socket.off("reconnect", handleReconnect);
|
||||||
};
|
};
|
||||||
}, [subscriptionEnabled, socket, bodyshop, client]);
|
}, [subscriptionEnabled, socket, bodyshop, client, refetch]);
|
||||||
|
|
||||||
// Functions to fetch updated job data
|
// Functions to fetch updated job data
|
||||||
const getUpdatedJobData = async (jobId) => {
|
const getUpdatedJobData = async (jobId) => {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { connect } from "react-redux";
|
||||||
|
import { GlobalOutlined } from "@ant-design/icons";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import React from "react";
|
||||||
|
import { selectWssStatus } from "../../redux/application/application.selectors";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
//currentUser: selectCurrentUser
|
||||||
|
wssStatus: selectWssStatus
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(WssStatusDisplay);
|
||||||
|
|
||||||
|
export function WssStatusDisplay({ wssStatus }) {
|
||||||
|
console.log("🚀 ~ WssStatusDisplay ~ wssStatus:", wssStatus);
|
||||||
|
return <GlobalOutlined style={{ color: wssStatus === "connected" ? "green" : "red", marginRight: ".5rem" }} />;
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState, useRef } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
import SocketIO from "socket.io-client";
|
import SocketIO from "socket.io-client";
|
||||||
import { auth } from "../../firebase/firebase.utils";
|
import { auth } from "../../firebase/firebase.utils";
|
||||||
|
import { store } from "../../redux/store";
|
||||||
|
import { setWssStatus } from "../../redux/application/application.actions";
|
||||||
const useSocket = (bodyshop) => {
|
const useSocket = (bodyshop) => {
|
||||||
const socketRef = useRef(null);
|
const socketRef = useRef(null);
|
||||||
const [clientId, setClientId] = useState(null);
|
const [clientId, setClientId] = useState(null);
|
||||||
@@ -38,18 +39,22 @@ const useSocket = (bodyshop) => {
|
|||||||
console.log("Socket connected:", socketInstance.id);
|
console.log("Socket connected:", socketInstance.id);
|
||||||
socketInstance.emit("join-bodyshop-room", bodyshop.id);
|
socketInstance.emit("join-bodyshop-room", bodyshop.id);
|
||||||
setClientId(socketInstance.id);
|
setClientId(socketInstance.id);
|
||||||
|
store.dispatch(setWssStatus("connected"))
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReconnect = (attempt) => {
|
const handleReconnect = (attempt) => {
|
||||||
console.log(`Socket reconnected after ${attempt} attempts`);
|
console.log(`Socket reconnected after ${attempt} attempts`);
|
||||||
|
store.dispatch(setWssStatus("connected"))
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConnectionError = (err) => {
|
const handleConnectionError = (err) => {
|
||||||
console.error("Socket connection error:", err);
|
console.error("Socket connection error:", err);
|
||||||
|
store.dispatch(setWssStatus("error"))
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDisconnect = () => {
|
const handleDisconnect = () => {
|
||||||
console.log("Socket disconnected");
|
console.log("Socket disconnected");
|
||||||
|
store.dispatch(setWssStatus("disconnected"))
|
||||||
};
|
};
|
||||||
|
|
||||||
socketInstance.on("connect", handleConnect);
|
socketInstance.on("connect", handleConnect);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.se
|
|||||||
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
||||||
import "./manage.page.styles.scss";
|
import "./manage.page.styles.scss";
|
||||||
|
import WssStatusDisplayComponent from "../../components/wss-status-display/wss-status-display.component.jsx";
|
||||||
|
|
||||||
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
||||||
|
|
||||||
@@ -604,6 +605,7 @@ export function Manage({ conflict, bodyshop }) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
|
<WssStatusDisplayComponent />
|
||||||
<div onClick={broadcastMessage}>
|
<div onClick={broadcastMessage}>
|
||||||
{`${InstanceRenderManager({
|
{`${InstanceRenderManager({
|
||||||
imex: t("titles.imexonline"),
|
imex: t("titles.imexonline"),
|
||||||
|
|||||||
@@ -67,3 +67,7 @@ export const setUpdateAvailable = (isUpdateAvailable) => ({
|
|||||||
type: ApplicationActionTypes.SET_UPDATE_AVAILABLE,
|
type: ApplicationActionTypes.SET_UPDATE_AVAILABLE,
|
||||||
payload: isUpdateAvailable
|
payload: isUpdateAvailable
|
||||||
});
|
});
|
||||||
|
export const setWssStatus = (status) => ({
|
||||||
|
type: ApplicationActionTypes.SET_WSS_STATUS,
|
||||||
|
payload: status
|
||||||
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import ApplicationActionTypes from "./application.types";
|
|||||||
const INITIAL_STATE = {
|
const INITIAL_STATE = {
|
||||||
loading: false,
|
loading: false,
|
||||||
online: true,
|
online: true,
|
||||||
|
wssStatus: "disconnected",
|
||||||
updateAvailable: false,
|
updateAvailable: false,
|
||||||
breadcrumbs: [],
|
breadcrumbs: [],
|
||||||
recentItems: [],
|
recentItems: [],
|
||||||
@@ -87,6 +88,9 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
|
|||||||
case ApplicationActionTypes.SET_PROBLEM_JOBS: {
|
case ApplicationActionTypes.SET_PROBLEM_JOBS: {
|
||||||
return { ...state, problemJobs: action.payload };
|
return { ...state, problemJobs: action.payload };
|
||||||
}
|
}
|
||||||
|
case ApplicationActionTypes.SET_WSS_STATUS: {
|
||||||
|
return { ...state, wssStatus: action.payload };
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,3 +22,4 @@ export const selectJobReadOnly = createSelector([selectApplication], (applicatio
|
|||||||
export const selectOnline = createSelector([selectApplication], (application) => application.online);
|
export const selectOnline = createSelector([selectApplication], (application) => application.online);
|
||||||
export const selectProblemJobs = createSelector([selectApplication], (application) => application.problemJobs);
|
export const selectProblemJobs = createSelector([selectApplication], (application) => application.problemJobs);
|
||||||
export const selectUpdateAvailable = createSelector([selectApplication], (application) => application.updateAvailable);
|
export const selectUpdateAvailable = createSelector([selectApplication], (application) => application.updateAvailable);
|
||||||
|
export const selectWssStatus = createSelector([selectApplication], (application) => application.wssStatus);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const ApplicationActionTypes = {
|
|||||||
SET_ONLINE_STATUS: "SET_ONLINE_STATUS",
|
SET_ONLINE_STATUS: "SET_ONLINE_STATUS",
|
||||||
INSERT_AUDIT_TRAIL: "INSERT_AUDIT_TRAIL",
|
INSERT_AUDIT_TRAIL: "INSERT_AUDIT_TRAIL",
|
||||||
SET_PROBLEM_JOBS: "SET_PROBLEM_JOBS",
|
SET_PROBLEM_JOBS: "SET_PROBLEM_JOBS",
|
||||||
SET_UPDATE_AVAILABLE: "SET_UPDATE_AVAILABLE"
|
SET_UPDATE_AVAILABLE: "SET_UPDATE_AVAILABLE",
|
||||||
|
SET_WSS_STATUS: "SET_WSS_STATUS"
|
||||||
};
|
};
|
||||||
export default ApplicationActionTypes;
|
export default ApplicationActionTypes;
|
||||||
|
|||||||
Reference in New Issue
Block a user