From 2958587fca5b5b31f2a2f5cf7366324fe6cbde6e Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Thu, 29 Aug 2024 11:36:56 +0200 Subject: [PATCH 1/9] add: user settings time and date format --- src/gmp/commands/users.js | 8 ++ src/gmp/locale/date.js | 47 +++++++-- .../components/date/__tests__/datetime.jsx | 96 ++++++++++++++++--- .../hooks/__tests__/useUserSessionTimeout.jsx | 11 ++- src/web/pages/login/loginpage.jsx | 68 +++++++------ src/web/pages/usersettings/dialog.jsx | 16 +++- src/web/pages/usersettings/generalpart.jsx | 6 +- .../pages/usersettings/usersettingspage.jsx | 87 +++++++++++++---- src/web/store/usersettings/reducers.js | 1 - src/web/store/usersettings/selectors.js | 1 - src/web/utils/render.jsx | 15 ++- .../utils/userSettingTimeDateFormatters.js | 40 ++++++++ 12 files changed, 310 insertions(+), 86 deletions(-) create mode 100644 src/web/utils/userSettingTimeDateFormatters.js diff --git a/src/gmp/commands/users.js b/src/gmp/commands/users.js index 9a70662bcb..f2c3107838 100644 --- a/src/gmp/commands/users.js +++ b/src/gmp/commands/users.js @@ -70,6 +70,11 @@ export const DEFAULT_FILTER_SETTINGS = { vulnerability: '17c9d269-95e7-4bfa-b1b2-bc106a2175c7', }; +const PARAM_KEYS = { + DATE: 'date_format', + TIME: 'time_format', +}; + const saveDefaultFilterSettingId = entityType => `settings_filter:${DEFAULT_FILTER_SETTINGS[entityType]}`; @@ -251,9 +256,12 @@ export class UserCommand extends EntityCommand { saveSettings(data) { log.debug('Saving settings', data); + return this.httpPost({ cmd: 'save_my_settings', text: data.timezone, + [PARAM_KEYS.DATE]: data.dateFormat, + [PARAM_KEYS.TIME]: data.timeFormat, old_password: data.oldPassword, password: data.newPassword, lang: data.userInterfaceLanguage, diff --git a/src/gmp/locale/date.js b/src/gmp/locale/date.js index 253a60b3a3..6d03596357 100644 --- a/src/gmp/locale/date.js +++ b/src/gmp/locale/date.js @@ -34,7 +34,7 @@ export const ensureDate = date => { return date; }; -export const dateFormat = (date, format, tz) => { +export const getFormattedDate = (date, format, tz) => { date = ensureDate(date); if (!isDefined(date)) { return undefined; @@ -43,14 +43,49 @@ export const dateFormat = (date, format, tz) => { if (isDefined(tz)) { date.tz(tz); } + return date.format(format); }; -export const shortDate = (date, tz) => dateFormat(date, 'L', tz); +export const dateTimeFormatOptions = { + time: {12: 'h:mm A', 24: 'H:mm'}, + date: {wmdy: 'ddd, MMM D, YYYY', wdmy: 'ddd, D MMM YYYY'}, +}; + +export const shortDate = (date, tz, userInterfaceDateFormat) => { + const formatString = dateTimeFormatOptions.date[userInterfaceDateFormat] + ? 'DD/MM/YYYY' + : 'L'; + + return getFormattedDate(date, formatString, tz); +}; + +export const longDate = ( + date, + tz, + userInterfaceTimeFormat, + userInterfaceDateFormat, +) => { + const formatString = + dateTimeFormatOptions.date[userInterfaceDateFormat] && + dateTimeFormatOptions.time[userInterfaceTimeFormat] + ? `${dateTimeFormatOptions.date[userInterfaceDateFormat]} ${dateTimeFormatOptions.time[userInterfaceTimeFormat]} z` + : 'llll'; -export const longDate = (date, tz) => dateFormat(date, 'llll', tz); + return getFormattedDate(date, formatString, tz); +}; -export const dateTimeWithTimeZone = (date, tz) => - dateFormat(date, 'llll z', tz); +export const dateTimeWithTimeZone = ( + date, + tz, + userInterfaceTimeFormat, + userInterfaceDateFormat, +) => { + const formatString = + dateTimeFormatOptions.date[userInterfaceDateFormat] && + dateTimeFormatOptions.time[userInterfaceTimeFormat] + ? `${dateTimeFormatOptions.date[userInterfaceDateFormat]} ${dateTimeFormatOptions.time[userInterfaceTimeFormat]} z` + : 'llll z'; -// vim: set ts=2 sw=2 tw=80: + return getFormattedDate(date, formatString, tz); +}; diff --git a/src/web/components/date/__tests__/datetime.jsx b/src/web/components/date/__tests__/datetime.jsx index edcf0bfaeb..55933e789b 100644 --- a/src/web/components/date/__tests__/datetime.jsx +++ b/src/web/components/date/__tests__/datetime.jsx @@ -4,7 +4,14 @@ */ /* eslint-disable no-console */ -import {describe, test, expect, testing} from '@gsa/testing'; +import { + describe, + test, + expect, + testing, + beforeAll, + afterAll, +} from '@gsa/testing'; import Date from 'gmp/models/date'; @@ -13,10 +20,35 @@ import {rendererWith} from 'web/utils/testing'; import {setTimezone} from 'web/store/usersettings/actions'; import DateTime from '../datetime'; +import {loadingActions} from 'web/store/usersettings/defaults/actions'; + +const getSetting = testing.fn().mockResolvedValue({}); + +const gmp = { + user: { + getSetting, + }, +}; describe('DateTime render tests', () => { + let originalSetItem; + + beforeAll(() => { + originalSetItem = localStorage.setItem; + localStorage.setItem = testing.fn(); + }); + + afterAll(() => { + localStorage.setItem = originalSetItem; + }); test('should render nothing if date is undefined', () => { - const {render} = rendererWith({store: true}); + const {render, store} = rendererWith({gmp, store: true}); + store.dispatch( + loadingActions.success({ + userinterfacetimeformat: {value: 12}, + userinterfacedateformat: {value: 'wdmy'}, + }), + ); const {element} = render(); @@ -25,10 +57,16 @@ describe('DateTime render tests', () => { test('should render nothing for invalid date', () => { // deactivate console.warn for test - const consolewarn = console.warn; + const consoleWarn = console.warn; console.warn = () => {}; - const {render} = rendererWith({store: true}); + const {render, store} = rendererWith({gmp, store: true}); + store.dispatch( + loadingActions.success({ + userinterfacetimeformat: {value: 12}, + userinterfacedateformat: {value: 'wdmy'}, + }), + ); const date = Date('foo'); @@ -38,12 +76,13 @@ describe('DateTime render tests', () => { expect(element).toBeNull(); - console.warn = consolewarn; + console.warn = consoleWarn; }); test('should call formatter', () => { const formatter = testing.fn().mockReturnValue('foo'); - const {render, store} = rendererWith({store: true}); + + const {render, store} = rendererWith({gmp, store: true}); const date = Date('2019-01-01T12:00:00Z'); @@ -51,6 +90,9 @@ describe('DateTime render tests', () => { store.dispatch(setTimezone('CET')); + localStorage.setItem('userInterfaceTimeFormat', 12); + localStorage.setItem('userInterfaceDateFormat', 'wdmy'); + const {baseElement} = render( , ); @@ -59,17 +101,49 @@ describe('DateTime render tests', () => { expect(baseElement).toHaveTextContent('foo'); }); - test('should render with default formatter', () => { - const {render, store} = rendererWith({store: true}); + test.each([ + [ + 'should render with default formatter', + { + userinterfacetimeformat: {value: undefined}, + userinterfacedateformat: {value: undefined}, + }, + 'Tue, Jan 1, 2019 1:00 PM CET', + ], + [ + 'should render with 24 h and WeekDay, Month, Day, Year formatter', + { + userinterfacetimeformat: {value: 24}, + userinterfacedateformat: {value: 'wmdy'}, + }, + 'Tue, Jan 1, 2019 13:00 CET', + ], + [ + 'should render with 12 h and WeekDay, Day, Month, Year formatter', + { + userinterfacetimeformat: {value: 12}, + userinterfacedateformat: {value: 'wdmy'}, + }, + 'Tue, 1 Jan 2019 1:00 PM CET', + ], + ])('%s', (_, settings, expectedText) => { + const {render, store} = rendererWith({gmp, store: true}); + + localStorage.setItem( + 'userInterfaceTimeFormat', + settings.userinterfacetimeformat.value, + ); + localStorage.setItem( + 'userInterfaceDateFormat', + settings.userinterfacedateformat.value, + ); const date = Date('2019-01-01T12:00:00Z'); - expect(date.isValid()).toEqual(true); store.dispatch(setTimezone('CET')); const {baseElement} = render(); - - expect(baseElement).toHaveTextContent('Tue, Jan 1, 2019 1:00 PM CET'); + expect(baseElement).toHaveTextContent(expectedText); }); }); diff --git a/src/web/hooks/__tests__/useUserSessionTimeout.jsx b/src/web/hooks/__tests__/useUserSessionTimeout.jsx index 75c9202378..5f4f94adbd 100644 --- a/src/web/hooks/__tests__/useUserSessionTimeout.jsx +++ b/src/web/hooks/__tests__/useUserSessionTimeout.jsx @@ -5,7 +5,7 @@ import {describe, test, expect} from '@gsa/testing'; -import {dateFormat} from 'gmp/locale/date'; +import {getFormattedDate} from 'gmp/locale/date'; import date from 'gmp/models/date'; import {setSessionTimeout as setSessionTimeoutAction} from 'web/store/usersettings/actions'; @@ -17,9 +17,12 @@ import useUserSessionTimeout from '../useUserSessionTimeout'; const TestUserSessionTimeout = () => { const [sessionTimeout, setSessionTimeout] = useUserSessionTimeout(); return ( - setSessionTimeout(date('2020-03-10'))}> - {dateFormat(sessionTimeout, 'DD-MM-YY')} - + ); }; diff --git a/src/web/pages/login/loginpage.jsx b/src/web/pages/login/loginpage.jsx index 67a6bb77ec..38d6fdc363 100644 --- a/src/web/pages/login/loginpage.jsx +++ b/src/web/pages/login/loginpage.jsx @@ -94,38 +94,46 @@ class LoginPage extends React.Component { this.login(gmp.settings.guestUsername, gmp.settings.guestPassword); } - login(username, password) { + async login(username, password) { const {gmp} = this.props; - gmp.login(username, password).then( - data => { - const {locale, timezone, sessionTimeout} = data; - - const {location, navigate} = this.props; - - this.props.setTimezone(timezone); - this.props.setLocale(locale); - this.props.setSessionTimeout(sessionTimeout); - this.props.setUsername(username); - // must be set before changing the location - this.props.setIsLoggedIn(true); - - if ( - location && - location.state && - location.state.next && - location.state.next !== location.pathname - ) { - navigate(location.state.next, {replace: true}); - } else { - navigate('/dashboard', {replace: true}); - } - }, - rej => { - log.error(rej); - this.setState({error: rej}); - }, - ); + try { + const data = await gmp.login(username, password); + + const {location, navigate} = this.props; + const {locale, timezone, sessionTimeout} = data; + + this.props.setTimezone(timezone); + this.props.setLocale(locale); + this.props.setSessionTimeout(sessionTimeout); + this.props.setUsername(username); + // must be set before changing the location + this.props.setIsLoggedIn(true); + + if (location?.state?.next && location.state.next !== location.pathname) { + navigate(location.state.next, {replace: true}); + } else { + navigate('/dashboard', {replace: true}); + } + } catch (error) { + log.error(error); + this.setState({error}); + } + + try { + const userSettings = await gmp.user.currentSettings(); + + localStorage.setItem( + 'userInterfaceTimeFormat', + userSettings.data.userinterfacetimeformat.value, + ); + localStorage.setItem( + 'userInterfaceDateFormat', + userSettings.data.userinterfacedateformat.value, + ); + } catch (error) { + log.error(error); + } } componentDidMount() { diff --git a/src/web/pages/usersettings/dialog.jsx b/src/web/pages/usersettings/dialog.jsx index eebb0c6ca2..332d8cad33 100644 --- a/src/web/pages/usersettings/dialog.jsx +++ b/src/web/pages/usersettings/dialog.jsx @@ -39,7 +39,7 @@ const FormGroupSizer = styled.div` const fieldsToValidate = ['rowsPerPage']; -let UserSettingsDialog = ({ +const UserSettingsDialogComponent = ({ alerts, credentials, filters, @@ -49,6 +49,8 @@ let UserSettingsDialog = ({ schedules, targets, timezone, + userInterfaceTimeFormat, + userInterfaceDateFormat, userInterfaceLanguage, rowsPerPage, maxRowsPerPage, @@ -103,6 +105,8 @@ let UserSettingsDialog = ({ }) => { const settings = { timezone, + userInterfaceTimeFormat, + userInterfaceDateFormat, oldPassword: '', newPassword: '', confPassword: '', @@ -193,6 +197,8 @@ let UserSettingsDialog = ({ { +const UserSettingsDialog = connect(rootState => { const entities = isDefined(rootState.entities) ? rootState.entities : []; return { entities, }; -})(UserSettingsDialog); +})(UserSettingsDialogComponent); export default compose(withGmp, withCapabilities)(UserSettingsDialog); diff --git a/src/web/pages/usersettings/generalpart.jsx b/src/web/pages/usersettings/generalpart.jsx index 69262416ae..c5a28890cc 100644 --- a/src/web/pages/usersettings/generalpart.jsx +++ b/src/web/pages/usersettings/generalpart.jsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import styled from 'styled-components'; @@ -69,6 +68,8 @@ Notification.propTypes = { const GeneralPart = ({ timezone, + userInterfaceDateFormat, + userInterfaceTimeFormat, oldPassword, newPassword, confPassword, @@ -192,13 +193,14 @@ GeneralPart.propTypes = { detailsExportFileName: PropTypes.string, errors: PropTypes.object.isRequired, listExportFileName: PropTypes.string, - maxRowsPerPage: PropTypes.number, newPassword: PropTypes.string, oldPassword: PropTypes.string, reportExportFileName: PropTypes.string, rowsPerPage: PropTypes.number, shouldWarn: PropTypes.bool.isRequired, timezone: PropTypes.string, + userInterfaceTimeFormat: PropTypes.oneOf([12, 24]), + userInterfaceDateFormat: PropTypes.oneOf(['wmdy', 'wdmy']), userInterfaceLanguage: PropTypes.string, onChange: PropTypes.func, }; diff --git a/src/web/pages/usersettings/usersettingspage.jsx b/src/web/pages/usersettings/usersettingspage.jsx index 0301996034..eadb0e5c73 100644 --- a/src/web/pages/usersettings/usersettingspage.jsx +++ b/src/web/pages/usersettings/usersettingspage.jsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import {connect} from 'react-redux'; @@ -232,23 +231,45 @@ class UserSettings extends React.Component { } } - handleSaveSettings(data) { - const {gmp} = this.props; - const {userInterfaceLanguage = BROWSER_LANGUAGE, timezone} = data; + async handleSaveSettings(data) { + try { + const {gmp} = this.props; - this.handleInteraction(); + const {userInterfaceLanguage = BROWSER_LANGUAGE, timezone} = data; - return gmp.user.saveSettings(data).then(() => { - this.closeDialog(); - this.props.setLocale( - userInterfaceLanguage === BROWSER_LANGUAGE - ? undefined - : userInterfaceLanguage, + this.handleInteraction(); + await gmp.user.saveSetting( + 'd9857b7c-1159-4193-9bc0-18fae5473a69', + data.userInterfaceDateFormat, + ); + await gmp.user.saveSetting( + '11deb7ff-550b-4950-aacf-06faeb7c61b9', + data.userInterfaceTimeFormat, ); - this.props.setTimezone(timezone); - this.loadSettings(); - }); + await gmp.user.saveSettings(data).then(() => { + this.closeDialog(); + this.props.setLocale( + userInterfaceLanguage === BROWSER_LANGUAGE + ? undefined + : userInterfaceLanguage, + ); + this.props.setTimezone(timezone); + + localStorage.setItem( + 'userInterfaceTimeFormat', + data.userInterfaceTimeFormat, + ); + localStorage.setItem( + 'userInterfaceDateFormat', + data.userInterfaceDateFormat, + ); + + this.loadSettings(); + }); + } catch (error) { + console.error(error); + } } handleValueChange(value, name) { @@ -269,6 +290,8 @@ class UserSettings extends React.Component { targets, isLoading = true, timezone, + userInterfaceDateFormat = {}, + userInterfaceTimeFormat = {}, userInterfaceLanguage = {}, rowsPerPage = {}, maxRowsPerPage = {}, @@ -359,11 +382,10 @@ class UserSettings extends React.Component { nvtFilter = hasValue(nvtFilter) ? nvtFilter : {}; certBundFilter = hasValue(certBundFilter) ? certBundFilter : {}; dfnCertFilter = hasValue(dfnCertFilter) ? dfnCertFilter : {}; - const openVasScanners = scanners.filter(openVasScannersFilter); return ( - + <> ) : ( - + <> {_('Timezone')} {timezone} + + {_('Time Format')} + + {userInterfaceTimeFormat.value}h + + + + {_('Date Format')} + {userInterfaceDateFormat.value} + + {_('Password')} ******** @@ -719,7 +752,7 @@ class UserSettings extends React.Component { )} - + )} {dialogVisible && !isLoading && ( )} - + ); } } @@ -807,6 +842,7 @@ UserSettings.propTypes = { credentials: PropTypes.array, credentialsFilter: PropTypes.object, cveFilter: PropTypes.object, + userInterfaceDateFormat: PropTypes.oneOf(['wdmy', 'wmdy']), defaultAlert: PropTypes.object, defaultEsxiCredential: PropTypes.object, defaultOpenvasScanConfig: PropTypes.object, @@ -866,6 +902,7 @@ UserSettings.propTypes = { tasksFilter: PropTypes.object, ticketsFilter: PropTypes.object, timezone: PropTypes.string, + userInterfaceTimeFormat: PropTypes.oneOf([12, 24]), tlsCertificatesFilter: PropTypes.object, userInterfaceLanguage: PropTypes.object, usersFilter: PropTypes.object, @@ -875,11 +912,21 @@ UserSettings.propTypes = { const mapStateToProps = rootState => { const userDefaultsSelector = getUserSettingsDefaults(rootState); + const userDefaultFilterSelector = getUserSettingsDefaultFilter(rootState); const userInterfaceLanguage = userDefaultsSelector.getByName( 'userinterfacelanguage', ); + + const userInterfaceTimeFormat = userDefaultsSelector.getByName( + 'userinterfacetimeformat', + ); + + const userInterfaceDateFormat = userDefaultsSelector.getByName( + 'userinterfacedateformat', + ); + const rowsPerPage = userDefaultsSelector.getByName('rowsperpage'); const detailsExportFileName = userDefaultsSelector.getByName( 'detailsexportfilename', @@ -1002,6 +1049,8 @@ const mapStateToProps = rootState => { schedules: schedulesSel.getEntities(ALL_FILTER), targets: targetsSel.getEntities(ALL_FILTER), timezone: getTimezone(rootState), + userInterfaceTimeFormat, + userInterfaceDateFormat, userInterfaceLanguage, rowsPerPage, detailsExportFileName, diff --git a/src/web/store/usersettings/reducers.js b/src/web/store/usersettings/reducers.js index 311422ea19..9110a1eebd 100644 --- a/src/web/store/usersettings/reducers.js +++ b/src/web/store/usersettings/reducers.js @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import {combineReducers} from 'web/store/utils'; import defaults from './defaults/reducers'; diff --git a/src/web/store/usersettings/selectors.js b/src/web/store/usersettings/selectors.js index 28e99689f1..215d38ef8c 100644 --- a/src/web/store/usersettings/selectors.js +++ b/src/web/store/usersettings/selectors.js @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - export const getReportComposerDefaults = rootState => { const {userSettings = {}} = rootState; const {reportComposerDefaults} = userSettings; diff --git a/src/web/utils/render.jsx b/src/web/utils/render.jsx index 35ab6f8b4e..e585c512d7 100644 --- a/src/web/utils/render.jsx +++ b/src/web/utils/render.jsx @@ -3,13 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import {format} from 'd3-format'; import React from 'react'; import {_} from 'gmp/locale/lang'; -import {dateFormat} from 'gmp/locale/date'; +import {getFormattedDate} from 'gmp/locale/date'; import {isDefined, isFunction, isObject} from 'gmp/utils/identity'; import {isEmpty, shorten, split} from 'gmp/utils/string'; @@ -525,12 +524,12 @@ export const generateFilename = ({ mTime = currentTime; } - const percentC = dateFormat(cTime, 'YYYYMMDD'); - const percentc = dateFormat(cTime, 'HHMMSS'); - const percentD = dateFormat(currentTime, 'YYYYMMDD'); - const percentt = dateFormat(currentTime, 'HHMMSS'); - const percentM = dateFormat(mTime, 'YYYYMMDD'); - const percentm = dateFormat(mTime, 'HHMMSS'); + const percentC = getFormattedDate(cTime, 'YYYYMMDD'); + const percentc = getFormattedDate(cTime, 'HHMMSS'); + const percentD = getFormattedDate(currentTime, 'YYYYMMDD'); + const percentt = getFormattedDate(currentTime, 'HHMMSS'); + const percentM = getFormattedDate(mTime, 'YYYYMMDD'); + const percentm = getFormattedDate(mTime, 'HHMMSS'); const percentN = isDefined(resourceName) ? resourceName : resourceType; const fileNameMap = { diff --git a/src/web/utils/userSettingTimeDateFormatters.js b/src/web/utils/userSettingTimeDateFormatters.js new file mode 100644 index 0000000000..cfd2d4d457 --- /dev/null +++ b/src/web/utils/userSettingTimeDateFormatters.js @@ -0,0 +1,40 @@ +/* SPDX-FileCopyrightText: 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import {longDate, shortDate, dateTimeWithTimeZone} from 'gmp/locale/date'; + +export const formattedUserSettingShortDate = (date, tz) => { + const userInterfaceDateFormat = localStorage.getItem( + 'userInterfaceTimeFormat', + ); + + return shortDate(date, tz, userInterfaceDateFormat); +}; + +export const formattedUserSettingLongDate = (date, tz) => { + const userInterfaceDateFormat = localStorage.getItem( + 'userInterfaceDateFormat', + ); + + const userInterfaceTimeFormat = localStorage.getItem( + 'userInterfaceTimeFormat', + ); + return longDate(date, tz, userInterfaceTimeFormat, userInterfaceDateFormat); +}; + +export const formattedUserSettingDateTimeWithTimeZone = (date, tz) => { + const userInterfaceDateFormat = localStorage.getItem( + 'userInterfaceDateFormat', + ); + const userInterfaceTimeFormat = localStorage.getItem( + 'userInterfaceTimeFormat', + ); + return dateTimeWithTimeZone( + date, + tz, + userInterfaceTimeFormat, + userInterfaceDateFormat, + ); +}; From 6b9e6b809881e5fe0ceeb1eed0257fccec40acd8 Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Thu, 29 Aug 2024 11:40:16 +0200 Subject: [PATCH 2/9] change dateTimeWithTimeZone --- src/web/components/chart/schedule.jsx | 10 ++++++---- src/web/components/date/datetime.jsx | 10 ++++++++-- src/web/pages/tasks/dashboard/schedulesdisplay.jsx | 4 ++-- src/web/pages/tasks/icons/scheduleicon.jsx | 8 ++++---- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/web/components/chart/schedule.jsx b/src/web/components/chart/schedule.jsx index 7422279f34..737cd1afae 100644 --- a/src/web/components/chart/schedule.jsx +++ b/src/web/components/chart/schedule.jsx @@ -10,7 +10,7 @@ import {LinearGradient} from '@visx/gradient'; import {scaleBand, scaleUtc} from 'd3-scale'; import _ from 'gmp/locale'; -import {dateTimeWithTimeZone} from 'gmp/locale/date'; +import {formattedUserSettingDateTimeWithTimeZone} from 'web/utils/userSettingTimeDateFormatters'; import date from 'gmp/models/date'; @@ -63,12 +63,14 @@ const cloneSchedule = (d, start) => { duration === 0 ? _('{{name}} Start: {{date}}', { name: d.label, - date: dateTimeWithTimeZone(start), + date: formattedUserSettingDateTimeWithTimeZone(start), }) : _('{{name}} Start: {{startdate}} End: {{enddate}}', { name: d.label, - startdate: dateTimeWithTimeZone(start), - enddate: dateTimeWithTimeZone(start.clone().add(duration, 'seconds')), + startdate: formattedUserSettingDateTimeWithTimeZone(start), + enddate: formattedUserSettingDateTimeWithTimeZone( + start.clone().add(duration, 'seconds'), + ), }); return { ...d, diff --git a/src/web/components/date/datetime.jsx b/src/web/components/date/datetime.jsx index 5f673c643b..5af2bf60c9 100644 --- a/src/web/components/date/datetime.jsx +++ b/src/web/components/date/datetime.jsx @@ -3,14 +3,19 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import {dateTimeWithTimeZone, ensureDate} from 'gmp/locale/date'; +import {ensureDate} from 'gmp/locale/date'; +import {formattedUserSettingDateTimeWithTimeZone} from 'web/utils/userSettingTimeDateFormatters'; import {isDefined, hasValue} from 'gmp/utils/identity'; import PropTypes from 'web/utils/proptypes'; import useUserTimezone from 'web/hooks/useUserTimezone'; -const DateTime = ({formatter = dateTimeWithTimeZone, timezone, date}) => { +const DateTime = ({ + formatter = formattedUserSettingDateTimeWithTimeZone, + timezone, + date, +}) => { date = ensureDate(date); const [userTimezone] = useUserTimezone(); @@ -18,6 +23,7 @@ const DateTime = ({formatter = dateTimeWithTimeZone, timezone, date}) => { if (!hasValue(timezone)) { timezone = userTimezone; } + return !isDefined(date) || !date.isValid() ? null : formatter(date, timezone); }; diff --git a/src/web/pages/tasks/dashboard/schedulesdisplay.jsx b/src/web/pages/tasks/dashboard/schedulesdisplay.jsx index 68219581e0..31d4a51bc9 100644 --- a/src/web/pages/tasks/dashboard/schedulesdisplay.jsx +++ b/src/web/pages/tasks/dashboard/schedulesdisplay.jsx @@ -4,7 +4,7 @@ */ import {_, _l} from 'gmp/locale/lang'; -import {dateTimeWithTimeZone} from 'gmp/locale/date'; +import {formattedUserSettingDateTimeWithTimeZone} from 'web/utils/userSettingTimeDateFormatters'; import {isDefined} from 'gmp/utils/identity'; @@ -43,7 +43,7 @@ const transformScheduleData = (data = [], {endDate}) => { return { label: name, duration, - nextStart: dateTimeWithTimeZone(event.nextDate), + nextStart: formattedUserSettingDateTimeWithTimeZone(event.nextDate), starts: event.getNextDates(endDate), timezone, isInfinite: isDefined(recurrence.isFinite) && !recurrence.isFinite(), diff --git a/src/web/pages/tasks/icons/scheduleicon.jsx b/src/web/pages/tasks/icons/scheduleicon.jsx index fd9b12fc3e..e0cab189eb 100644 --- a/src/web/pages/tasks/icons/scheduleicon.jsx +++ b/src/web/pages/tasks/icons/scheduleicon.jsx @@ -17,7 +17,7 @@ import ScheduleIcon from 'web/components/icon/scheduleicon'; import DetailsLink from 'web/components/link/detailslink'; -import {dateTimeWithTimeZone} from 'gmp/locale/date'; +import {formattedUserSettingDateTimeWithTimeZone} from 'web/utils/userSettingTimeDateFormatters'; import {getTimezone} from 'web/store/usersettings/selectors'; const TaskScheduleIcon = ({size, links = true, schedule, timezone}) => { @@ -47,7 +47,7 @@ const TaskScheduleIcon = ({size, links = true, schedule, timezone}) => { } else if (count === 1) { title = _('View Details of Schedule {{name}} (Next due: {{time}} Once)', { name, - time: dateTimeWithTimeZone(nextDate, timezone), + time: formattedUserSettingDateTimeWithTimeZone(nextDate, timezone), }); } else if (count > 1) { title = _( @@ -55,14 +55,14 @@ const TaskScheduleIcon = ({size, links = true, schedule, timezone}) => { '{{time}}, {{periods}} more times )', { name, - time: dateTimeWithTimeZone(nextDate, timezone), + time: formattedUserSettingDateTimeWithTimeZone(nextDate, timezone), periods: count, }, ); } else { title = _('View Details of Schedule {{name}} (Next due: {{time}})', { name, - time: dateTimeWithTimeZone(nextDate, timezone), + time: formattedUserSettingDateTimeWithTimeZone(nextDate, timezone), }); } From ee9af43cdcdb1d85ec12e766b9a16b46a4a93261 Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Thu, 29 Aug 2024 11:41:26 +0200 Subject: [PATCH 3/9] change shortDate --- .../display/created/createdtransform.jsx | 4 +-- src/web/pages/audits/detailspage.jsx | 34 +++++++++++-------- .../pages/hosts/dashboard/modifieddisplay.jsx | 4 +-- .../hosts/dashboard/modifiedhighdisplay.jsx | 4 +-- src/web/pages/reportformats/details.jsx | 5 ++- src/web/pages/reportformats/row.jsx | 7 ++-- .../reports/details/tlscertificatestable.jsx | 13 ++++--- src/web/pages/tasks/detailspage.jsx | 10 ++++-- .../tickets/dashboard/createddisplay.jsx | 4 +-- .../dashboard/modifieddisplay.jsx | 4 +-- 10 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/web/components/dashboard/display/created/createdtransform.jsx b/src/web/components/dashboard/display/created/createdtransform.jsx index 53d52cdf9b..8ddcbf8aec 100644 --- a/src/web/components/dashboard/display/created/createdtransform.jsx +++ b/src/web/components/dashboard/display/created/createdtransform.jsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import {shortDate} from 'gmp/locale/date'; +import {formattedUserSettingShortDate} from 'web/utils/userSettingTimeDateFormatters'; import {parseInt, parseDate} from 'gmp/parser'; @@ -14,7 +14,7 @@ const transformCreated = (data = {}) => { const createdDate = parseDate(value); return { x: createdDate, - label: shortDate(createdDate), + label: formattedUserSettingShortDate(createdDate), y: parseInt(count), y2: parseInt(c_count), }; diff --git a/src/web/pages/audits/detailspage.jsx b/src/web/pages/audits/detailspage.jsx index d76a7c685b..bf4577fcef 100644 --- a/src/web/pages/audits/detailspage.jsx +++ b/src/web/pages/audits/detailspage.jsx @@ -3,11 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import _ from 'gmp/locale'; -import {shortDate} from 'gmp/locale/date'; +import {formattedUserSettingShortDate} from 'web/utils/userSettingTimeDateFormatters'; import {isDefined} from 'gmp/utils/identity'; @@ -171,25 +170,30 @@ export const ToolBarIcons = ({ id={entity.current_report.id} title={_('Current Report for Audit {{- name}} from {{- date}}', { name: entity.name, - date: shortDate(entity.current_report.scan_start), + date: formattedUserSettingShortDate( + entity.current_report.scan_start, + ), })} > )} - {!isDefined(entity.current_report) && isDefined(entity.last_report) && ( - - - - )} + {!isDefined(entity.current_report) && + isDefined(entity.last_report) && ( + + + + )} { const modified = parseDate(value); return { x: modified, - label: shortDate(modified), + label: formattedUserSettingShortDate(modified), y: parseInt(count), y2: parseInt(c_count), }; diff --git a/src/web/pages/hosts/dashboard/modifiedhighdisplay.jsx b/src/web/pages/hosts/dashboard/modifiedhighdisplay.jsx index 402b47f962..6ff1d3787e 100644 --- a/src/web/pages/hosts/dashboard/modifiedhighdisplay.jsx +++ b/src/web/pages/hosts/dashboard/modifiedhighdisplay.jsx @@ -6,7 +6,7 @@ import React from 'react'; import {_, _l} from 'gmp/locale/lang'; -import {shortDate} from 'gmp/locale/date'; +import {formattedUserSettingShortDate} from 'web/utils/userSettingTimeDateFormatters'; import {parseInt, parseDate} from 'gmp/parser'; @@ -38,7 +38,7 @@ const transformModified = (data = {}) => { const modified = parseDate(value); return { x: modified, - label: shortDate(modified), + label: formattedUserSettingShortDate(modified), y: parseInt(count), y2: parseInt(c_count), }; diff --git a/src/web/pages/reportformats/details.jsx b/src/web/pages/reportformats/details.jsx index d2d4664270..859840594b 100644 --- a/src/web/pages/reportformats/details.jsx +++ b/src/web/pages/reportformats/details.jsx @@ -3,11 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import _ from 'gmp/locale'; -import {shortDate} from 'gmp/locale/date'; +import {formattedUserSettingShortDate} from 'web/utils/userSettingTimeDateFormatters'; import {isDefined} from 'gmp/utils/identity'; @@ -72,7 +71,7 @@ const ReportFormatDetails = ({entity, links = true}) => { {renderYesNo(trust.value)} {isDefined(trust.time) && ( - ({shortDate(trust.time)}) + ({formattedUserSettingShortDate(trust.time)}) )} diff --git a/src/web/pages/reportformats/row.jsx b/src/web/pages/reportformats/row.jsx index fdf57d2083..3fd51dcb05 100644 --- a/src/web/pages/reportformats/row.jsx +++ b/src/web/pages/reportformats/row.jsx @@ -3,11 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import _ from 'gmp/locale'; -import {shortDate} from 'gmp/locale/date'; +import {formattedUserSettingShortDate} from 'web/utils/userSettingTimeDateFormatters'; import EntityNameTableData from 'web/entities/entitynametabledata'; @@ -82,7 +81,9 @@ const Row = ({ {entity.content_type} {renderYesNo(entity.trust.value)} - {entity.trust.time && ({shortDate(entity.trust.time)})} + {entity.trust.time && ( + ({formattedUserSettingShortDate(entity.trust.time)}) + )} {renderYesNo(entity.isActive())} diff --git a/src/web/pages/reports/details/tlscertificatestable.jsx b/src/web/pages/reports/details/tlscertificatestable.jsx index 87d2b2d10b..62b2c9f0e4 100644 --- a/src/web/pages/reports/details/tlscertificatestable.jsx +++ b/src/web/pages/reports/details/tlscertificatestable.jsx @@ -3,13 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import styled from 'styled-components'; import {_, _l} from 'gmp/locale/lang'; -import {shortDate} from 'gmp/locale/date'; +import {formattedUserSettingShortDate} from 'web/utils/userSettingTimeDateFormatters'; import PropTypes from 'web/utils/proptypes'; @@ -123,10 +122,16 @@ const Row = ({ {serial} - + - + @@ -188,7 +190,9 @@ export const ToolBarIcons = ({ id={entity.last_report.id} title={_('Last Report for Task {{- name}} from {{- date}}', { name: entity.name, - date: shortDate(entity.last_report.scan_start), + date: formattedUserSettingShortDate( + entity.last_report.scan_start, + ), })} > diff --git a/src/web/pages/tickets/dashboard/createddisplay.jsx b/src/web/pages/tickets/dashboard/createddisplay.jsx index c79f5266ad..16bc6c96dd 100644 --- a/src/web/pages/tickets/dashboard/createddisplay.jsx +++ b/src/web/pages/tickets/dashboard/createddisplay.jsx @@ -4,7 +4,7 @@ */ import {_, _l} from 'gmp/locale/lang'; -import {shortDate} from 'gmp/locale/date'; +import {formattedUserSettingShortDate} from 'web/utils/userSettingTimeDateFormatters'; import date from 'gmp/models/date'; import {TICKETS_FILTER_FILTER} from 'gmp/models/filter'; @@ -63,7 +63,7 @@ export const TicketsCreatedDisplay = createDisplay({ }); export const TicketsCreatedTableDisplay = createDisplay({ - dataRow: row => [row.y, row.y2, shortDate(row.x)], + dataRow: row => [row.y, row.y2, formattedUserSettingShortDate(row.x)], dataTitles: [_l('Created Tickets'), _l('Total Tickets'), _l('Time')], dataTransform: transfromCreated, displayComponent: DataTableDisplay, diff --git a/src/web/pages/tlscertificates/dashboard/modifieddisplay.jsx b/src/web/pages/tlscertificates/dashboard/modifieddisplay.jsx index ac11cecfa5..40c631cd36 100644 --- a/src/web/pages/tlscertificates/dashboard/modifieddisplay.jsx +++ b/src/web/pages/tlscertificates/dashboard/modifieddisplay.jsx @@ -6,7 +6,7 @@ import React from 'react'; import {_, _l} from 'gmp/locale/lang'; -import {shortDate} from 'gmp/locale/date'; +import {formattedUserSettingShortDate} from 'web/utils/userSettingTimeDateFormatters'; import {parseInt, parseDate} from 'gmp/parser'; @@ -37,7 +37,7 @@ const transformModified = (data = {}) => { const modified = parseDate(value); return { x: modified, - label: shortDate(modified), + label: formattedUserSettingShortDate(modified), y: parseInt(count), y2: parseInt(c_count), }; From 15b8c79be30b58235d063edaf87edc0dcd32d27a Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Thu, 29 Aug 2024 11:41:48 +0200 Subject: [PATCH 4/9] change longDate --- .../pages/hosts/dashboard/vulnscoredisplay.jsx | 4 ++-- src/web/pages/notes/detailspage.jsx | 8 +++++--- .../dashboard/vulnscoredisplay.jsx | 6 +++--- src/web/pages/overrides/detailspage.jsx | 16 ++++++++++------ .../reports/dashboard/highresultsdisplay.jsx | 4 ++-- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/web/pages/hosts/dashboard/vulnscoredisplay.jsx b/src/web/pages/hosts/dashboard/vulnscoredisplay.jsx index ebe54913b9..f539b3c351 100644 --- a/src/web/pages/hosts/dashboard/vulnscoredisplay.jsx +++ b/src/web/pages/hosts/dashboard/vulnscoredisplay.jsx @@ -10,7 +10,7 @@ import {withRouter} from 'web/utils/withRouter'; import styled from 'styled-components'; import {_, _l} from 'gmp/locale/lang'; -import {longDate} from 'gmp/locale/date'; +import {formattedUserSettingLongDate} from 'web/utils/userSettingTimeDateFormatters'; import {parseFloat, parseSeverity} from 'gmp/parser'; @@ -51,7 +51,7 @@ const transformVulnScoreData = (data = {}, {severityClass}) => { const {severity} = stats; const averageSeverity = parseSeverity(severity.mean); const riskFactor = resultSeverityRiskFactor(averageSeverity); - const modifiedDate = longDate(modified); + const modifiedDate = formattedUserSettingLongDate(modified); const toolTip = ( {name}: diff --git a/src/web/pages/notes/detailspage.jsx b/src/web/pages/notes/detailspage.jsx index 18c559a7d7..beb0f2b022 100644 --- a/src/web/pages/notes/detailspage.jsx +++ b/src/web/pages/notes/detailspage.jsx @@ -3,13 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import {connect} from 'react-redux'; import _ from 'gmp/locale'; -import {longDate} from 'gmp/locale/date'; +import {formattedUserSettingLongDate} from 'web/utils/userSettingTimeDateFormatters'; import {isDefined} from 'gmp/utils/identity'; @@ -146,7 +145,10 @@ const Details = connect(rootState => ({ isDefined(entity.endTime) && ' ' + _('until {{- enddate}}', { - enddate: longDate(entity.endTime, timezone), + enddate: formattedUserSettingLongDate( + entity.endTime, + timezone, + ), })} diff --git a/src/web/pages/operatingsystems/dashboard/vulnscoredisplay.jsx b/src/web/pages/operatingsystems/dashboard/vulnscoredisplay.jsx index 96c4409643..920246e407 100644 --- a/src/web/pages/operatingsystems/dashboard/vulnscoredisplay.jsx +++ b/src/web/pages/operatingsystems/dashboard/vulnscoredisplay.jsx @@ -10,7 +10,7 @@ import {withRouter} from 'web/utils/withRouter'; import styled from 'styled-components'; import {_, _l} from 'gmp/locale/lang'; -import {longDate} from 'gmp/locale/date'; +import {formattedUserSettingLongDate} from 'web/utils/userSettingTimeDateFormatters'; import {parseFloat, parseSeverity} from 'gmp/parser'; @@ -37,7 +37,7 @@ const ToolTip = styled.div` line-height: 1.2em; `; -const transformVulnScoreData = (data = {}, {severityClass}) => { +const transformVulnScoreData = (data = {}) => { const {groups = []} = data; const tdata = groups .filter(group => { @@ -51,7 +51,7 @@ const transformVulnScoreData = (data = {}, {severityClass}) => { const {average_severity, average_severity_score} = stats; const averageSeverity = parseSeverity(average_severity.mean); const riskFactor = resultSeverityRiskFactor(averageSeverity); - const modifiedDate = longDate(modified); + const modifiedDate = formattedUserSettingLongDate(modified); const toolTip = ( {name}: diff --git a/src/web/pages/overrides/detailspage.jsx b/src/web/pages/overrides/detailspage.jsx index a081378351..5193f91237 100644 --- a/src/web/pages/overrides/detailspage.jsx +++ b/src/web/pages/overrides/detailspage.jsx @@ -3,13 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import {connect} from 'react-redux'; import _ from 'gmp/locale'; -import {longDate} from 'gmp/locale/date'; +import {formattedUserSettingLongDate} from 'web/utils/userSettingTimeDateFormatters'; import {isDefined} from 'gmp/utils/identity'; @@ -109,9 +108,11 @@ ToolBarIcons.propTypes = { onOverrideEditClick: PropTypes.func.isRequired, }; -const Details = connect(rootState => ({ - timezone: getTimezone(rootState), -}))(({entity, timezone, ...props}) => { +const Details = connect(rootState => { + return { + timezone: getTimezone(rootState), + }; +})(({entity, timezone, ...props}) => { const {nvt} = entity; return ( @@ -149,7 +150,10 @@ const Details = connect(rootState => ({ isDefined(entity.endTime) && ' ' + _('until {{- enddate}}', { - enddate: longDate(entity.endTime, timezone), + enddate: formattedUserSettingLongDate( + entity.endTime, + timezone, + ), })} diff --git a/src/web/pages/reports/dashboard/highresultsdisplay.jsx b/src/web/pages/reports/dashboard/highresultsdisplay.jsx index 79beaafcf3..4f73fb79aa 100644 --- a/src/web/pages/reports/dashboard/highresultsdisplay.jsx +++ b/src/web/pages/reports/dashboard/highresultsdisplay.jsx @@ -6,7 +6,7 @@ import React from 'react'; import {_, _l} from 'gmp/locale/lang'; -import {longDate} from 'gmp/locale/date'; +import {formattedUserSettingLongDate} from 'web/utils/userSettingTimeDateFormatters'; import {parseInt, parseFloat, parseDate} from 'gmp/parser'; @@ -33,7 +33,7 @@ const transformHighResults = (data = {}) => { return groups.map(group => { const reportDate = parseDate(group.value); return { - label: longDate(reportDate), + label: formattedUserSettingLongDate(reportDate), x: reportDate, y: parseInt(group.stats.high.max), y2: parseFloat(group.stats.high_per_host.max), From 6983d1e747cc5f4e90b0be800d0d31fe723afafc Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Fri, 30 Aug 2024 13:34:34 +0200 Subject: [PATCH 5/9] add translation and sys default --- public/locales/gsa-de.json | 5 + src/gmp/locale/__tests__/date.js | 152 ++++++++++++++++-- src/gmp/locale/date.js | 128 ++++++++++++--- src/web/pages/usersettings/generalpart.jsx | 1 + .../pages/usersettings/usersettingspage.jsx | 10 +- .../utils/userSettingTimeDateFormatters.js | 2 +- 6 files changed, 259 insertions(+), 39 deletions(-) diff --git a/public/locales/gsa-de.json b/public/locales/gsa-de.json index 89bbabef69..cfd65aa9ca 100644 --- a/public/locales/gsa-de.json +++ b/public/locales/gsa-de.json @@ -435,6 +435,7 @@ "Dashboards": "Dashboards", "Dashboards limit reached": "Dashboardgrenze erreicht", "Date": "Datum", + "Date Format": "Datumsformat", "Day": "Tag", "Debug": "Debug", "Decrease the minimum QoD in the filter settings to 30 percent to see those results.": "Die Minimum-QdE im Filter auf 30 Prozent verringern, um diese Ergebnisse zu sehen.", @@ -1538,6 +1539,7 @@ "Super (Has super access)": "Super (hat Super-Zugriff)", "Support for LDAP is not available.": "Unterstützung für LDAP ist nicht verfügbar.", "Support for RADIUS is not available.": "Unterstützung für RADIUS ist nicht verfügbar.", + "System Default": "Systemstandard", "System Logger": "System-Logger", "System Reports": "Systemberichte", "TCP": "TCP", @@ -1727,6 +1729,7 @@ "Tickets by Creation Time (Total: {{count}})": "Tickets nach Erstellungszeit (Gesamt: {{count}})", "Tickets by Status (Total: {{count}})": "Tickets nach Status (Gesamt: {{count}})", "Time": "Zeit", + "Time Format": "Zeitformat", "Timeout": "Timeout", "Timezone": "Zeitzone", "TippingPoint SMS": "TippingPoint SMS", @@ -1857,6 +1860,8 @@ "Wednesday": "Mittwoch", "Week": "Woche", "Weekly": "Wöchentlich", + "Weekday, Month, Day, Year": "Wochentag, Monat, Tag, Jahr", + "Weekday, Day, Month, Year": "Wochentag, Tag, Monat, Jahr", "When changing status to \"closed\", a \"Note for Closed\" is required.": "Wenn der Status zu \"Geschlossen\" geändert wird, ist eine \"Notiz für Geschlossen\" erforderlich.", "When changing status to \"fixed\", a \"Note for Fixed\" is required.": "Wenn der Status zu \"Behoben\" geändert wird, ist eine \"Notiz für Behoben\" erforderlich.", "When changing status to \"open\", a \"Note for Open\" is required.": "Wenn der Status zu \"Offen\" geändert wird, ist eine \"Notiz für Offen\" erforderlich.", diff --git a/src/gmp/locale/__tests__/date.js b/src/gmp/locale/__tests__/date.js index c7ab574391..a55762f249 100644 --- a/src/gmp/locale/__tests__/date.js +++ b/src/gmp/locale/__tests__/date.js @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import {describe, test, expect} from '@gsa/testing'; +import {describe, test, expect, beforeEach} from '@gsa/testing'; import date, {setLocale as locale} from '../../models/date'; @@ -55,15 +55,30 @@ describe('shortDate tests', () => { expect(shortDate(d)).toEqual('01/01/2018'); }); - test('should format string', () => { - setLocale('en'); - expect(shortDate('2018-01-01')).toEqual('01/01/2018'); + test('should format date locale', () => { + setLocale('de'); + expect( + shortDate(date('2018-11-24T15:30:00Z'), 'UTC', 'system_default'), + ).toEqual('24.11.2018'); }); - test('should format JS date', () => { - const d = new Date('2018-01-01'); - setLocale('en'); - expect(shortDate(d)).toEqual('01/01/2018'); + describe('shortDate tests', () => { + beforeEach(() => { + setLocale('en'); + }); + + test.each([ + ['2018-01-01', undefined, undefined, '01/01/2018'], + [new Date('2018-01-01'), undefined, undefined, '01/01/2018'], + [new Date('2018-11-24'), undefined, 'wmdy', '11/24/2018'], + [new Date('2018-11-24'), undefined, 'wdmy', '24/11/2018'], + [new Date('2018-11-24'), undefined, 'system_default', '11/24/2018'], + ])( + 'should format date %p with tz %p and userInterfaceDateFormat %p to %p', + (input, tz, userInterfaceDateFormat, expected) => { + expect(shortDate(input, tz, userInterfaceDateFormat)).toEqual(expected); + }, + ); }); }); @@ -83,15 +98,55 @@ describe('longDate tests', () => { expect(longDate(d)).toEqual('Mon, Jan 1, 2018 12:00 AM'); }); - test('should format string', () => { - setLocale('en'); - expect(longDate('2018-01-01')).toEqual('Mon, Jan 1, 2018 12:00 AM'); + test('should format date locale', () => { + setLocale('de'); + expect( + longDate( + date('2018-11-24T15:30:00Z'), + 'UTC', + 'system_default', + 'system_default', + ), + ).toEqual('Sa., 24. Nov. 2018 15:30'); }); - test('should format JS date', () => { - const d = new Date('2018-01-01T00:00:00'); - setLocale('en'); - expect(longDate(d)).toEqual('Mon, Jan 1, 2018 12:00 AM'); + describe('longDate tests', () => { + beforeEach(() => { + setLocale('en'); + }); + + test.each([ + ['2018-11-24', 'UTC', undefined, undefined, 'Fri, Nov 23, 2018 11:00 PM'], + [ + new Date('2018-11-23T00:00:00'), + 'UTC', + undefined, + undefined, + 'Thu, Nov 22, 2018 11:00 PM', + ], + ['2018-11-24T15:30:00Z', 'UTC', 12, 'wdmy', 'Sat, 24 Nov 2018 3:30 PM'], + ['2018-11-24T15:30:00Z', 'UTC', 24, 'wmdy', 'Sat, Nov 24, 2018 15:30'], + [ + '2018-11-24T15:30:00Z', + 'UTC', + 'system_default', + 'system_default', + 'Sat, Nov 24, 2018 3:30 PM', + ], + ])( + 'should format date %p with tz %p, userInterfaceTimeFormat %p, and userInterfaceDateFormat %p to %p', + ( + input, + tz, + userInterfaceTimeFormat, + userInterfaceDateFormat, + expected, + ) => { + expect( + longDate(input, tz, userInterfaceTimeFormat, userInterfaceDateFormat), + ).toEqual(expected); + }, + ); }); }); @@ -110,6 +165,73 @@ describe('dateTimeWithTimeZone tests', () => { const d = date('2018-01-01T00:00:00+01:00').tz('CET'); expect(dateTimeWithTimeZone(d)).toEqual('Mon, Jan 1, 2018 12:00 AM CET'); }); + + test('should format date locale', () => { + setLocale('de'); + expect( + dateTimeWithTimeZone( + date('2018-11-24T15:30:00Z'), + 'UTC', + 'system_default', + 'system_default', + ), + ).toEqual('Sa., 24. Nov. 2018 15:30 UTC'); + }); + + describe('dateTimeWithTimeZone tests', () => { + beforeEach(() => { + setLocale('en'); + }); + + test.each([ + [ + new Date('2018-11-23T00:00:00'), + 'UTC', + undefined, + undefined, + 'Thu, Nov 22, 2018 11:00 PM UTC', + ], + [ + '2018-11-24T15:30:00Z', + 'UTC', + 12, + 'wdmy', + 'Sat, 24 Nov 2018 3:30 PM UTC', + ], + [ + '2018-11-24T15:30:00Z', + 'UTC', + 24, + 'wmdy', + 'Sat, Nov 24, 2018 15:30 UTC', + ], + [ + '2018-11-24T15:30:00Z', + 'UTC', + 'system_default', + 'system_default', + 'Sat, Nov 24, 2018 3:30 PM UTC', + ], + ])( + 'should format date %p with tz %p, userInterfaceTimeFormat %p, and userInterfaceDateFormat %p to %p', + ( + input, + tz, + userInterfaceTimeFormat, + userInterfaceDateFormat, + expected, + ) => { + expect( + dateTimeWithTimeZone( + input, + tz, + userInterfaceTimeFormat, + userInterfaceDateFormat, + ), + ).toEqual(expected); + }, + ); + }); }); // vim: set ts=2 sw=2 tw=80: diff --git a/src/gmp/locale/date.js b/src/gmp/locale/date.js index 6d03596357..1f0f6a562f 100644 --- a/src/gmp/locale/date.js +++ b/src/gmp/locale/date.js @@ -13,6 +13,38 @@ import {setLocale as setMomentLocale, isDate} from 'gmp/models/date'; const log = logger.getLogger('gmp.locale.date'); +const SYSTEM_DEFAULT = 'system_default'; +const LONG_DATE = 'longDate'; +const SHORT_DATE = 'shortDate'; +const TIME = 'time'; + +export const dateTimeFormatOptions = { + [TIME]: { + options: { + 12: {format: 'h:mm A', label: '12h'}, + 24: {format: 'H:mm', label: '24h'}, + [SYSTEM_DEFAULT]: { + format: 'LT', + label: 'System Default', + }, + }, + }, + [SHORT_DATE]: { + options: { + wmdy: {format: 'MM/DD/YYYY'}, + wdmy: {format: 'DD/MM/YYYY'}, + [SYSTEM_DEFAULT]: {format: 'L'}, + }, + }, + [LONG_DATE]: { + options: { + wmdy: {format: 'ddd, MMM D, YYYY', label: 'Weekday, Month, Day, Year'}, + wdmy: {format: 'ddd, D MMM YYYY', label: 'Weekday, Day, Month, Year'}, + [SYSTEM_DEFAULT]: {format: 'llll', label: 'System Default'}, + }, + }, +}; + export const setLocale = lang => { log.debug('Setting date locale to', lang); setMomentLocale(lang); @@ -47,45 +79,99 @@ export const getFormattedDate = (date, format, tz) => { return date.format(format); }; -export const dateTimeFormatOptions = { - time: {12: 'h:mm A', 24: 'H:mm'}, - date: {wmdy: 'ddd, MMM D, YYYY', wdmy: 'ddd, D MMM YYYY'}, +/** + * Retrieves the format string based on the category and key. + * + * @param {string} category - The category of the format. + * @param {string} key - The key for the specific format. + * @returns {string|undefined} - The format string if found, otherwise undefined. + */ + +export const getFormatString = (category, key) => { + return dateTimeFormatOptions[category].options[key]?.format; }; -export const shortDate = (date, tz, userInterfaceDateFormat) => { - const formatString = dateTimeFormatOptions.date[userInterfaceDateFormat] - ? 'DD/MM/YYYY' - : 'L'; +/** + * Formats a date with a given time zone and user setting date format. + * + * @param {Date} date - The date to format. + * @param {string} tz - The time zone. + * @param {string} [userInterfaceDateFormat=SYSTEM_DEFAULT] - The user setting date format. + * @returns {string} - The formatted date string. + */ +export const shortDate = ( + date, + tz, + userInterfaceDateFormat = SYSTEM_DEFAULT, +) => { + const dateFormatString = getFormatString(SHORT_DATE, userInterfaceDateFormat); + + const formatString = + isDefined(dateFormatString) && userInterfaceDateFormat !== SYSTEM_DEFAULT + ? dateFormatString + : 'L'; return getFormattedDate(date, formatString, tz); }; +/** + * Formats a date with a given time zone and user setting formats. + * + * @param {Date} date - The date to format. + * @param {string} tz - The time zone. + * @param {string} [userInterfaceTimeFormat=SYSTEM_DEFAULT] - The user setting time format. + * @param {string} [userInterfaceDateFormat=SYSTEM_DEFAULT] - The user setting date format. + * @returns {string} - The formatted date string. + */ + export const longDate = ( date, tz, - userInterfaceTimeFormat, - userInterfaceDateFormat, + userInterfaceTimeFormat = SYSTEM_DEFAULT, + userInterfaceDateFormat = SYSTEM_DEFAULT, ) => { - const formatString = - dateTimeFormatOptions.date[userInterfaceDateFormat] && - dateTimeFormatOptions.time[userInterfaceTimeFormat] - ? `${dateTimeFormatOptions.date[userInterfaceDateFormat]} ${dateTimeFormatOptions.time[userInterfaceTimeFormat]} z` - : 'llll'; + const dateFormatString = getFormatString(LONG_DATE, userInterfaceDateFormat); + const timeFormatString = getFormatString(TIME, userInterfaceTimeFormat); + + const useDefaultFormat = + userInterfaceTimeFormat === SYSTEM_DEFAULT || + userInterfaceDateFormat === SYSTEM_DEFAULT || + (!isDefined(dateFormatString) && !isDefined(timeFormatString)); + + const formatString = useDefaultFormat + ? 'llll' + : `${dateFormatString} ${timeFormatString}`; return getFormattedDate(date, formatString, tz); }; +/** + * Formats a date with a given time zone and user setting formats. + * + * @param {Date} date - The date to format. + * @param {string} tz - The time zone. + * @param {string} [userInterfaceTimeFormat=SYSTEM_DEFAULT] - The user setting time format. + * @param {string} [userInterfaceDateFormat=SYSTEM_DEFAULT] - The user setting date format. + * @returns {string} - The formatted date string with time zone. + */ + export const dateTimeWithTimeZone = ( date, tz, - userInterfaceTimeFormat, - userInterfaceDateFormat, + userInterfaceTimeFormat = SYSTEM_DEFAULT, + userInterfaceDateFormat = SYSTEM_DEFAULT, ) => { - const formatString = - dateTimeFormatOptions.date[userInterfaceDateFormat] && - dateTimeFormatOptions.time[userInterfaceTimeFormat] - ? `${dateTimeFormatOptions.date[userInterfaceDateFormat]} ${dateTimeFormatOptions.time[userInterfaceTimeFormat]} z` - : 'llll z'; + const dateFormatString = getFormatString(LONG_DATE, userInterfaceDateFormat); + const timeFormatString = getFormatString(TIME, userInterfaceTimeFormat); + + const useDefaultFormat = + userInterfaceTimeFormat === SYSTEM_DEFAULT || + userInterfaceDateFormat === SYSTEM_DEFAULT || + (!isDefined(dateFormatString) && !isDefined(timeFormatString)); + + const formatString = useDefaultFormat + ? 'llll z' + : `${dateFormatString} ${timeFormatString} z`; return getFormattedDate(date, formatString, tz); }; diff --git a/src/web/pages/usersettings/generalpart.jsx b/src/web/pages/usersettings/generalpart.jsx index c5a28890cc..4f7a9d74ba 100644 --- a/src/web/pages/usersettings/generalpart.jsx +++ b/src/web/pages/usersettings/generalpart.jsx @@ -9,6 +9,7 @@ import styled from 'styled-components'; import _ from 'gmp/locale'; import {parseYesNo, YES_VALUE, NO_VALUE} from 'gmp/parser'; import {isDefined} from 'gmp/utils/identity'; +import {dateTimeFormatOptions} from 'gmp/locale/date'; import Checkbox from 'web/components/form/checkbox'; import FormGroup from 'web/components/form/formgroup'; diff --git a/src/web/pages/usersettings/usersettingspage.jsx b/src/web/pages/usersettings/usersettingspage.jsx index eadb0e5c73..1b75203df7 100644 --- a/src/web/pages/usersettings/usersettingspage.jsx +++ b/src/web/pages/usersettings/usersettingspage.jsx @@ -429,12 +429,18 @@ class UserSettings extends React.Component { {_('Time Format')} - {userInterfaceTimeFormat.value}h + {userInterfaceTimeFormat.value === 'system_default' + ? _('System Default') + : `${Number(userInterfaceTimeFormat.value)}h`} {_('Date Format')} - {userInterfaceDateFormat.value} + + {userInterfaceDateFormat.value === 'system_default' + ? _('System Default') + : userInterfaceDateFormat.value} + diff --git a/src/web/utils/userSettingTimeDateFormatters.js b/src/web/utils/userSettingTimeDateFormatters.js index cfd2d4d457..733728824f 100644 --- a/src/web/utils/userSettingTimeDateFormatters.js +++ b/src/web/utils/userSettingTimeDateFormatters.js @@ -7,7 +7,7 @@ import {longDate, shortDate, dateTimeWithTimeZone} from 'gmp/locale/date'; export const formattedUserSettingShortDate = (date, tz) => { const userInterfaceDateFormat = localStorage.getItem( - 'userInterfaceTimeFormat', + 'userInterfaceDateFormat', ); return shortDate(date, tz, userInterfaceDateFormat); From d0bd5046b3ba650dc9ea4bf57121b7163746b7e2 Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Fri, 30 Aug 2024 16:28:36 +0200 Subject: [PATCH 6/9] update dialog for sys default --- public/locales/gsa-de.json | 1 + src/gmp/locale/__tests__/date.js | 16 +++++++++----- src/gmp/locale/date.js | 8 +------ src/web/pages/usersettings/dialog.jsx | 14 +++++++++--- src/web/pages/usersettings/generalpart.jsx | 10 +++++---- .../pages/usersettings/usersettingspage.jsx | 22 ++++++++++++++----- 6 files changed, 47 insertions(+), 24 deletions(-) diff --git a/public/locales/gsa-de.json b/public/locales/gsa-de.json index cfd65aa9ca..3490dfa289 100644 --- a/public/locales/gsa-de.json +++ b/public/locales/gsa-de.json @@ -1798,6 +1798,7 @@ "Uploading": "Hochladen", "Urgency": "Dringlichkeit", "Use LDAPS only": "Ausschließlich LDAPS verwenden", + "Use System Default for Time and Date Format": "Systemstandard für Zeit- und Datumsformat verwenden", "Use workaround for default certificate": "Problemumgehung für Standard-Zertifikat verwenden", "User": "Benutzer", "User ID": "Benutzer-ID", diff --git a/src/gmp/locale/__tests__/date.js b/src/gmp/locale/__tests__/date.js index a55762f249..6b81640622 100644 --- a/src/gmp/locale/__tests__/date.js +++ b/src/gmp/locale/__tests__/date.js @@ -116,13 +116,19 @@ describe('longDate tests', () => { }); test.each([ - ['2018-11-24', 'UTC', undefined, undefined, 'Fri, Nov 23, 2018 11:00 PM'], + [ + '2018-11-24', + undefined, + undefined, + undefined, + 'Sat, Nov 24, 2018 12:00 AM', + ], [ new Date('2018-11-23T00:00:00'), - 'UTC', undefined, undefined, - 'Thu, Nov 22, 2018 11:00 PM', + undefined, + 'Fri, Nov 23, 2018 12:00 AM', ], ['2018-11-24T15:30:00Z', 'UTC', 12, 'wdmy', 'Sat, 24 Nov 2018 3:30 PM'], ['2018-11-24T15:30:00Z', 'UTC', 24, 'wmdy', 'Sat, Nov 24, 2018 15:30'], @@ -186,10 +192,10 @@ describe('dateTimeWithTimeZone tests', () => { test.each([ [ new Date('2018-11-23T00:00:00'), - 'UTC', undefined, undefined, - 'Thu, Nov 22, 2018 11:00 PM UTC', + undefined, + 'Fri, Nov 23, 2018 12:00 AM ', ], [ '2018-11-24T15:30:00Z', diff --git a/src/gmp/locale/date.js b/src/gmp/locale/date.js index 1f0f6a562f..bb6700e162 100644 --- a/src/gmp/locale/date.js +++ b/src/gmp/locale/date.js @@ -13,7 +13,7 @@ import {setLocale as setMomentLocale, isDate} from 'gmp/models/date'; const log = logger.getLogger('gmp.locale.date'); -const SYSTEM_DEFAULT = 'system_default'; +export const SYSTEM_DEFAULT = 'system_default'; const LONG_DATE = 'longDate'; const SHORT_DATE = 'shortDate'; const TIME = 'time'; @@ -23,24 +23,18 @@ export const dateTimeFormatOptions = { options: { 12: {format: 'h:mm A', label: '12h'}, 24: {format: 'H:mm', label: '24h'}, - [SYSTEM_DEFAULT]: { - format: 'LT', - label: 'System Default', - }, }, }, [SHORT_DATE]: { options: { wmdy: {format: 'MM/DD/YYYY'}, wdmy: {format: 'DD/MM/YYYY'}, - [SYSTEM_DEFAULT]: {format: 'L'}, }, }, [LONG_DATE]: { options: { wmdy: {format: 'ddd, MMM D, YYYY', label: 'Weekday, Month, Day, Year'}, wdmy: {format: 'ddd, D MMM YYYY', label: 'Weekday, Day, Month, Year'}, - [SYSTEM_DEFAULT]: {format: 'llll', label: 'System Default'}, }, }, }; diff --git a/src/web/pages/usersettings/dialog.jsx b/src/web/pages/usersettings/dialog.jsx index 332d8cad33..e4b8e8dd85 100644 --- a/src/web/pages/usersettings/dialog.jsx +++ b/src/web/pages/usersettings/dialog.jsx @@ -12,7 +12,9 @@ import {connect} from 'react-redux'; import _ from 'gmp/locale'; import {isDefined} from 'gmp/utils/identity'; -import {parseFloat, parseYesNo} from 'gmp/parser'; +import {parseFloat, parseYesNo, YES_VALUE, NO_VALUE} from 'gmp/parser'; + +import {SYSTEM_DEFAULT} from 'gmp/locale/date'; import SaveDialog from 'web/components/dialog/savedialog'; @@ -51,6 +53,7 @@ const UserSettingsDialogComponent = ({ timezone, userInterfaceTimeFormat, userInterfaceDateFormat, + isUserInterfaceTimeDateDefault, userInterfaceLanguage, rowsPerPage, maxRowsPerPage, @@ -107,6 +110,7 @@ const UserSettingsDialogComponent = ({ timezone, userInterfaceTimeFormat, userInterfaceDateFormat, + isUserInterfaceTimeDateDefault, oldPassword: '', newPassword: '', confPassword: '', @@ -199,6 +203,9 @@ const UserSettingsDialogComponent = ({ timezone={values.timezone} userInterfaceTimeFormat={values.userInterfaceTimeFormat} userInterfaceDateFormat={values.userInterfaceDateFormat} + isUserInterfaceTimeDateDefault={ + values.isUserInterfaceTimeDateDefault + } oldPassword={values.oldPassword} newPassword={values.newPassword} confPassword={values.confPassword} @@ -307,7 +314,7 @@ UserSettingsDialogComponent.propTypes = { credentials: PropTypes.array, credentialsFilter: PropTypes.string, cveFilter: PropTypes.string, - userInterfaceDateFormat: PropTypes.oneOf(['wmdy', 'wdmy']), + userInterfaceDateFormat: PropTypes.oneOf(['wmdy', 'wdmy', SYSTEM_DEFAULT]), defaultAlert: PropTypes.string, defaultEsxiCredential: PropTypes.string, defaultOpenvasScanConfig: PropTypes.string, @@ -326,6 +333,7 @@ UserSettingsDialogComponent.propTypes = { filtersFilter: PropTypes.string, groupsFilter: PropTypes.string, hostsFilter: PropTypes.string, + isUserInterfaceTimeDateDefault: PropTypes.oneOfType([YES_VALUE, NO_VALUE]), listExportFileName: PropTypes.string, maxRowsPerPage: PropTypes.number, notesFilter: PropTypes.string, @@ -352,7 +360,7 @@ UserSettingsDialogComponent.propTypes = { tasksFilter: PropTypes.string, ticketsFilter: PropTypes.string, timezone: PropTypes.string, - userInterfaceTimeFormat: PropTypes.oneOf([12, 24]), + userInterfaceTimeFormat: PropTypes.oneOf([12, 24, SYSTEM_DEFAULT]), tlsCertificatesFilter: PropTypes.string, userInterfaceLanguage: PropTypes.string, usersFilter: PropTypes.string, diff --git a/src/web/pages/usersettings/generalpart.jsx b/src/web/pages/usersettings/generalpart.jsx index 4f7a9d74ba..b1963fde36 100644 --- a/src/web/pages/usersettings/generalpart.jsx +++ b/src/web/pages/usersettings/generalpart.jsx @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import React from 'react'; +import {useState} from 'react'; import styled from 'styled-components'; import _ from 'gmp/locale'; import {parseYesNo, YES_VALUE, NO_VALUE} from 'gmp/parser'; import {isDefined} from 'gmp/utils/identity'; -import {dateTimeFormatOptions} from 'gmp/locale/date'; +import {dateTimeFormatOptions, SYSTEM_DEFAULT} from 'gmp/locale/date'; import Checkbox from 'web/components/form/checkbox'; import FormGroup from 'web/components/form/formgroup'; @@ -71,6 +71,7 @@ const GeneralPart = ({ timezone, userInterfaceDateFormat, userInterfaceTimeFormat, + isUserInterfaceTimeDateDefault, oldPassword, newPassword, confPassword, @@ -200,8 +201,9 @@ GeneralPart.propTypes = { rowsPerPage: PropTypes.number, shouldWarn: PropTypes.bool.isRequired, timezone: PropTypes.string, - userInterfaceTimeFormat: PropTypes.oneOf([12, 24]), - userInterfaceDateFormat: PropTypes.oneOf(['wmdy', 'wdmy']), + userInterfaceTimeFormat: PropTypes.oneOf([12, 24, SYSTEM_DEFAULT]), + userInterfaceDateFormat: PropTypes.oneOf(['wmdy', 'wdmy', SYSTEM_DEFAULT]), + isUserInterfaceTimeDateDefault: PropTypes.oneOfType([YES_VALUE, NO_VALUE]), userInterfaceLanguage: PropTypes.string, onChange: PropTypes.func, }; diff --git a/src/web/pages/usersettings/usersettingspage.jsx b/src/web/pages/usersettings/usersettingspage.jsx index 1b75203df7..cbf648bef2 100644 --- a/src/web/pages/usersettings/usersettingspage.jsx +++ b/src/web/pages/usersettings/usersettingspage.jsx @@ -11,8 +11,9 @@ import _ from 'gmp/locale'; import {ALL_FILTER} from 'gmp/models/filter'; import {filterEmptyScanConfig} from 'gmp/models/scanconfig'; import {openVasScannersFilter} from 'gmp/models/scanner'; +import {SYSTEM_DEFAULT} from 'gmp/locale/date'; -import {YES_VALUE, parseYesNo} from 'gmp/parser'; +import {YES_VALUE, NO_VALUE, parseYesNo} from 'gmp/parser'; import {hasValue, isDefined} from 'gmp/utils/identity'; @@ -292,6 +293,13 @@ class UserSettings extends React.Component { timezone, userInterfaceDateFormat = {}, userInterfaceTimeFormat = {}, + isUserInterfaceTimeDateDefault = { + value: + userInterfaceTimeFormat.value === SYSTEM_DEFAULT && + userInterfaceDateFormat.value === SYSTEM_DEFAULT + ? YES_VALUE + : NO_VALUE, + }, userInterfaceLanguage = {}, rowsPerPage = {}, maxRowsPerPage = {}, @@ -429,7 +437,7 @@ class UserSettings extends React.Component { {_('Time Format')} - {userInterfaceTimeFormat.value === 'system_default' + {userInterfaceTimeFormat.value === SYSTEM_DEFAULT ? _('System Default') : `${Number(userInterfaceTimeFormat.value)}h`} @@ -437,7 +445,7 @@ class UserSettings extends React.Component { {_('Date Format')} - {userInterfaceDateFormat.value === 'system_default' + {userInterfaceDateFormat.value === SYSTEM_DEFAULT ? _('System Default') : userInterfaceDateFormat.value} @@ -773,6 +781,9 @@ class UserSettings extends React.Component { timezone={timezone} userInterfaceTimeFormat={userInterfaceTimeFormat.value} userInterfaceDateFormat={userInterfaceDateFormat.value} + isUserInterfaceTimeDateDefault={ + isUserInterfaceTimeDateDefault.value + } userInterfaceLanguage={userInterfaceLanguage.value} rowsPerPage={rowsPerPage.value} maxRowsPerPage={maxRowsPerPage.value} @@ -848,7 +859,7 @@ UserSettings.propTypes = { credentials: PropTypes.array, credentialsFilter: PropTypes.object, cveFilter: PropTypes.object, - userInterfaceDateFormat: PropTypes.oneOf(['wdmy', 'wmdy']), + userInterfaceDateFormat: PropTypes.oneOf(['wdmy', 'wmdy', SYSTEM_DEFAULT]), defaultAlert: PropTypes.object, defaultEsxiCredential: PropTypes.object, defaultOpenvasScanConfig: PropTypes.object, @@ -866,6 +877,7 @@ UserSettings.propTypes = { dynamicSeverity: PropTypes.object, filters: PropTypes.array, filtersFilter: PropTypes.object, + isUserInterfaceTimeDateDefault: PropTypes.oneOfType([YES_VALUE, NO_VALUE]), gmp: PropTypes.gmp.isRequired, groupsFilter: PropTypes.object, hostsFilter: PropTypes.object, @@ -908,7 +920,7 @@ UserSettings.propTypes = { tasksFilter: PropTypes.object, ticketsFilter: PropTypes.object, timezone: PropTypes.string, - userInterfaceTimeFormat: PropTypes.oneOf([12, 24]), + userInterfaceTimeFormat: PropTypes.oneOf([12, 24, SYSTEM_DEFAULT]), tlsCertificatesFilter: PropTypes.object, userInterfaceLanguage: PropTypes.object, usersFilter: PropTypes.object, From 34a6530a4153f121b1a208aeeed7a9ac4dcb0f9d Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Mon, 2 Sep 2024 18:08:51 +0200 Subject: [PATCH 7/9] address comments --- src/gmp/commands/users.js | 4 ++-- src/web/pages/overrides/detailspage.jsx | 8 +++----- src/web/pages/usersettings/usersettingspage.jsx | 10 ---------- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/gmp/commands/users.js b/src/gmp/commands/users.js index f2c3107838..3062df03f0 100644 --- a/src/gmp/commands/users.js +++ b/src/gmp/commands/users.js @@ -260,8 +260,8 @@ export class UserCommand extends EntityCommand { return this.httpPost({ cmd: 'save_my_settings', text: data.timezone, - [PARAM_KEYS.DATE]: data.dateFormat, - [PARAM_KEYS.TIME]: data.timeFormat, + [PARAM_KEYS.DATE]: data.userInterfaceDateFormat, + [PARAM_KEYS.TIME]: data.userInterfaceTimeFormat, old_password: data.oldPassword, password: data.newPassword, lang: data.userInterfaceLanguage, diff --git a/src/web/pages/overrides/detailspage.jsx b/src/web/pages/overrides/detailspage.jsx index 5193f91237..aa47d15b18 100644 --- a/src/web/pages/overrides/detailspage.jsx +++ b/src/web/pages/overrides/detailspage.jsx @@ -108,11 +108,9 @@ ToolBarIcons.propTypes = { onOverrideEditClick: PropTypes.func.isRequired, }; -const Details = connect(rootState => { - return { - timezone: getTimezone(rootState), - }; -})(({entity, timezone, ...props}) => { +const Details = connect(rootState => ({ + timezone: getTimezone(rootState), +}))(({entity, timezone, ...props}) => { const {nvt} = entity; return ( diff --git a/src/web/pages/usersettings/usersettingspage.jsx b/src/web/pages/usersettings/usersettingspage.jsx index cbf648bef2..a599ae487e 100644 --- a/src/web/pages/usersettings/usersettingspage.jsx +++ b/src/web/pages/usersettings/usersettingspage.jsx @@ -238,16 +238,6 @@ class UserSettings extends React.Component { const {userInterfaceLanguage = BROWSER_LANGUAGE, timezone} = data; - this.handleInteraction(); - await gmp.user.saveSetting( - 'd9857b7c-1159-4193-9bc0-18fae5473a69', - data.userInterfaceDateFormat, - ); - await gmp.user.saveSetting( - '11deb7ff-550b-4950-aacf-06faeb7c61b9', - data.userInterfaceTimeFormat, - ); - await gmp.user.saveSettings(data).then(() => { this.closeDialog(); this.props.setLocale( From 0b99ae94b88ea4585e6d11bda19b224a3ff82741 Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Fri, 6 Sep 2024 14:32:11 +0200 Subject: [PATCH 8/9] Update generalpart and usermenu --- src/web/components/menu/usermenu.jsx | 7 +- src/web/pages/usersettings/generalpart.jsx | 90 +++++++++++++++++++++- 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/src/web/components/menu/usermenu.jsx b/src/web/components/menu/usermenu.jsx index d4b6623c52..0ff47d4d16 100644 --- a/src/web/components/menu/usermenu.jsx +++ b/src/web/components/menu/usermenu.jsx @@ -10,7 +10,7 @@ import {useNavigate} from 'react-router-dom'; import styled, {keyframes} from 'styled-components'; import _ from 'gmp/locale'; -import {dateTimeWithTimeZone} from 'gmp/locale/date'; +import {formattedUserSettingDateTimeWithTimeZone} from 'web/utils/userSettingTimeDateFormatters'; import LogoutIcon from 'web/components/icon/logouticon'; import MySettingsIcon from 'web/components/icon/mysettingsicon'; @@ -151,7 +151,10 @@ const UserMenuContainer = () => { {_('Session timeout: {{date}}', { - date: dateTimeWithTimeZone(sessionTimeout, userTimezone), + date: formattedUserSettingDateTimeWithTimeZone( + sessionTimeout, + userTimezone, + ), })} { + const [prevUserInterfaceTimeFormat, setPrevUserInterfaceTimeFormat] = + useState(undefined); + const [prevUserInterfaceDateFormat, setPrevUserInterfaceDateFormat] = + useState(undefined); + + const getSelectItems = category => { + return Object.entries(dateTimeFormatOptions[category].options).map( + ([value, {label}]) => ({ + value: isNaN(value) ? value : Number(value), + label: _(label), + }), + ); + }; + + const handleSysDefaultChange = event => { + const isSystemDefault = parseYesNo(event); + + const defaultTimeFormat = 24; + const defaultDateFormat = 'wdmy'; + + const currentUserInterfaceTimeFormat = + userInterfaceTimeFormat || defaultTimeFormat; + const currentUserInterfaceDateFormat = + userInterfaceDateFormat || defaultDateFormat; + + if (!isSystemDefault) { + onChange( + prevUserInterfaceTimeFormat || defaultTimeFormat, + 'userInterfaceTimeFormat', + ); + onChange( + prevUserInterfaceDateFormat || defaultDateFormat, + 'userInterfaceDateFormat', + ); + } else { + setPrevUserInterfaceTimeFormat(currentUserInterfaceTimeFormat); + setPrevUserInterfaceDateFormat(currentUserInterfaceDateFormat); + + onChange(SYSTEM_DEFAULT, 'userInterfaceTimeFormat'); + onChange(SYSTEM_DEFAULT, 'userInterfaceDateFormat'); + } + + onChange(isSystemDefault, 'isUserInterfaceTimeDateDefault'); + }; return ( - + <> + + + + + + + + @@ -185,7 +271,7 @@ const GeneralPart = ({ onChange={onChange} /> - + ); }; From 5c332dfa929d1ee0b160f2fee4e1dca40f11c5cf Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Fri, 6 Sep 2024 14:56:21 +0200 Subject: [PATCH 9/9] fix linting --- src/web/pages/usersettings/generalpart.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/web/pages/usersettings/generalpart.jsx b/src/web/pages/usersettings/generalpart.jsx index 102d0a69b2..e8466e8eee 100644 --- a/src/web/pages/usersettings/generalpart.jsx +++ b/src/web/pages/usersettings/generalpart.jsx @@ -77,7 +77,6 @@ const GeneralPart = ({ confPassword, userInterfaceLanguage, rowsPerPage, - maxRowsPerPage, detailsExportFileName, listExportFileName, reportExportFileName,