diff --git a/graylog2-web-interface/package.json b/graylog2-web-interface/package.json index af5c94e73957..601bb5591725 100644 --- a/graylog2-web-interface/package.json +++ b/graylog2-web-interface/package.json @@ -74,6 +74,7 @@ "md5": "^2.2.1", "moment": "^2.24.0", "moment-duration-format": "2.3.2", + "moment-precise-range-plugin": "^1.3.0", "mousetrap": "^1.6.5", "numeral": "^2.0.6", "opensans-npm-webfont": "1.0.0", @@ -121,6 +122,7 @@ "@types/chroma-js": "^2.1.3", "@types/express": "^4.17.11", "@types/lodash": "^4.14.165", + "@types/moment-duration-format": "^2.2.2", "@types/plotly.js": "1.54.8", "@types/react-beautiful-dnd": "^13.0.0", "@types/react-plotly.js": "^2.2.4", diff --git a/graylog2-web-interface/src/views/Constants.ts b/graylog2-web-interface/src/views/Constants.ts index f2e4ca81da39..b24c1667a587 100644 --- a/graylog2-web-interface/src/views/Constants.ts +++ b/graylog2-web-interface/src/views/Constants.ts @@ -69,9 +69,6 @@ export const RELATIVE_RANGE_TYPES = [ }, { type: 'days', label: 'Days', - }, { - type: 'weeks', - label: 'Weeks', }, ] as const; diff --git a/graylog2-web-interface/src/views/components/searchbar/date-time-picker/TimeRangeLivePreview.tsx b/graylog2-web-interface/src/views/components/searchbar/date-time-picker/TimeRangeLivePreview.tsx index f1cbad9ccf4b..93ada71f33d2 100644 --- a/graylog2-web-interface/src/views/components/searchbar/date-time-picker/TimeRangeLivePreview.tsx +++ b/graylog2-web-interface/src/views/components/searchbar/date-time-picker/TimeRangeLivePreview.tsx @@ -19,6 +19,9 @@ import { useEffect, useState } from 'react'; import styled, { css } from 'styled-components'; import PropTypes from 'prop-types'; import { useFormikContext } from 'formik'; +import moment from 'moment'; +import 'moment-duration-format'; +import 'moment-precise-range-plugin'; import { isTypeRelative, isTypeRelativeWithEnd, isTypeRelativeWithStartOnly } from 'views/typeGuards/timeRange'; import type { TimeRange, NoTimeRangeOverride } from 'views/logic/queries/Query'; @@ -68,10 +71,17 @@ const MiddleIcon = styled.span(({ theme }) => css` padding: 0 15px; `); -const readableRange = (timerange: TimeRange, fieldName: 'range' | 'from' | 'to', placeholder = 'All Time') => { - return !timerange[fieldName] ? placeholder : DateTime.now() - .subtract(timerange[fieldName] * 1000) - .fromNow(); +const readableRange = (timerange: TimeRange, fieldName: 'range' | 'from' | 'to', placeholder = 'All Time'): string => { + const rangeAsSeconds = timerange?.[fieldName]; + + if (!rangeAsSeconds) { + return placeholder; + } + + const dateAgo = moment().subtract(rangeAsSeconds, 'seconds'); + const rangeTimespan = moment.preciseDiff(moment(), dateAgo); + + return `${rangeTimespan} ago`; }; const dateOutput = (timerange: TimeRange | NoTimeRangeOverride) => { diff --git a/graylog2-web-interface/src/views/components/searchbar/date-time-picker/index.d.ts b/graylog2-web-interface/src/views/components/searchbar/date-time-picker/index.d.ts new file mode 100644 index 000000000000..86c80a911af1 --- /dev/null +++ b/graylog2-web-interface/src/views/components/searchbar/date-time-picker/index.d.ts @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ + +// Type definitions for moment-precise-range 0.2 +// Original: +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/bc1eb3b28e41b2ed54865aecf134eae6c0b23add/types/moment-precise-range-plugin/index.d.ts +// Project: https://github.com/codebox/moment-precise-range +// Definitions by: Mitchell Grice +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +import moment = require('moment'); + +export = moment; + +declare module 'moment' { + interface PreciseRangeValueObject { + years: number; + months: number; + days: number; + hours: number; + minutes: number; + seconds: number; + firstDateWasLater: boolean; + } + + interface Moment { + preciseDiff(d2: Moment, returnValueObject?: false): string; + preciseDiff(d2: Moment, returnValueObject: true): PreciseRangeValueObject; + } + + function preciseDiff(d1: Moment, d2: Moment, returnValueObject?: false): string; + function preciseDiff(d1: Moment, d2: Moment, returnValueObject: true): PreciseRangeValueObject; +} diff --git a/graylog2-web-interface/yarn.lock b/graylog2-web-interface/yarn.lock index b82f86f6f95a..8e96e35ebedf 100644 --- a/graylog2-web-interface/yarn.lock +++ b/graylog2-web-interface/yarn.lock @@ -2250,6 +2250,13 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= +"@types/moment-duration-format@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@types/moment-duration-format/-/moment-duration-format-2.2.2.tgz#da0c9501861661d19fdb62415907a93c44709a81" + integrity sha512-CuYswsMI3y5uR5sD9i/VUqIbZrsYN2eaCs7nH3qpDl2CZlNI48mjMf4ve2RpQ/65irprtnQVetfnea9my+jqcg== + dependencies: + moment ">=2.14.0" + "@types/node@*": version "13.7.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.4.tgz#76c3cb3a12909510f52e5dc04a6298cdf9504ffd" @@ -3176,6 +3183,17 @@ array-includes@^3.1.3: get-intrinsic "^1.1.1" is-string "^1.0.5" +array-includes@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a" + integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + get-intrinsic "^1.1.1" + is-string "^1.0.5" + array-normalize@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/array-normalize/-/array-normalize-1.1.4.tgz#d75cec57383358af38efdf6a78071aa36ae4174c" @@ -9271,6 +9289,14 @@ is-regex@^1.1.2: call-bind "^1.0.2" has-symbols "^1.0.1" +is-regex@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" + integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.1" + is-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" @@ -11239,6 +11265,11 @@ moment-duration-format@2.3.2: resolved "https://registry.yarnpkg.com/moment-duration-format/-/moment-duration-format-2.3.2.tgz#5fa2b19b941b8d277122ff3f87a12895ec0d6212" integrity sha512-cBMXjSW+fjOb4tyaVHuaVE/A5TqkukDWiOfxxAjY+PEqmmBQlLwn+8OzwPiG3brouXKY5Un4pBjAeB6UToXHaQ== +moment-precise-range-plugin@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/moment-precise-range-plugin/-/moment-precise-range-plugin-1.3.0.tgz#60ac075fdfd14689f6d102af751d171a80b4ab60" + integrity sha1-YKwHX9/RRon20QKvdR0XGoC0q2A= + moment-timezone@0.5.31: version "0.5.31" resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05" @@ -11246,7 +11277,7 @@ moment-timezone@0.5.31: dependencies: moment ">= 2.9.0" -moment@2.29.1: +moment@2.29.1, moment@>=2.14.0: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== @@ -11795,6 +11826,16 @@ object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + object.entries@^1.1.0, object.entries@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" @@ -11879,6 +11920,16 @@ object.values@^1.1.3: es-abstract "^1.18.0-next.2" has "^1.0.3" +object.values@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.3.tgz#eaa8b1e17589f02f698db093f7c62ee1699742ee" + integrity sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + has "^1.0.3" + obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -14261,6 +14312,14 @@ resolve@^2.0.0-next.3: is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@^2.0.0-next.3: + version "2.0.0-next.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" + integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + resolve@~1.14.2: version "1.14.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2"