diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a17b9170..3e788c4b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,11 +26,10 @@ "@xterm/xterm": "5.5.0", "axios": "1.7.4", "copy-to-clipboard": "3.3.3", + "dayjs": "^1.11.13", "generate-password": "1.7.1", "jwt-decode": "4.0.0", "md5": "2.3.0", - "moment": "2.30.1", - "moment-duration-format": "2.3.2", "msgpack5": "6.0.2", "pluralize": "8.0.0", "react": "^18.2.0", @@ -105,6 +104,7 @@ "less-loader": "12.2.0", "lint-staged": "15.2.7", "mini-css-extract-plugin": "2.9.0", + "mockdate": "^3.0.5", "msw": "2.3.4", "postcss": "8.4.40", "postcss-loader": "8.1.1", @@ -7953,9 +7953,9 @@ "integrity": "sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg==" }, "node_modules/dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" }, "node_modules/debug": { "version": "4.3.4", @@ -14044,6 +14044,12 @@ "node": ">=10" } }, + "node_modules/mockdate": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/mockdate/-/mockdate-3.0.5.tgz", + "integrity": "sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==", + "dev": true + }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -14052,11 +14058,6 @@ "node": "*" } }, - "node_modules/moment-duration-format": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/moment-duration-format/-/moment-duration-format-2.3.2.tgz", - "integrity": "sha512-cBMXjSW+fjOb4tyaVHuaVE/A5TqkukDWiOfxxAjY+PEqmmBQlLwn+8OzwPiG3brouXKY5Un4pBjAeB6UToXHaQ==" - }, "node_modules/moment-timezone": { "version": "0.5.44", "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.44.tgz", diff --git a/frontend/package.json b/frontend/package.json index 2d5045bd..bb78abdb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,11 +16,10 @@ "@stripe/stripe-js": "4.1.0", "axios": "1.7.4", "copy-to-clipboard": "3.3.3", + "dayjs": "^1.11.13", "generate-password": "1.7.1", "jwt-decode": "4.0.0", "md5": "2.3.0", - "moment": "2.30.1", - "moment-duration-format": "2.3.2", "msgpack5": "6.0.2", "pluralize": "8.0.0", "react": "^18.2.0", @@ -99,6 +98,7 @@ "less-loader": "12.2.0", "lint-staged": "15.2.7", "mini-css-extract-plugin": "2.9.0", + "mockdate": "^3.0.5", "msw": "2.3.4", "postcss": "8.4.40", "postcss-loader": "8.1.1", diff --git a/frontend/src/js/actions/appActions.js b/frontend/src/js/actions/appActions.js index aa00ad35..56d8f408 100644 --- a/frontend/src/js/actions/appActions.js +++ b/frontend/src/js/actions/appActions.js @@ -11,7 +11,8 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import moment from 'moment'; +import dayjs from 'dayjs'; +import durationDayJs from 'dayjs/plugin/duration'; import Cookies from 'universal-cookie'; import GeneralApi from '../api/general-api'; @@ -50,6 +51,7 @@ import { getReleases } from './releaseActions'; import { getGlobalSettings, getRoles, getUserSettings, saveGlobalSettings, saveUserSettings, setShowStartupNotification } from './userActions'; const cookies = new Cookies(); +dayjs.extend(durationDayJs); export const commonErrorFallback = 'Please check your connection.'; export const commonErrorHandler = (err, errorContext, dispatch, fallback, mightBeAuthRelated = false) => { @@ -174,7 +176,7 @@ const interpretAppData = () => (dispatch, getState) => { const { canManageUsers } = getUserCapabilities(getState()); const { interval, intervalUnit } = getOfflineThresholdSettings(getState()); if (canManageUsers && intervalUnit && intervalUnit !== timeUnits.days) { - const duration = moment.duration(interval, intervalUnit); + const duration = dayjs.duration(interval, intervalUnit); const days = duration.asDays(); if (days < 1) { tasks.push(Promise.resolve(setTimeout(() => dispatch(setShowStartupNotification(true)), TIMEOUTS.fiveSeconds))); diff --git a/frontend/src/js/components/app.js b/frontend/src/js/components/app.js index 1a64de65..52f519c2 100644 --- a/frontend/src/js/components/app.js +++ b/frontend/src/js/components/app.js @@ -20,7 +20,7 @@ import createCache from '@emotion/cache'; import { CacheProvider } from '@emotion/react'; import { CssBaseline, GlobalStyles, ThemeProvider, createTheme, styled } from '@mui/material'; import { LocalizationProvider } from '@mui/x-date-pickers'; -import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { makeStyles } from 'tss-react/mui'; import Cookies from 'universal-cookie'; @@ -221,7 +221,7 @@ export const AppProviders = ({ basename = 'ui' }) => ( - + diff --git a/frontend/src/js/components/auditlogs/auditlogs.js b/frontend/src/js/components/auditlogs/auditlogs.js index e95641ca..16af9e77 100644 --- a/frontend/src/js/components/auditlogs/auditlogs.js +++ b/frontend/src/js/components/auditlogs/auditlogs.js @@ -17,7 +17,7 @@ import { useDispatch, useSelector } from 'react-redux'; import { Button, TextField } from '@mui/material'; import { makeStyles } from 'tss-react/mui'; -import moment from 'moment'; +import dayjs from 'dayjs'; import historyImage from '../../../assets/img/history.png'; import { getAuditLogs, getAuditLogsCsvLink, setAuditlogsState } from '../../actions/organizationActions'; @@ -181,11 +181,7 @@ export const AuditLogs = props => { const createCsvDownload = () => { setCsvLoading(true); dispatch(getAuditLogsCsvLink()).then(address => { - createDownload( - encodeURI(address), - `Mender-AuditLog-${moment(startDate).format(moment.HTML5_FMT.DATE)}-${moment(endDate).format(moment.HTML5_FMT.DATE)}.csv`, - token - ); + createDownload(encodeURI(address), `Mender-AuditLog-${dayjs(startDate).format('YYYY-MM-DD')}-${dayjs(endDate).format('YYYY-MM-DD')}.csv`, token); setCsvLoading(false); }); }; diff --git a/frontend/src/js/components/auditlogs/auditlogs.test.js b/frontend/src/js/components/auditlogs/auditlogs.test.js index 43e9b5f7..d6f172e2 100644 --- a/frontend/src/js/components/auditlogs/auditlogs.test.js +++ b/frontend/src/js/components/auditlogs/auditlogs.test.js @@ -17,7 +17,7 @@ import { MemoryRouter } from 'react-router-dom'; import { ThemeProvider, createTheme } from '@mui/material/styles'; import { LocalizationProvider } from '@mui/x-date-pickers'; -import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { prettyDOM, screen, render as testingLibRender, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; @@ -56,7 +56,7 @@ const preloadedStateNoAuditlogs = { describe('Auditlogs Component', () => { it('renders correctly', async () => { const { baseElement } = render( - + , { preloadedState: preloadedStateNoAuditlogs } @@ -73,7 +73,7 @@ describe('Auditlogs Component', () => { let store = getConfiguredStore({ preloadedState }); const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); render( - + @@ -93,7 +93,7 @@ describe('Auditlogs Component', () => { const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); const theme = createTheme(lightTheme); const ui = ( - + diff --git a/frontend/src/js/components/auditlogs/eventdetails/portforward.js b/frontend/src/js/components/auditlogs/eventdetails/portforward.js index 26979357..39134325 100644 --- a/frontend/src/js/components/auditlogs/eventdetails/portforward.js +++ b/frontend/src/js/components/auditlogs/eventdetails/portforward.js @@ -16,8 +16,8 @@ import { useDispatch, useSelector } from 'react-redux'; import { useTheme } from '@mui/material/styles'; -import moment from 'moment'; -import momentDurationFormatSetup from 'moment-duration-format'; +import dayjs from 'dayjs'; +import duration from 'dayjs/plugin/duration'; import { getDeviceById, getSessionDetails } from '../../../actions/deviceActions'; import { getAuditlogDevice, getIdAttribute, getUserCapabilities } from '../../../selectors'; @@ -25,7 +25,7 @@ import Loader from '../../common/loader'; import Time from '../../common/time'; import DeviceDetails, { DetailInformation } from './devicedetails'; -momentDurationFormatSetup(moment); +dayjs.extend(duration); export const PortForward = ({ item, onClose }) => { const theme = useTheme(); @@ -53,7 +53,7 @@ export const PortForward = ({ item, onClose }) => { 'Session ID': item.meta.session_id[0], 'Start time':