diff --git a/netmanager/src/AppRoutes.js b/netmanager/src/AppRoutes.js index f167b52f51..4d13bbcc1a 100644 --- a/netmanager/src/AppRoutes.js +++ b/netmanager/src/AppRoutes.js @@ -1,4 +1,4 @@ -import React, { Suspense, lazy } from 'react'; +import React, { Suspense, lazy, useState, useEffect } from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import PrivateRoute from './views/components/PrivateRoute/PrivateRoute'; import { useInternetConnectivityCheck, useJiraHelpDesk } from 'utils/customHooks'; @@ -10,6 +10,9 @@ import { Main as MainLayout, Minimal as MinimalLayout } from 'views/layouts/'; import { NotFound as NotFoundView } from './views/pages/NotFound'; import { LargeCircularLoader } from 'views/components/Loader/CircularLoader'; import PermissionDenied from './views/pages/PermissionDenied'; +import { logoutUser } from './redux/Join/actions'; +import { connect } from 'react-redux'; +import ConfirmDialog from './views/containers/ConfirmDialog'; // lazy imports const Landing = lazy(() => import('./views/layouts/Landing')); @@ -43,9 +46,42 @@ const Logs = lazy(() => import('./views/pages/Logs')); const ExportDownloads = lazy(() => import('./views/pages/ExportData/downloads')); const ExportData = lazy(() => import('./views/pages/ExportData')); -const AppRoutes = () => { +const AppRoutes = ({ auth, logoutUser }) => { useJiraHelpDesk(); useInternetConnectivityCheck(); + + const sessionTimeoutInSeconds = 30; + let inactivityTimer; + + const [sessionExpired, setSessionExpired] = useState(false); + + const resetInactivityTimer = () => { + clearTimeout(inactivityTimer); + inactivityTimer = setTimeout(() => { + setSessionExpired(true); + logoutUser(); + }, sessionTimeoutInSeconds* 60 * 1000); + }; + + const handleUserActivity = () => { + resetInactivityTimer(); + }; + + useEffect(() => { + if (auth.isAuthenticated) { + resetInactivityTimer(); + window.addEventListener('mousemove', handleUserActivity); + window.addEventListener('keypress', handleUserActivity); + + return () => { + clearTimeout(inactivityTimer); + window.removeEventListener('mousemove', handleUserActivity); + window.removeEventListener('keypress', handleUserActivity); + }; + } + }, [auth.isAuthenticated]); + + return (
@@ -152,9 +188,25 @@ const AppRoutes = () => { >
+ + {sessionExpired && ( + window.location.replace('/')} + title="Session Expired" + message="Your session has expired due to inactivity. Please log in again." + confirmBtnMsg="Log In" + confirm={() => setSessionExpired(false)} + error={false} + /> + )} +
); }; +const mapStateToProps = (state) => ({ + auth: state.auth +}); -export default AppRoutes; +export default connect(mapStateToProps, { logoutUser })(AppRoutes); diff --git a/netmanager/src/__tests__/TimeSession.test.js b/netmanager/src/__tests__/TimeSession.test.js new file mode 100644 index 0000000000..e3343d4835 --- /dev/null +++ b/netmanager/src/__tests__/TimeSession.test.js @@ -0,0 +1,20 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import ConfirmDialog from 'views/containers/ConfirmDialog'; + +describe('ConfirmDialog Component', () => { + it('renders without crashing', () => { + const wrapper = shallow( + {}} + title="Session Expired" + message="Your session has expired due to inactivity. Please log in again." + confirmBtnMsg="Log In" + confirm={() => {}} + error={false} + /> + ); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/netmanager/src/__tests__/__snapshots__/TimeSession.test.js.snap b/netmanager/src/__tests__/__snapshots__/TimeSession.test.js.snap new file mode 100644 index 0000000000..23f6bc99bd --- /dev/null +++ b/netmanager/src/__tests__/__snapshots__/TimeSession.test.js.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ConfirmDialog Component renders without crashing 1`] = ` + +
+
+ Session Expired +
+
+ Your session has expired due to inactivity. Please log in again. +
+
+ + Cancel + + + Log In + +
+
+
+`;