diff --git a/client/src/components/production-board-kanban/production-board-kanban.container.jsx b/client/src/components/production-board-kanban/production-board-kanban.container.jsx index dd5d73d1e..643204469 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.container.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.container.jsx @@ -47,7 +47,7 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionTyp 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( 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 socket.on("production-job-updated", handleJobUpdates); - + socket.on("reconnect", handleReconnect); // Clean up on unmount or when dependencies change return () => { socket.off("production-job-updated", handleJobUpdates); + socket.off("reconnect", handleReconnect); }; - }, [subscriptionEnabled, socket, bodyshop, data, client]); + }, [subscriptionEnabled, socket, bodyshop, data, client, refetch]); const filteredAssociationSettings = useMemo(() => { return associationSettings?.associations[0] || null; diff --git a/client/src/components/production-list-table/production-list-table.container.jsx b/client/src/components/production-list-table/production-list-table.container.jsx index 3cb9d8b5e..fedcf3ff5 100644 --- a/client/src/components/production-list-table/production-list-table.container.jsx +++ b/client/src/components/production-list-table/production-list-table.container.jsx @@ -27,7 +27,7 @@ export default function ProductionListTableContainer({ bodyshop, subscriptionTyp }); // Determine if subscription is enabled - const subscriptionEnabled = Websocket_Production?.treatment === "on"; + const subscriptionEnabled = Websocket_Production?.treatment === "off"; // Use GraphQL query 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 socket.on("production-job-updated", handleJobUpdates); + socket.on("reconnect", handleReconnect); // Clean up on unmount or when dependencies change return () => { 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 const getUpdatedJobData = async (jobId) => { diff --git a/client/src/components/wss-status-display/wss-status-display.component.jsx b/client/src/components/wss-status-display/wss-status-display.component.jsx new file mode 100644 index 000000000..40ff0e42a --- /dev/null +++ b/client/src/components/wss-status-display/wss-status-display.component.jsx @@ -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 ; +} diff --git a/client/src/contexts/SocketIO/useSocket.js b/client/src/contexts/SocketIO/useSocket.js index e6b78748c..11577c906 100644 --- a/client/src/contexts/SocketIO/useSocket.js +++ b/client/src/contexts/SocketIO/useSocket.js @@ -1,7 +1,8 @@ import { useEffect, useState, useRef } from "react"; import SocketIO from "socket.io-client"; import { auth } from "../../firebase/firebase.utils"; - +import { store } from "../../redux/store"; +import { setWssStatus } from "../../redux/application/application.actions"; const useSocket = (bodyshop) => { const socketRef = useRef(null); const [clientId, setClientId] = useState(null); @@ -38,18 +39,22 @@ const useSocket = (bodyshop) => { console.log("Socket connected:", socketInstance.id); socketInstance.emit("join-bodyshop-room", bodyshop.id); setClientId(socketInstance.id); + store.dispatch(setWssStatus("connected")) }; const handleReconnect = (attempt) => { console.log(`Socket reconnected after ${attempt} attempts`); + store.dispatch(setWssStatus("connected")) }; const handleConnectionError = (err) => { console.error("Socket connection error:", err); + store.dispatch(setWssStatus("error")) }; const handleDisconnect = () => { console.log("Socket disconnected"); + store.dispatch(setWssStatus("disconnected")) }; socketInstance.on("connect", handleConnect); diff --git a/client/src/pages/manage/manage.page.component.jsx b/client/src/pages/manage/manage.page.component.jsx index e66f47398..46d66016f 100644 --- a/client/src/pages/manage/manage.page.component.jsx +++ b/client/src/pages/manage/manage.page.component.jsx @@ -25,6 +25,7 @@ import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.se import UpdateAlert from "../../components/update-alert/update-alert.component"; import InstanceRenderManager from "../../utils/instanceRenderMgr.js"; import "./manage.page.styles.scss"; +import WssStatusDisplayComponent from "../../components/wss-status-display/wss-status-display.component.jsx"; const JobsPage = lazy(() => import("../jobs/jobs.page")); @@ -604,6 +605,7 @@ export function Manage({ conflict, bodyshop }) { }} >
+
{`${InstanceRenderManager({ imex: t("titles.imexonline"), diff --git a/client/src/redux/application/application.actions.js b/client/src/redux/application/application.actions.js index 7c5485ac5..c8246022b 100644 --- a/client/src/redux/application/application.actions.js +++ b/client/src/redux/application/application.actions.js @@ -67,3 +67,7 @@ export const setUpdateAvailable = (isUpdateAvailable) => ({ type: ApplicationActionTypes.SET_UPDATE_AVAILABLE, payload: isUpdateAvailable }); +export const setWssStatus = (status) => ({ + type: ApplicationActionTypes.SET_WSS_STATUS, + payload: status +}); diff --git a/client/src/redux/application/application.reducer.js b/client/src/redux/application/application.reducer.js index 421403f19..21878e52a 100644 --- a/client/src/redux/application/application.reducer.js +++ b/client/src/redux/application/application.reducer.js @@ -3,6 +3,7 @@ import ApplicationActionTypes from "./application.types"; const INITIAL_STATE = { loading: false, online: true, + wssStatus: "disconnected", updateAvailable: false, breadcrumbs: [], recentItems: [], @@ -87,6 +88,9 @@ const applicationReducer = (state = INITIAL_STATE, action) => { case ApplicationActionTypes.SET_PROBLEM_JOBS: { return { ...state, problemJobs: action.payload }; } + case ApplicationActionTypes.SET_WSS_STATUS: { + return { ...state, wssStatus: action.payload }; + } default: return state; } diff --git a/client/src/redux/application/application.selectors.js b/client/src/redux/application/application.selectors.js index d81699522..a4f434cfe 100644 --- a/client/src/redux/application/application.selectors.js +++ b/client/src/redux/application/application.selectors.js @@ -22,3 +22,4 @@ export const selectJobReadOnly = createSelector([selectApplication], (applicatio export const selectOnline = createSelector([selectApplication], (application) => application.online); export const selectProblemJobs = createSelector([selectApplication], (application) => application.problemJobs); export const selectUpdateAvailable = createSelector([selectApplication], (application) => application.updateAvailable); +export const selectWssStatus = createSelector([selectApplication], (application) => application.wssStatus); diff --git a/client/src/redux/application/application.types.js b/client/src/redux/application/application.types.js index 9b95dd6ee..1672cda0b 100644 --- a/client/src/redux/application/application.types.js +++ b/client/src/redux/application/application.types.js @@ -12,6 +12,7 @@ const ApplicationActionTypes = { SET_ONLINE_STATUS: "SET_ONLINE_STATUS", INSERT_AUDIT_TRAIL: "INSERT_AUDIT_TRAIL", 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;