mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-10 19:19:00 +08:00
feat: update time range selection flows to handle relative and absolu… (#4742)
* feat: update time range selection flows to handle relative and absolute times * fix: lint error * fix: lint error * feat: update logic to handle custom relative times on load and standardize relative time formats * fix: type issue * fix: handle light mode and on custom time range select * chore: update alert frequency corresponding times * chore: update copy URL * feat: update styles
This commit is contained in:
parent
7c2f5352d2
commit
6eced60bf5
@ -5,13 +5,14 @@ import './CustomTimePicker.styles.scss';
|
||||
import { Input, Popover, Tooltip, Typography } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
|
||||
import { Options } from 'container/TopNav/DateTimeSelection/config';
|
||||
import {
|
||||
FixedDurationSuggestionOptions,
|
||||
Options,
|
||||
RelativeDurationSuggestionOptions,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import dayjs from 'dayjs';
|
||||
import { defaultTo, noop } from 'lodash-es';
|
||||
import { isValidTimeFormat } from 'lib/getMinMax';
|
||||
import { defaultTo, isFunction, noop } from 'lodash-es';
|
||||
import debounce from 'lodash-es/debounce';
|
||||
import { CheckCircle, ChevronDown, Clock } from 'lucide-react';
|
||||
import {
|
||||
@ -33,7 +34,14 @@ interface CustomTimePickerProps {
|
||||
onError: (value: boolean) => void;
|
||||
selectedValue: string;
|
||||
selectedTime: string;
|
||||
onValidCustomDateChange: ([t1, t2]: any[]) => void;
|
||||
onValidCustomDateChange: ({
|
||||
time: [t1, t2],
|
||||
timeStr,
|
||||
}: {
|
||||
time: [dayjs.Dayjs | null, dayjs.Dayjs | null];
|
||||
timeStr: string;
|
||||
}) => void;
|
||||
onCustomTimeStatusUpdate?: (isValid: boolean) => void;
|
||||
open: boolean;
|
||||
setOpen: Dispatch<SetStateAction<boolean>>;
|
||||
items: any[];
|
||||
@ -53,6 +61,7 @@ function CustomTimePicker({
|
||||
open,
|
||||
setOpen,
|
||||
onValidCustomDateChange,
|
||||
onCustomTimeStatusUpdate,
|
||||
newPopover,
|
||||
customDateTimeVisible,
|
||||
setCustomDTPickerVisible,
|
||||
@ -85,6 +94,7 @@ function CustomTimePicker({
|
||||
return Options[index].label;
|
||||
}
|
||||
}
|
||||
|
||||
for (
|
||||
let index = 0;
|
||||
index < RelativeDurationSuggestionOptions.length;
|
||||
@ -94,12 +104,17 @@ function CustomTimePicker({
|
||||
return RelativeDurationSuggestionOptions[index].label;
|
||||
}
|
||||
}
|
||||
|
||||
for (let index = 0; index < FixedDurationSuggestionOptions.length; index++) {
|
||||
if (FixedDurationSuggestionOptions[index].value === selectedTime) {
|
||||
return FixedDurationSuggestionOptions[index].label;
|
||||
}
|
||||
}
|
||||
|
||||
if (isValidTimeFormat(selectedTime)) {
|
||||
return selectedTime;
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
@ -161,13 +176,22 @@ function CustomTimePicker({
|
||||
setInputStatus('error');
|
||||
onError(true);
|
||||
setInputErrorMessage('Please enter time less than 6 months');
|
||||
if (isFunction(onCustomTimeStatusUpdate)) {
|
||||
onCustomTimeStatusUpdate(true);
|
||||
}
|
||||
} else {
|
||||
onValidCustomDateChange([minTime, currentTime]);
|
||||
onValidCustomDateChange({
|
||||
time: [minTime, currentTime],
|
||||
timeStr: inputValue,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setInputStatus('error');
|
||||
onError(true);
|
||||
setInputErrorMessage(null);
|
||||
if (isFunction(onCustomTimeStatusUpdate)) {
|
||||
onCustomTimeStatusUpdate(false);
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
|
||||
@ -320,4 +344,5 @@ CustomTimePicker.defaultProps = {
|
||||
setCustomDTPickerVisible: noop,
|
||||
onCustomDateHandler: noop,
|
||||
handleGoLive: noop,
|
||||
onCustomTimeStatusUpdate: noop,
|
||||
};
|
||||
|
@ -29,4 +29,5 @@ export enum QueryParams {
|
||||
expandedWidgetId = 'expandedWidgetId',
|
||||
integration = 'integration',
|
||||
pagination = 'pagination',
|
||||
relativeTime = 'relativeTime',
|
||||
}
|
||||
|
@ -7,7 +7,10 @@ import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import { getFormatNameByOptionId } from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import {
|
||||
CustomTimeType,
|
||||
Time as TimeV2,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useResizeObserver } from 'hooks/useDimensions';
|
||||
@ -39,7 +42,7 @@ export interface ChartPreviewProps {
|
||||
query: Query | null;
|
||||
graphType?: PANEL_TYPES;
|
||||
selectedTime?: timePreferenceType;
|
||||
selectedInterval?: Time | TimeV2;
|
||||
selectedInterval?: Time | TimeV2 | CustomTimeType;
|
||||
headline?: JSX.Element;
|
||||
alertDef?: AlertDef;
|
||||
userQueryKey?: string;
|
||||
@ -53,7 +56,7 @@ function ChartPreview({
|
||||
query,
|
||||
graphType = PANEL_TYPES.TIME_SERIES,
|
||||
selectedTime = 'GLOBAL_TIME',
|
||||
selectedInterval = '5min',
|
||||
selectedInterval = '5m',
|
||||
headline,
|
||||
userQueryKey,
|
||||
allowSelectedIntervalForStepGen = false,
|
||||
|
@ -12,22 +12,30 @@ import {
|
||||
// toChartInterval converts eval window to chart selection time interval
|
||||
export const toChartInterval = (evalWindow: string | undefined): Time => {
|
||||
switch (evalWindow) {
|
||||
case '1m0s':
|
||||
return '1m';
|
||||
case '5m0s':
|
||||
return '5min';
|
||||
return '5m';
|
||||
case '10m0s':
|
||||
return '10min';
|
||||
return '10m';
|
||||
case '15m0s':
|
||||
return '15min';
|
||||
return '15m';
|
||||
case '30m0s':
|
||||
return '30min';
|
||||
return '30m';
|
||||
case '1h0m0s':
|
||||
return '1hr';
|
||||
return '1h';
|
||||
case '3h0m0s':
|
||||
return '3h';
|
||||
case '4h0m0s':
|
||||
return '4hr';
|
||||
return '4h';
|
||||
case '6h0m0s':
|
||||
return '6h';
|
||||
case '12h0m0s':
|
||||
return '12h';
|
||||
case '24h0m0s':
|
||||
return '1day';
|
||||
return '1d';
|
||||
default:
|
||||
return '5min';
|
||||
return '5m';
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
@ -81,8 +82,13 @@ function GridCardGraph({
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const startTime = searchParams.get(QueryParams.startTime);
|
||||
const endTime = searchParams.get(QueryParams.endTime);
|
||||
const relativeTime = searchParams.get(
|
||||
QueryParams.relativeTime,
|
||||
) as CustomTimeType;
|
||||
|
||||
if (startTime && endTime && startTime !== endTime) {
|
||||
if (relativeTime) {
|
||||
dispatch(UpdateTimeInterval(relativeTime));
|
||||
} else if (startTime && endTime && startTime !== endTime) {
|
||||
dispatch(
|
||||
UpdateTimeInterval('custom', [
|
||||
parseInt(getTimeString(startTime), 10),
|
||||
|
@ -2,6 +2,7 @@ import Graph from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import getChartData, { GetChartDataProps } from 'lib/getChartData';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
@ -65,8 +66,13 @@ function LogsExplorerChart({
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const startTime = searchParams.get(QueryParams.startTime);
|
||||
const endTime = searchParams.get(QueryParams.endTime);
|
||||
const relativeTime = searchParams.get(
|
||||
QueryParams.relativeTime,
|
||||
) as CustomTimeType;
|
||||
|
||||
if (startTime && endTime && startTime !== endTime) {
|
||||
if (relativeTime) {
|
||||
dispatch(UpdateTimeInterval(relativeTime));
|
||||
} else if (startTime && endTime && startTime !== endTime) {
|
||||
dispatch(
|
||||
UpdateTimeInterval('custom', [
|
||||
parseInt(getTimeString(startTime), 10),
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import {
|
||||
CustomTimeType,
|
||||
Time as TimeV2,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { GetMinMaxPayload } from 'lib/getMinMax';
|
||||
|
||||
export const getGlobalTime = (
|
||||
selectedTime: Time | TimeV2,
|
||||
selectedTime: Time | TimeV2 | CustomTimeType,
|
||||
globalTime: GetMinMaxPayload,
|
||||
): GetMinMaxPayload | undefined => {
|
||||
if (selectedTime === 'custom') {
|
||||
|
@ -3,6 +3,7 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
|
||||
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useResizeObserver } from 'hooks/useDimensions';
|
||||
@ -97,8 +98,13 @@ function WidgetGraph({
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const startTime = searchParams.get(QueryParams.startTime);
|
||||
const endTime = searchParams.get(QueryParams.endTime);
|
||||
const relativeTime = searchParams.get(
|
||||
QueryParams.relativeTime,
|
||||
) as CustomTimeType;
|
||||
|
||||
if (startTime && endTime && startTime !== endTime) {
|
||||
if (relativeTime) {
|
||||
dispatch(UpdateTimeInterval(relativeTime));
|
||||
} else if (startTime && endTime && startTime !== endTime) {
|
||||
dispatch(
|
||||
UpdateTimeInterval('custom', [
|
||||
parseInt(getTimeString(startTime), 10),
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { ServiceDataProps } from 'api/metrics/getTopLevelOperations';
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import {
|
||||
CustomTimeType,
|
||||
Time as TimeV2,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
@ -25,7 +28,7 @@ export interface GetQueryRangeRequestDataProps {
|
||||
topLevelOperations: [keyof ServiceDataProps, string[]][];
|
||||
maxTime: number;
|
||||
minTime: number;
|
||||
globalSelectedInterval: Time | TimeV2;
|
||||
globalSelectedInterval: Time | TimeV2 | CustomTimeType;
|
||||
}
|
||||
|
||||
export interface GetServiceListFromQueryProps {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import GetMinMax, { GetMinMaxPayload } from 'lib/getMinMax';
|
||||
|
||||
import { Time } from '../DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from '../DateTimeSelectionV2/config';
|
||||
import { CustomTimeType, Time as TimeV2 } from '../DateTimeSelectionV2/config';
|
||||
|
||||
export const options: IOptions[] = [
|
||||
{
|
||||
@ -68,7 +68,7 @@ export interface IOptions {
|
||||
}
|
||||
|
||||
export const getMinMax = (
|
||||
selectedTime: Time | TimeV2,
|
||||
selectedTime: Time | TimeV2 | CustomTimeType,
|
||||
minTime: number,
|
||||
maxTime: number,
|
||||
): GetMinMaxPayload =>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import GetMinMax, { GetMinMaxPayload } from 'lib/getMinMax';
|
||||
|
||||
import { Time } from '../DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from '../DateTimeSelectionV2/config';
|
||||
import { CustomTimeType, Time as TimeV2 } from '../DateTimeSelectionV2/config';
|
||||
|
||||
export const options: IOptions[] = [
|
||||
{
|
||||
@ -68,7 +68,7 @@ export interface IOptions {
|
||||
}
|
||||
|
||||
export const getMinMax = (
|
||||
selectedTime: Time | TimeV2,
|
||||
selectedTime: Time | TimeV2 | CustomTimeType,
|
||||
minTime: number,
|
||||
maxTime: number,
|
||||
): GetMinMaxPayload =>
|
||||
|
@ -1,16 +1,18 @@
|
||||
import ROUTES from 'constants/routes';
|
||||
|
||||
type FiveMin = '5min';
|
||||
type TenMin = '10min';
|
||||
type FifteenMin = '15min';
|
||||
type ThirtyMin = '30min';
|
||||
type OneMin = '1min';
|
||||
type SixHour = '6hr';
|
||||
type OneHour = '1hr';
|
||||
type FourHour = '4hr';
|
||||
type OneDay = '1day';
|
||||
type ThreeDay = '3days';
|
||||
type OneWeek = '1week';
|
||||
type FiveMin = '5m';
|
||||
type TenMin = '10m';
|
||||
type FifteenMin = '15m';
|
||||
type ThirtyMin = '30m';
|
||||
type OneMin = '1m';
|
||||
type SixHour = '6h';
|
||||
type OneHour = '1h';
|
||||
type FourHour = '4h';
|
||||
type ThreeHour = '3h';
|
||||
type TwelveHour = '12h';
|
||||
type OneDay = '1d';
|
||||
type ThreeDay = '3d';
|
||||
type OneWeek = '1w';
|
||||
type Custom = 'custom';
|
||||
|
||||
export type Time =
|
||||
@ -22,37 +24,62 @@ export type Time =
|
||||
| FourHour
|
||||
| SixHour
|
||||
| OneHour
|
||||
| ThreeHour
|
||||
| Custom
|
||||
| OneWeek
|
||||
| OneDay
|
||||
| TwelveHour
|
||||
| ThreeDay;
|
||||
|
||||
export const Options: Option[] = [
|
||||
{ value: '5min', label: 'Last 5 min' },
|
||||
{ value: '15min', label: 'Last 15 min' },
|
||||
{ value: '30min', label: 'Last 30 min' },
|
||||
{ value: '1hr', label: 'Last 1 hour' },
|
||||
{ value: '6hr', label: 'Last 6 hour' },
|
||||
{ value: '1day', label: 'Last 1 day' },
|
||||
{ value: '3days', label: 'Last 3 days' },
|
||||
{ value: '1week', label: 'Last 1 week' },
|
||||
{ value: '5m', label: 'Last 5 min' },
|
||||
{ value: '15m', label: 'Last 15 min' },
|
||||
{ value: '30m', label: 'Last 30 min' },
|
||||
{ value: '1h', label: 'Last 1 hour' },
|
||||
{ value: '6h', label: 'Last 6 hour' },
|
||||
{ value: '1d', label: 'Last 1 day' },
|
||||
{ value: '3d', label: 'Last 3 days' },
|
||||
{ value: '1w', label: 'Last 1 week' },
|
||||
{ value: 'custom', label: 'Custom' },
|
||||
];
|
||||
|
||||
type TimeFrame = {
|
||||
'5min': string;
|
||||
'15min': string;
|
||||
'30min': string;
|
||||
'1hr': string;
|
||||
'6hr': string;
|
||||
'1day': string;
|
||||
'3days': string;
|
||||
'1week': string;
|
||||
[key: string]: string; // Index signature to allow any string as index
|
||||
};
|
||||
|
||||
export const RelativeTimeMap: TimeFrame = {
|
||||
'5min': '5m',
|
||||
'15min': '15m',
|
||||
'30min': '30m',
|
||||
'1hr': '1h',
|
||||
'6hr': '6h',
|
||||
'1day': '1d',
|
||||
'3days': '3d',
|
||||
'1week': '1w',
|
||||
};
|
||||
|
||||
export interface Option {
|
||||
value: Time;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const RelativeDurationOptions: Option[] = [
|
||||
{ value: '5min', label: 'Last 5 min' },
|
||||
{ value: '15min', label: 'Last 15 min' },
|
||||
{ value: '30min', label: 'Last 30 min' },
|
||||
{ value: '1hr', label: 'Last 1 hour' },
|
||||
{ value: '6hr', label: 'Last 6 hour' },
|
||||
{ value: '1day', label: 'Last 1 day' },
|
||||
{ value: '3days', label: 'Last 3 days' },
|
||||
{ value: '1week', label: 'Last 1 week' },
|
||||
{ value: '5m', label: 'Last 5 min' },
|
||||
{ value: '15m', label: 'Last 15 min' },
|
||||
{ value: '30m', label: 'Last 30 min' },
|
||||
{ value: '1h', label: 'Last 1 hour' },
|
||||
{ value: '6h', label: 'Last 6 hour' },
|
||||
{ value: '1d', label: 'Last 1 day' },
|
||||
{ value: '3d', label: 'Last 3 days' },
|
||||
{ value: '1w', label: 'Last 1 week' },
|
||||
];
|
||||
|
||||
export const getDefaultOption = (route: string): Time => {
|
||||
|
@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import AutoRefresh from '../AutoRefresh';
|
||||
import CustomDateTimeModal, { DateTimeRangeType } from '../CustomDateTimeModal';
|
||||
import { Time as TimeV2 } from '../DateTimeSelectionV2/config';
|
||||
import { CustomTimeType, Time as TimeV2 } from '../DateTimeSelectionV2/config';
|
||||
import {
|
||||
getDefaultOption,
|
||||
getOptions,
|
||||
@ -122,7 +122,7 @@ function DateTimeSelection({
|
||||
const getInputLabel = (
|
||||
startTime?: Dayjs,
|
||||
endTime?: Dayjs,
|
||||
timeInterval: Time | TimeV2 = '15min',
|
||||
timeInterval: Time | TimeV2 | CustomTimeType = '15m',
|
||||
): string | Time => {
|
||||
if (startTime && endTime && timeInterval === 'custom') {
|
||||
const format = 'YYYY/MM/DD HH:mm';
|
||||
@ -225,7 +225,7 @@ function DateTimeSelection({
|
||||
[location.pathname],
|
||||
);
|
||||
|
||||
const onSelectHandler = (value: Time | TimeV2): void => {
|
||||
const onSelectHandler = (value: Time | TimeV2 | CustomTimeType): void => {
|
||||
if (value !== 'custom') {
|
||||
updateTimeInterval(value);
|
||||
updateLocalStorageForRoutes(value);
|
||||
@ -358,7 +358,7 @@ function DateTimeSelection({
|
||||
}}
|
||||
selectedTime={selectedTime}
|
||||
onValidCustomDateChange={(dateTime): void =>
|
||||
onCustomDateHandler(dateTime as DateTimeRangeType)
|
||||
onCustomDateHandler(dateTime.time as DateTimeRangeType)
|
||||
}
|
||||
selectedValue={getInputLabel(
|
||||
dayjs(minTime / 1000000),
|
||||
@ -406,7 +406,7 @@ function DateTimeSelection({
|
||||
|
||||
interface DispatchProps {
|
||||
updateTimeInterval: (
|
||||
interval: Time | TimeV2,
|
||||
interval: Time | TimeV2 | CustomTimeType,
|
||||
dateTimeRange?: [number, number],
|
||||
) => (dispatch: Dispatch<AppActions>) => void;
|
||||
globalTimeLoading: () => void;
|
||||
|
@ -54,9 +54,61 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.share-link-btn {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.shareable-link-popover {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.date-time-root {
|
||||
.share-modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
width: 420px;
|
||||
|
||||
.absolute-relative-time-toggler-container {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.absolute-relative-time-toggler {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.absolute-relative-time-error {
|
||||
font-size: 12px;
|
||||
color: var(--bg-amber-600);
|
||||
}
|
||||
|
||||
.share-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.share-url {
|
||||
flex: 1;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
border-radius: 2px;
|
||||
background: var(--bg-ink-300);
|
||||
height: 32px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.copy-url-btn {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.date-time-root,
|
||||
.shareable-link-popover-root {
|
||||
.ant-popover-inner {
|
||||
border-radius: 4px !important;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
@ -185,7 +237,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.date-time-root {
|
||||
.date-time-root,
|
||||
.shareable-link-popover-root {
|
||||
.ant-popover-inner {
|
||||
border: 1px solid var(--bg-vanilla-400);
|
||||
background: var(--bg-vanilla-100) !important;
|
||||
@ -234,4 +287,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.share-modal-content {
|
||||
.share-link {
|
||||
.share-url {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import ROUTES from 'constants/routes';
|
||||
|
||||
type FiveMin = '5min';
|
||||
type TenMin = '10min';
|
||||
type FifteenMin = '15min';
|
||||
type ThirtyMin = '30min';
|
||||
type FortyFiveMin = '45min';
|
||||
type OneMin = '1min';
|
||||
type ThreeHour = '3hr';
|
||||
type SixHour = '6hr';
|
||||
type OneHour = '1hr';
|
||||
type FourHour = '4hr';
|
||||
type TwelveHour = '12hr';
|
||||
type OneDay = '1day';
|
||||
type ThreeDay = '3days';
|
||||
type FourDay = '4days';
|
||||
type TenDay = '10days';
|
||||
type OneWeek = '1week';
|
||||
type TwoWeek = '2weeks';
|
||||
type SixWeek = '6weeks';
|
||||
type FiveMin = '5m';
|
||||
type TenMin = '10m';
|
||||
type FifteenMin = '15m';
|
||||
type ThirtyMin = '30m';
|
||||
type FortyFiveMin = '45m';
|
||||
type OneMin = '1m';
|
||||
type ThreeHour = '3h';
|
||||
type SixHour = '6h';
|
||||
type OneHour = '1h';
|
||||
type FourHour = '4h';
|
||||
type TwelveHour = '12h';
|
||||
type OneDay = '1d';
|
||||
type ThreeDay = '3d';
|
||||
type FourDay = '4d';
|
||||
type TenDay = '10d';
|
||||
type OneWeek = '1w';
|
||||
type TwoWeek = '2w';
|
||||
type SixWeek = '6w';
|
||||
type TwoMonths = '2months';
|
||||
type Custom = 'custom';
|
||||
|
||||
@ -44,15 +44,19 @@ export type Time =
|
||||
| TwoWeek
|
||||
| TwoMonths;
|
||||
|
||||
export type TimeUnit = 'm' | 'h' | 'd' | 'w';
|
||||
|
||||
export type CustomTimeType = `${string}${TimeUnit}`;
|
||||
|
||||
export const Options: Option[] = [
|
||||
{ value: '5min', label: 'Last 5 minutes' },
|
||||
{ value: '15min', label: 'Last 15 minutes' },
|
||||
{ value: '30min', label: 'Last 30 minutes' },
|
||||
{ value: '1hr', label: 'Last 1 hour' },
|
||||
{ value: '6hr', label: 'Last 6 hours' },
|
||||
{ value: '1day', label: 'Last 1 day' },
|
||||
{ value: '3days', label: 'Last 3 days' },
|
||||
{ value: '1week', label: 'Last 1 week' },
|
||||
{ value: '5m', label: 'Last 5 minutes' },
|
||||
{ value: '15m', label: 'Last 15 minutes' },
|
||||
{ value: '30m', label: 'Last 30 minutes' },
|
||||
{ value: '1h', label: 'Last 1 hour' },
|
||||
{ value: '6h', label: 'Last 6 hours' },
|
||||
{ value: '1d', label: 'Last 1 day' },
|
||||
{ value: '3d', label: 'Last 3 days' },
|
||||
{ value: '1w', label: 'Last 1 week' },
|
||||
{ value: 'custom', label: 'Custom' },
|
||||
];
|
||||
|
||||
@ -61,36 +65,92 @@ export interface Option {
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const OLD_RELATIVE_TIME_VALUES = [
|
||||
'1min',
|
||||
'10min',
|
||||
'15min',
|
||||
'1hr',
|
||||
'30min',
|
||||
'45min',
|
||||
'5min',
|
||||
'1day',
|
||||
'3days',
|
||||
'4days',
|
||||
'10days',
|
||||
'1week',
|
||||
'2weeks',
|
||||
'6weeks',
|
||||
'3hr',
|
||||
'4hr',
|
||||
'6hr',
|
||||
'12hr',
|
||||
];
|
||||
|
||||
export const RelativeDurationOptions: Option[] = [
|
||||
{ value: '5min', label: 'Last 5 minutes' },
|
||||
{ value: '15min', label: 'Last 15 minutes' },
|
||||
{ value: '30min', label: 'Last 30 minutes' },
|
||||
{ value: '1hr', label: 'Last 1 hour' },
|
||||
{ value: '6hr', label: 'Last 6 hour' },
|
||||
{ value: '1day', label: 'Last 1 day' },
|
||||
{ value: '3days', label: 'Last 3 days' },
|
||||
{ value: '1week', label: 'Last 1 week' },
|
||||
{ value: '5m', label: 'Last 5 minutes' },
|
||||
{ value: '15m', label: 'Last 15 minutes' },
|
||||
{ value: '30m', label: 'Last 30 minutes' },
|
||||
{ value: '1h', label: 'Last 1 hour' },
|
||||
{ value: '6h', label: 'Last 6 hour' },
|
||||
{ value: '1d', label: 'Last 1 day' },
|
||||
{ value: '3d', label: 'Last 3 days' },
|
||||
{ value: '1w', label: 'Last 1 week' },
|
||||
];
|
||||
|
||||
export const RelativeDurationSuggestionOptions: Option[] = [
|
||||
{ value: '3hr', label: '3h' },
|
||||
{ value: '4days', label: '4d' },
|
||||
{ value: '6weeks', label: '6w' },
|
||||
{ value: '12hr', label: '12 hours' },
|
||||
{ value: '10days', label: '10d' },
|
||||
{ value: '2weeks', label: '2 weeks' },
|
||||
{ value: '3h', label: 'Last 3 hours' },
|
||||
{ value: '4d', label: 'Last 4 days' },
|
||||
{ value: '6w', label: 'Last 6 weeks' },
|
||||
{ value: '12h', label: 'Last 12 hours' },
|
||||
{ value: '10d', label: 'Last 10 days' },
|
||||
{ value: '2w', label: 'Last 2 weeks' },
|
||||
{ value: '2months', label: 'Last 2 months' },
|
||||
{ value: '1day', label: 'today' },
|
||||
{ value: '1d', label: 'today' },
|
||||
];
|
||||
export const FixedDurationSuggestionOptions: Option[] = [
|
||||
{ value: '45min', label: '45m' },
|
||||
{ value: '12hr', label: '12 hours' },
|
||||
{ value: '10days', label: '10d' },
|
||||
{ value: '2weeks', label: '2 weeks' },
|
||||
{ value: '45m', label: 'Last 45 mins' },
|
||||
{ value: '12h', label: 'Last 12 hours' },
|
||||
{ value: '10d', label: 'Last 10 days' },
|
||||
{ value: '2w', label: 'Last 2 weeks' },
|
||||
{ value: '2months', label: 'Last 2 months' },
|
||||
{ value: '1day', label: 'today' },
|
||||
{ value: '1d', label: 'today' },
|
||||
];
|
||||
|
||||
export const convertOldTimeToNewValidCustomTimeFormat = (
|
||||
time: string,
|
||||
): CustomTimeType => {
|
||||
const regex = /^(\d+)([a-zA-Z]+)/;
|
||||
const match = regex.exec(time);
|
||||
|
||||
if (match) {
|
||||
let unit = 'm';
|
||||
|
||||
switch (match[2]) {
|
||||
case 'min':
|
||||
unit = 'm';
|
||||
break;
|
||||
case 'hr':
|
||||
unit = 'h';
|
||||
break;
|
||||
case 'day':
|
||||
case 'days':
|
||||
unit = 'd';
|
||||
break;
|
||||
case 'week':
|
||||
case 'weeks':
|
||||
unit = 'w';
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return `${match[1]}${unit}` as CustomTimeType;
|
||||
}
|
||||
|
||||
return '30m';
|
||||
};
|
||||
|
||||
export const getDefaultOption = (route: string): Time => {
|
||||
if (route === ROUTES.SERVICE_MAP) {
|
||||
return RelativeDurationOptions[2].value;
|
||||
|
@ -1,7 +1,8 @@
|
||||
import './DateTimeSelectionV2.styles.scss';
|
||||
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Popover, Switch, Typography } from 'antd';
|
||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||
import setLocalStorageKey from 'api/browser/localstorage/set';
|
||||
import CustomTimePicker from 'components/CustomTimePicker/CustomTimePicker';
|
||||
@ -26,10 +27,12 @@ import GetMinMax from 'lib/getMinMax';
|
||||
import getTimeString from 'lib/getTimeString';
|
||||
import history from 'lib/history';
|
||||
import { isObject } from 'lodash-es';
|
||||
import { Check, Copy, Info, Send } from 'lucide-react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { connect, useSelector } from 'react-redux';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { GlobalTimeLoading, UpdateTimeInterval } from 'store/actions';
|
||||
@ -42,9 +45,12 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import AutoRefresh from '../AutoRefreshV2';
|
||||
import { DateTimeRangeType } from '../CustomDateTimeModal';
|
||||
import {
|
||||
convertOldTimeToNewValidCustomTimeFormat,
|
||||
CustomTimeType,
|
||||
getDefaultOption,
|
||||
getOptions,
|
||||
LocalStorageTimeRange,
|
||||
OLD_RELATIVE_TIME_VALUES,
|
||||
Time,
|
||||
TimeRange,
|
||||
} from './config';
|
||||
@ -66,6 +72,10 @@ function DateTimeSelection({
|
||||
const searchStartTime = urlQuery.get('startTime');
|
||||
const searchEndTime = urlQuery.get('endTime');
|
||||
const queryClient = useQueryClient();
|
||||
const [enableAbsoluteTime, setEnableAbsoluteTime] = useState(false);
|
||||
const [isValidteRelativeTime, setIsValidteRelativeTime] = useState(false);
|
||||
const [, handleCopyToClipboard] = useCopyToClipboard();
|
||||
const [isURLCopied, setIsURLCopied] = useState(false);
|
||||
|
||||
const {
|
||||
localstorageStartTime,
|
||||
@ -178,7 +188,7 @@ function DateTimeSelection({
|
||||
const getInputLabel = (
|
||||
startTime?: Dayjs,
|
||||
endTime?: Dayjs,
|
||||
timeInterval: Time = '15min',
|
||||
timeInterval: Time | CustomTimeType = '15m',
|
||||
): string | Time => {
|
||||
if (startTime && endTime && timeInterval === 'custom') {
|
||||
const format = 'DD/MM/YYYY HH:mm';
|
||||
@ -284,28 +294,38 @@ function DateTimeSelection({
|
||||
[location.pathname],
|
||||
);
|
||||
|
||||
const onSelectHandler = (value: Time): void => {
|
||||
const onSelectHandler = (value: Time | CustomTimeType): void => {
|
||||
if (value !== 'custom') {
|
||||
setIsOpen(false);
|
||||
updateTimeInterval(value);
|
||||
updateLocalStorageForRoutes(value);
|
||||
setIsValidteRelativeTime(true);
|
||||
if (refreshButtonHidden) {
|
||||
setRefreshButtonHidden(false);
|
||||
}
|
||||
} else {
|
||||
setRefreshButtonHidden(true);
|
||||
setCustomDTPickerVisible(true);
|
||||
setIsValidteRelativeTime(false);
|
||||
setEnableAbsoluteTime(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { maxTime, minTime } = GetMinMax(value, getTime());
|
||||
|
||||
if (!isLogsExplorerPage) {
|
||||
urlQuery.set(QueryParams.startTime, minTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, maxTime.toString());
|
||||
urlQuery.delete('startTime');
|
||||
urlQuery.delete('endTime');
|
||||
|
||||
urlQuery.set(QueryParams.relativeTime, value);
|
||||
|
||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
||||
history.replace(generatedUrl);
|
||||
}
|
||||
|
||||
// For logs explorer - time range handling is managed in useCopyLogLink.ts:52
|
||||
|
||||
if (!stagedQuery) {
|
||||
return;
|
||||
}
|
||||
@ -319,18 +339,22 @@ function DateTimeSelection({
|
||||
};
|
||||
|
||||
const onCustomDateHandler = (dateTimeRange: DateTimeRangeType): void => {
|
||||
// console.log('dateTimeRange', dateTimeRange);
|
||||
if (dateTimeRange !== null) {
|
||||
const [startTimeMoment, endTimeMoment] = dateTimeRange;
|
||||
if (startTimeMoment && endTimeMoment) {
|
||||
const startTime = startTimeMoment;
|
||||
const endTime = endTimeMoment;
|
||||
setCustomDTPickerVisible(false);
|
||||
|
||||
updateTimeInterval('custom', [
|
||||
startTime.toDate().getTime(),
|
||||
endTime.toDate().getTime(),
|
||||
]);
|
||||
|
||||
setLocalStorageKey('startTime', startTime.toString());
|
||||
setLocalStorageKey('endTime', endTime.toString());
|
||||
|
||||
updateLocalStorageForRoutes(JSON.stringify({ startTime, endTime }));
|
||||
|
||||
if (!isLogsExplorerPage) {
|
||||
@ -339,6 +363,7 @@ function DateTimeSelection({
|
||||
startTime?.toDate().getTime().toString(),
|
||||
);
|
||||
urlQuery.set(QueryParams.endTime, endTime?.toDate().getTime().toString());
|
||||
urlQuery.delete(QueryParams.relativeTime);
|
||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
||||
history.replace(generatedUrl);
|
||||
}
|
||||
@ -346,6 +371,57 @@ function DateTimeSelection({
|
||||
}
|
||||
};
|
||||
|
||||
const onValidCustomDateHandler = (dateTimeStr: CustomTimeType): void => {
|
||||
setIsOpen(false);
|
||||
updateTimeInterval(dateTimeStr);
|
||||
updateLocalStorageForRoutes(dateTimeStr);
|
||||
|
||||
urlQuery.delete('startTime');
|
||||
urlQuery.delete('endTime');
|
||||
|
||||
setIsValidteRelativeTime(true);
|
||||
|
||||
const { maxTime, minTime } = GetMinMax(dateTimeStr, getTime());
|
||||
|
||||
if (!isLogsExplorerPage) {
|
||||
urlQuery.delete('startTime');
|
||||
urlQuery.delete('endTime');
|
||||
|
||||
urlQuery.set(QueryParams.relativeTime, dateTimeStr);
|
||||
|
||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
||||
history.replace(generatedUrl);
|
||||
}
|
||||
|
||||
if (!stagedQuery) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the second boolean param directs the qb about the time change so to merge the query and retain the current state
|
||||
initQueryBuilderData(updateStepInterval(stagedQuery, maxTime, minTime), true);
|
||||
};
|
||||
|
||||
const getCustomOrIntervalTime = (
|
||||
time: Time,
|
||||
currentRoute: string,
|
||||
): Time | CustomTimeType => {
|
||||
if (searchEndTime !== null && searchStartTime !== null) {
|
||||
return 'custom';
|
||||
}
|
||||
if (
|
||||
(localstorageEndTime === null || localstorageStartTime === null) &&
|
||||
time === 'custom'
|
||||
) {
|
||||
return getDefaultOption(currentRoute);
|
||||
}
|
||||
|
||||
if (OLD_RELATIVE_TIME_VALUES.indexOf(time) > -1) {
|
||||
return convertOldTimeToNewValidCustomTimeFormat(time);
|
||||
}
|
||||
|
||||
return time;
|
||||
};
|
||||
|
||||
// this is triggred when we change the routes and based on that we are changing the default options
|
||||
useEffect(() => {
|
||||
const metricsTimeDuration = getLocalStorageKey(
|
||||
@ -365,21 +441,9 @@ function DateTimeSelection({
|
||||
const currentOptions = getOptions(currentRoute);
|
||||
setOptions(currentOptions);
|
||||
|
||||
const getCustomOrIntervalTime = (time: Time): Time => {
|
||||
if (searchEndTime !== null && searchStartTime !== null) {
|
||||
return 'custom';
|
||||
}
|
||||
if (
|
||||
(localstorageEndTime === null || localstorageStartTime === null) &&
|
||||
time === 'custom'
|
||||
) {
|
||||
return getDefaultOption(currentRoute);
|
||||
}
|
||||
const updatedTime = getCustomOrIntervalTime(time, currentRoute);
|
||||
|
||||
return time;
|
||||
};
|
||||
|
||||
const updatedTime = getCustomOrIntervalTime(time);
|
||||
setIsValidteRelativeTime(updatedTime !== 'custom');
|
||||
|
||||
const [preStartTime = 0, preEndTime = 0] = getTime() || [];
|
||||
|
||||
@ -388,18 +452,113 @@ function DateTimeSelection({
|
||||
updateTimeInterval(updatedTime, [preStartTime, preEndTime]);
|
||||
|
||||
if (updatedTime !== 'custom') {
|
||||
const { minTime, maxTime } = GetMinMax(updatedTime);
|
||||
urlQuery.set(QueryParams.startTime, minTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, maxTime.toString());
|
||||
urlQuery.delete('startTime');
|
||||
urlQuery.delete('endTime');
|
||||
|
||||
urlQuery.set(QueryParams.relativeTime, updatedTime);
|
||||
} else {
|
||||
urlQuery.set(QueryParams.startTime, preStartTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, preEndTime.toString());
|
||||
const startTime = preStartTime.toString();
|
||||
const endTime = preEndTime.toString();
|
||||
|
||||
urlQuery.set(QueryParams.startTime, startTime);
|
||||
urlQuery.set(QueryParams.endTime, endTime);
|
||||
}
|
||||
|
||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
||||
|
||||
history.replace(generatedUrl);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [location.pathname, updateTimeInterval, globalTimeLoading]);
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
const shareModalContent = (): JSX.Element => {
|
||||
let currentUrl = window.location.href;
|
||||
|
||||
const startTime = urlQuery.get(QueryParams.startTime);
|
||||
const endTime = urlQuery.get(QueryParams.endTime);
|
||||
const isCustomTime = !!(startTime && endTime && selectedTime === 'custom');
|
||||
|
||||
if (enableAbsoluteTime || isCustomTime) {
|
||||
if (selectedTime === 'custom') {
|
||||
if (searchStartTime && searchEndTime) {
|
||||
urlQuery.set(QueryParams.startTime, searchStartTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, searchEndTime.toString());
|
||||
}
|
||||
} else {
|
||||
const { minTime, maxTime } = GetMinMax(selectedTime);
|
||||
|
||||
urlQuery.set(QueryParams.startTime, minTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, maxTime.toString());
|
||||
}
|
||||
|
||||
urlQuery.delete(QueryParams.relativeTime);
|
||||
|
||||
currentUrl = `${window.location.origin}${
|
||||
location.pathname
|
||||
}?${urlQuery.toString()}`;
|
||||
} else {
|
||||
urlQuery.delete(QueryParams.startTime);
|
||||
urlQuery.delete(QueryParams.endTime);
|
||||
|
||||
urlQuery.set(QueryParams.relativeTime, selectedTime);
|
||||
currentUrl = `${window.location.origin}${
|
||||
location.pathname
|
||||
}?${urlQuery.toString()}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="share-modal-content">
|
||||
<div className="absolute-relative-time-toggler-container">
|
||||
<div className="absolute-relative-time-toggler">
|
||||
{(selectedTime === 'custom' || !isValidteRelativeTime) && (
|
||||
<Info size={14} color={Color.BG_AMBER_600} />
|
||||
)}
|
||||
<Switch
|
||||
checked={enableAbsoluteTime || isCustomTime}
|
||||
disabled={selectedTime === 'custom' || !isValidteRelativeTime}
|
||||
size="small"
|
||||
onChange={(): void => {
|
||||
setEnableAbsoluteTime(!enableAbsoluteTime);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Typography.Text>Enable Absolute Time</Typography.Text>
|
||||
</div>
|
||||
|
||||
{(selectedTime === 'custom' || !isValidteRelativeTime) && (
|
||||
<div className="absolute-relative-time-error">
|
||||
Please select / enter valid relative time to toggle.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="share-link">
|
||||
<Typography.Text ellipsis className="share-url">
|
||||
{currentUrl}
|
||||
</Typography.Text>
|
||||
|
||||
<Button
|
||||
className="periscope-btn copy-url-btn"
|
||||
onClick={(): void => {
|
||||
handleCopyToClipboard(currentUrl);
|
||||
setIsURLCopied(true);
|
||||
setTimeout(() => {
|
||||
setIsURLCopied(false);
|
||||
}, 1000);
|
||||
}}
|
||||
icon={
|
||||
isURLCopied ? (
|
||||
<Check size={14} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
<Copy size={14} color={Color.BG_ROBIN_500} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="date-time-selector">
|
||||
{!hasSelectedTimeError && !refreshButtonHidden && (
|
||||
@ -426,9 +585,12 @@ function DateTimeSelection({
|
||||
setHasSelectedTimeError(hasError);
|
||||
}}
|
||||
selectedTime={selectedTime}
|
||||
onValidCustomDateChange={(dateTime): void =>
|
||||
onCustomDateHandler(dateTime as DateTimeRangeType)
|
||||
}
|
||||
onValidCustomDateChange={(dateTime): void => {
|
||||
onValidCustomDateHandler(dateTime.timeStr as CustomTimeType);
|
||||
}}
|
||||
onCustomTimeStatusUpdate={(isValid: boolean): void => {
|
||||
setIsValidteRelativeTime(isValid);
|
||||
}}
|
||||
selectedValue={getInputLabel(
|
||||
dayjs(minTime / 1000000),
|
||||
dayjs(maxTime / 1000000),
|
||||
@ -457,6 +619,22 @@ function DateTimeSelection({
|
||||
</FormItem>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Popover
|
||||
rootClassName="shareable-link-popover-root"
|
||||
className="shareable-link-popover"
|
||||
placement="bottomRight"
|
||||
content={shareModalContent}
|
||||
arrow={false}
|
||||
trigger={['hover']}
|
||||
>
|
||||
<Button
|
||||
className="share-link-btn periscope-btn"
|
||||
icon={<Send size={14} />}
|
||||
>
|
||||
Share
|
||||
</Button>
|
||||
</Popover>
|
||||
</FormContainer>
|
||||
</Form>
|
||||
</div>
|
||||
@ -468,7 +646,7 @@ interface DateTimeSelectionV2Props {
|
||||
}
|
||||
interface DispatchProps {
|
||||
updateTimeInterval: (
|
||||
interval: Time,
|
||||
interval: Time | CustomTimeType,
|
||||
dateTimeRange?: [number, number],
|
||||
) => (dispatch: Dispatch<AppActions>) => void;
|
||||
globalTimeLoading: () => void;
|
||||
|
@ -11,8 +11,11 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { HIGHLIGHTED_DELAY } from './configs';
|
||||
import { LogTimeRange, UseCopyLogLink } from './types';
|
||||
@ -33,15 +36,30 @@ export const useCopyLogLink = (logId?: string): UseCopyLogLink => {
|
||||
null,
|
||||
);
|
||||
|
||||
const { selectedTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
|
||||
const onTimeRangeChange = useCallback(
|
||||
(newTimeRange: LogTimeRange | null): void => {
|
||||
urlQuery.set(QueryParams.timeRange, JSON.stringify(newTimeRange));
|
||||
urlQuery.set(QueryParams.startTime, newTimeRange?.start.toString() || '');
|
||||
urlQuery.set(QueryParams.endTime, newTimeRange?.end.toString() || '');
|
||||
|
||||
if (selectedTime !== 'custom') {
|
||||
urlQuery.delete(QueryParams.startTime);
|
||||
urlQuery.delete(QueryParams.endTime);
|
||||
|
||||
urlQuery.set(QueryParams.relativeTime, selectedTime);
|
||||
} else {
|
||||
urlQuery.set(QueryParams.startTime, newTimeRange?.start.toString() || '');
|
||||
urlQuery.set(QueryParams.endTime, newTimeRange?.end.toString() || '');
|
||||
|
||||
urlQuery.delete(QueryParams.relativeTime);
|
||||
}
|
||||
|
||||
const generatedUrl = `${pathname}?${urlQuery.toString()}`;
|
||||
history.replace(generatedUrl);
|
||||
},
|
||||
[pathname, urlQuery],
|
||||
[pathname, urlQuery, selectedTime],
|
||||
);
|
||||
|
||||
const isActiveLog = useMemo(() => activeLogId === logId, [activeLogId, logId]);
|
||||
|
@ -1,7 +1,10 @@
|
||||
import getService from 'api/metrics/getService';
|
||||
import { AxiosError } from 'axios';
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import {
|
||||
CustomTimeType,
|
||||
Time as TimeV2,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import {
|
||||
QueryKey,
|
||||
useQuery,
|
||||
@ -27,7 +30,7 @@ export const useQueryService = ({
|
||||
interface UseQueryServiceProps {
|
||||
minTime: number;
|
||||
maxTime: number;
|
||||
selectedTime: Time | TimeV2;
|
||||
selectedTime: Time | TimeV2 | CustomTimeType;
|
||||
selectedTags: Tags[];
|
||||
options?: UseQueryOptions<PayloadProps, AxiosError, PayloadProps, QueryKey>;
|
||||
}
|
||||
|
@ -6,7 +6,10 @@ import { getMetricsQueryRange } from 'api/metrics/getQueryRange';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import {
|
||||
CustomTimeType,
|
||||
Time as TimeV2,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { Pagination } from 'hooks/queryPagination';
|
||||
import { convertNewDataToOld } from 'lib/newQueryBuilder/convertNewDataToOld';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
@ -67,7 +70,7 @@ export interface GetQueryResultsProps {
|
||||
query: Query;
|
||||
graphType: PANEL_TYPES;
|
||||
selectedTime: timePreferenceType;
|
||||
globalSelectedInterval: Time | TimeV2;
|
||||
globalSelectedInterval: Time | TimeV2 | CustomTimeType;
|
||||
variables?: Record<string, unknown>;
|
||||
params?: Record<string, unknown>;
|
||||
tableParams?: {
|
||||
|
@ -1,63 +1,101 @@
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { isString } from 'lodash-es';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import getMinAgo from './getStartAndEndTime/getMinAgo';
|
||||
|
||||
const validCustomTimeRegex = /^(\d+)([mhdw])$/;
|
||||
|
||||
export const isValidTimeFormat = (time: string): boolean =>
|
||||
validCustomTimeRegex.test(time);
|
||||
|
||||
const extractTimeAndUnit = (time: string): { time: number; unit: string } => {
|
||||
// Match the pattern
|
||||
const match = /^(\d+)([mhdw])$/.exec(time);
|
||||
|
||||
if (match) {
|
||||
return { time: parseInt(match[1], 10), unit: match[2] };
|
||||
}
|
||||
|
||||
return {
|
||||
time: 30,
|
||||
unit: 'm',
|
||||
};
|
||||
};
|
||||
|
||||
export const getMinTimeForRelativeTimes = (
|
||||
time: number,
|
||||
unit: string,
|
||||
): number => {
|
||||
switch (unit) {
|
||||
case 'm':
|
||||
return getMinAgo({ minutes: 1 * time }).getTime();
|
||||
case 'h':
|
||||
return getMinAgo({ minutes: 60 * time }).getTime();
|
||||
case 'd':
|
||||
return getMinAgo({ minutes: 24 * 60 * time }).getTime();
|
||||
case 'w':
|
||||
return getMinAgo({ minutes: 24 * 60 * 7 * time }).getTime();
|
||||
default:
|
||||
return getMinAgo({ minutes: 1 }).getTime();
|
||||
}
|
||||
};
|
||||
|
||||
const GetMinMax = (
|
||||
interval: Time | TimeV2,
|
||||
interval: Time | TimeV2 | string,
|
||||
dateTimeRange?: [number, number],
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
): GetMinMaxPayload => {
|
||||
let maxTime = new Date().getTime();
|
||||
let minTime = 0;
|
||||
|
||||
if (interval === '1min') {
|
||||
if (interval === '1m') {
|
||||
const minTimeAgo = getMinAgo({ minutes: 1 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '10min') {
|
||||
} else if (interval === '10m') {
|
||||
const minTimeAgo = getMinAgo({ minutes: 10 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '15min') {
|
||||
} else if (interval === '15m') {
|
||||
const minTimeAgo = getMinAgo({ minutes: 15 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '1hr') {
|
||||
} else if (interval === '1h') {
|
||||
const minTimeAgo = getMinAgo({ minutes: 60 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '30min') {
|
||||
} else if (interval === '30m') {
|
||||
const minTimeAgo = getMinAgo({ minutes: 30 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '45min') {
|
||||
} else if (interval === '45m') {
|
||||
const minTimeAgo = getMinAgo({ minutes: 45 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '5min') {
|
||||
} else if (interval === '5m') {
|
||||
const minTimeAgo = getMinAgo({ minutes: 5 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '1day') {
|
||||
} else if (interval === '1d') {
|
||||
// one day = 24*60(min)
|
||||
const minTimeAgo = getMinAgo({ minutes: 24 * 60 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '3days') {
|
||||
} else if (interval === '3d') {
|
||||
// three day = one day * 3
|
||||
const minTimeAgo = getMinAgo({ minutes: 24 * 60 * 3 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '4days') {
|
||||
} else if (interval === '4d') {
|
||||
// four day = one day * 4
|
||||
const minTimeAgo = getMinAgo({ minutes: 24 * 60 * 4 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '10days') {
|
||||
} else if (interval === '10d') {
|
||||
// ten day = one day * 10
|
||||
const minTimeAgo = getMinAgo({ minutes: 24 * 60 * 10 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '1week') {
|
||||
} else if (interval === '1w') {
|
||||
// one week = one day * 7
|
||||
const minTimeAgo = getMinAgo({ minutes: 24 * 60 * 7 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '2weeks') {
|
||||
} else if (interval === '2w') {
|
||||
// two week = one day * 14
|
||||
const minTimeAgo = getMinAgo({ minutes: 24 * 60 * 14 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === '6weeks') {
|
||||
} else if (interval === '6w') {
|
||||
// six week = one day * 42
|
||||
const minTimeAgo = getMinAgo({ minutes: 24 * 60 * 42 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
@ -65,13 +103,17 @@ const GetMinMax = (
|
||||
// two months = one day * 60
|
||||
const minTimeAgo = getMinAgo({ minutes: 24 * 60 * 60 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (['3hr', '4hr', '6hr', '12hr'].includes(interval)) {
|
||||
const h = parseInt(interval.replace('hr', ''), 10);
|
||||
} else if (['3h', '4h', '6h', '12h'].includes(interval)) {
|
||||
const h = parseInt(interval.replace('h', ''), 10);
|
||||
const minTimeAgo = getMinAgo({ minutes: h * 60 }).getTime();
|
||||
minTime = minTimeAgo;
|
||||
} else if (interval === 'custom') {
|
||||
maxTime = (dateTimeRange || [])[1] || 0;
|
||||
minTime = (dateTimeRange || [])[0] || 0;
|
||||
} else if (isString(interval) && isValidTimeFormat(interval)) {
|
||||
const { time, unit } = extractTimeAndUnit(interval);
|
||||
|
||||
minTime = getMinTimeForRelativeTimes(time, unit);
|
||||
} else {
|
||||
throw new Error('invalid time type');
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import {
|
||||
CustomTimeType,
|
||||
Time as TimeV2,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import store from 'store';
|
||||
|
||||
import getMaxMinTime from './getMaxMinTime';
|
||||
@ -38,7 +41,7 @@ const getStartEndRangeTime = ({
|
||||
interface GetStartEndRangeTimesProps {
|
||||
type?: timePreferenceType;
|
||||
graphType?: PANEL_TYPES | null;
|
||||
interval?: Time | TimeV2;
|
||||
interval?: Time | TimeV2 | CustomTimeType;
|
||||
}
|
||||
|
||||
interface GetStartEndRangeTimesPayload {
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import {
|
||||
CustomTimeType,
|
||||
Time as TimeV2,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
import { Dispatch } from 'redux';
|
||||
import AppActions from 'types/actions';
|
||||
import { UPDATE_TIME_INTERVAL } from 'types/actions/globalTime';
|
||||
|
||||
export const UpdateTimeInterval = (
|
||||
interval: Time | TimeV2,
|
||||
interval: Time | TimeV2 | CustomTimeType,
|
||||
dateTimeRange: [number, number] = [0, 0],
|
||||
): ((dispatch: Dispatch<AppActions>) => void) => (
|
||||
dispatch: Dispatch<AppActions>,
|
||||
|
@ -88,4 +88,7 @@ export const getFilter = (data: GetFilterPayload): TraceReducer['filter'] => {
|
||||
};
|
||||
|
||||
export const stripTimestampsFromQuery = (query: string): string =>
|
||||
query.replace(/(\?|&)startTime=\d+/, '').replace(/&endTime=\d+/, '');
|
||||
query
|
||||
.replace(/(\?|&)startTime=\d+/, '')
|
||||
.replace(/&endTime=\d+/, '')
|
||||
.replace(/[?&]relativeTime=[^&]+/g, '');
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import {
|
||||
CustomTimeType,
|
||||
Time as TimeV2,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
|
||||
import { ResetIdStartAndEnd, SetSearchQueryString } from './logs';
|
||||
|
||||
@ -14,7 +17,7 @@ export type GlobalTime = {
|
||||
};
|
||||
|
||||
interface UpdateTime extends GlobalTime {
|
||||
selectedTime: Time | TimeV2;
|
||||
selectedTime: Time | TimeV2 | CustomTimeType;
|
||||
}
|
||||
|
||||
interface UpdateTimeInterval {
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { Time as TimeV2 } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import {
|
||||
CustomTimeType,
|
||||
Time as TimeV2,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { GlobalTime } from 'types/actions/globalTime';
|
||||
|
||||
export interface GlobalReducer {
|
||||
maxTime: GlobalTime['maxTime'];
|
||||
minTime: GlobalTime['minTime'];
|
||||
loading: boolean;
|
||||
selectedTime: Time | TimeV2;
|
||||
selectedTime: Time | TimeV2 | CustomTimeType;
|
||||
isAutoRefreshDisabled: boolean;
|
||||
selectedAutoRefreshInterval: string;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user