diff --git a/.github/workflows/ci-js.yml b/.github/workflows/ci-js.yml index 43145c6444..c7aa349d36 100644 --- a/.github/workflows/ci-js.yml +++ b/.github/workflows/ci-js.yml @@ -4,9 +4,11 @@ on: push: branches: - main + - opensight-ui pull_request: branches: - main + - opensight-ui jobs: testing: diff --git a/package-lock.json b/package-lock.json index 333eeb28bc..ae3a44c5fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "23.0.1-dev1", "license": "AGPL-3.0+", "dependencies": { - "@greenbone/opensight-ui-components": "^0.1.3-alpha1", + "@greenbone/opensight-ui-components": "^0.3.0", "@mantine/core": "^6.0.0", "@reduxjs/toolkit": "^2.2.5", "@sentry/react": "^8.7.0", @@ -2835,20 +2835,20 @@ "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" }, "node_modules/@greenbone/opensight-ui-components": { - "version": "0.1.3-alpha2", - "resolved": "https://registry.npmjs.org/@greenbone/opensight-ui-components/-/opensight-ui-components-0.1.3-alpha2.tgz", - "integrity": "sha512-Kn16uRoKO7pruC10FUR+Qoyw1aXmXCNZOkx+j+nE1+Y8E2YhvT0xJu9clg1mQYQHVaUXJ1DM4XOo5azWRL08vw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@greenbone/opensight-ui-components/-/opensight-ui-components-0.3.0.tgz", + "integrity": "sha512-Nf90SoaYP74I2fRA/cyx06xbdjBho7zuKP4hQkkZUJKx3uIH4QhH0NbltOvHt8a60xRgCIjVanPpDDUwhwP3MA==", "dependencies": { "@mantine/core": "^6.x.x", "@mantine/dates": "^6.x.x", "@mantine/hooks": "^6.x.x", "@mantine/notifications": "^6.x.x", - "lucide-react": "^0.376.0", + "lucide-react": "^0.390.0", "luxon": "^3.4.4", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7", - "react-i18next": "^14.1.1", + "react-i18next": "^14.1.2", "react-select": "^5.8.0", "tiny-invariant": "^1.3.3", "urlcat": "^3", @@ -2856,7 +2856,7 @@ "yup": "^1.4.0" }, "optionalDependencies": { - "@reduxjs/toolkit": "^2.2.3", + "@reduxjs/toolkit": "^2.2.5", "@swc/core-darwin-arm64": "^1.3.107" }, "peerDependencies": { @@ -2871,9 +2871,9 @@ } }, "node_modules/@greenbone/opensight-ui-components/node_modules/lucide-react": { - "version": "0.376.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.376.0.tgz", - "integrity": "sha512-g91IX3ERD6yUR1TL2dsL4BkcGygpZz/EsqjAeL/kcRQV0EApIOr/9eBfKhYOVyQIcGGuotFGjF3xKLHMEz+b7g==", + "version": "0.390.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.390.0.tgz", + "integrity": "sha512-APqbfEcVuHnZbiy3E97gYWLeBdkE4e6NbY6AuVETZDZVn/bQCHYUoHyxcUHyvRopfPOHhFUEvDyyQzHwM+S9/w==", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0" } diff --git a/package.json b/package.json index 0b2816a161..bbe4739390 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "node": ">=18.0" }, "dependencies": { - "@greenbone/opensight-ui-components": "^0.1.3-alpha1", + "@greenbone/opensight-ui-components": "^0.3.0", "@mantine/core": "^6.0.0", "@reduxjs/toolkit": "^2.2.5", "@sentry/react": "^8.7.0", diff --git a/src/web/components/sessionTimer/SessionTimer.jsx b/src/web/components/sessionTimer/SessionTimer.jsx new file mode 100644 index 0000000000..6af1f28fb0 --- /dev/null +++ b/src/web/components/sessionTimer/SessionTimer.jsx @@ -0,0 +1,54 @@ +import {useEffect, useState} from 'react'; + +import useUserSessionTimeout from 'web/utils/useUserSessionTimeout'; +import date from 'gmp/models/date'; +import {RefreshCcw} from 'lucide-react'; +import Divider from 'web/components/layout/divider'; +import {ActionIcon} from '@mantine/core'; +import useTranslation from 'web/hooks/useTranslation'; + +const SessionTimer = () => { + const [sessionTimeout, renewSession] = useUserSessionTimeout(); + const [timeLeft, setTimeLeft] = useState(''); + const [_] = useTranslation(); + + useEffect(() => { + const updateTimeLeft = () => { + if (!sessionTimeout) { + return
Session timer is currently unavailable.
; + } + const now = date(); + const duration = date.duration(sessionTimeout.diff(now)); + if (duration.asSeconds() <= 0) { + setTimeLeft('00:00'); + } else { + const formatted = + Math.floor(duration.asMinutes()) + + ':' + + ('0' + duration.seconds()).slice(-2); + setTimeLeft(formatted); + } + }; + + updateTimeLeft(); + const intervalId = setInterval(updateTimeLeft, 1000); + + return () => clearInterval(intervalId); + }, [sessionTimeout]); + + return ( + + {timeLeft} + + + + + ); +}; + +export default SessionTimer; diff --git a/src/web/components/structure/header.jsx b/src/web/components/structure/header.jsx index 8f8de43026..c56f272874 100644 --- a/src/web/components/structure/header.jsx +++ b/src/web/components/structure/header.jsx @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import React, {useCallback} from 'react'; +import {useCallback} from 'react'; import {useHistory} from 'react-router-dom'; @@ -30,6 +30,7 @@ import useGmp from 'web/utils/useGmp'; import LogoutIcon from 'web/components/icon/logouticon'; import MySettingsIcon from 'web/components/icon/mysettingsicon'; import LanguageSwitch from './languageswitch'; +import SessionTimer from '../sessionTimer/SessionTimer'; const Header = () => { const gmp = useGmp(); @@ -73,6 +74,7 @@ const Header = () => { languageSwitch={} menuPoints={menuPoints} isLoggedIn={loggedIn} + sessionTimer={} username={username} logoLink="/" /> diff --git a/src/web/utils/__tests__/useUserSessionTimeout.jsx b/src/web/utils/__tests__/useUserSessionTimeout.jsx index 9689be606f..34214a7fe9 100644 --- a/src/web/utils/__tests__/useUserSessionTimeout.jsx +++ b/src/web/utils/__tests__/useUserSessionTimeout.jsx @@ -22,16 +22,19 @@ import date from 'gmp/models/date'; import {setSessionTimeout as setSessionTimeoutAction} from 'web/store/usersettings/actions'; -import {rendererWith, fireEvent} from '../testing'; +import {rendererWith} from '../testing'; import useUserSessionTimeout from '../useUserSessionTimeout'; const TestUserSessionTimeout = () => { const [sessionTimeout, setSessionTimeout] = useUserSessionTimeout(); return ( - setSessionTimeout(date('2020-03-10'))}> + ); }; @@ -47,20 +50,4 @@ describe('useUserSessionTimeout tests', () => { expect(element).toHaveTextContent(/^10-10-19$/); }); - - test('should allow to set the users session timeout', () => { - const {render, store} = rendererWith({store: true}); - - const timeout = date('2019-10-10'); - - store.dispatch(setSessionTimeoutAction(timeout)); - - const {element} = render(); - - expect(element).toHaveTextContent(/^10-10-19$/); - - fireEvent.click(element); - - expect(element).toHaveTextContent(/^10-03-20$/); - }); }); diff --git a/src/web/utils/useUserSessionTimeout.jsx b/src/web/utils/useUserSessionTimeout.jsx index 761600e4a1..317fe4c063 100644 --- a/src/web/utils/useUserSessionTimeout.jsx +++ b/src/web/utils/useUserSessionTimeout.jsx @@ -19,13 +19,29 @@ import {useSelector, useDispatch} from 'react-redux'; import {getSessionTimeout} from 'web/store/usersettings/selectors'; import {setSessionTimeout} from 'web/store/usersettings/actions'; +import useGmp from 'web/utils/useGmp'; + +/** + * Custom hook to manage user session timeout. + * + * This hook provides the current session timeout, represented as a moment object, and a function to renew the session timeout through an API call. + * The `renewSessionAndUpdateTimeout` function makes an API call to renew the session and updates the session timeout based on the response, also represented as a moment object. + * This function does not require any parameters and will update the session timeout to the new value obtained from the API response. + * + * @returns {Array} An array containing the current `sessionTimeout` as a moment object and the `renewSessionAndUpdateTimeout` function. + */ const useUserSessionTimeout = () => { + const gmp = useGmp(); const dispatch = useDispatch(); - return [ - useSelector(getSessionTimeout), - timeout => dispatch(setSessionTimeout(timeout)), - ]; + const sessionTimeout = useSelector(getSessionTimeout); + + const renewSessionAndUpdateTimeout = async () => { + const response = await gmp.user.renewSession(); + dispatch(setSessionTimeout(response.data)); + }; + + return [sessionTimeout, renewSessionAndUpdateTimeout]; }; export default useUserSessionTimeout;