2024-02-12 00:23:19 +05:30
import './DateTimeSelectionV2.styles.scss' ;
import { SyncOutlined } from '@ant-design/icons' ;
2024-03-29 14:53:48 +05:30
import { Color } from '@signozhq/design-tokens' ;
import { Button , Popover , Switch , Typography } from 'antd' ;
2024-02-12 00:23:19 +05:30
import getLocalStorageKey from 'api/browser/localstorage/get' ;
import setLocalStorageKey from 'api/browser/localstorage/set' ;
import CustomTimePicker from 'components/CustomTimePicker/CustomTimePicker' ;
2025-01-27 16:28:54 +04:30
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats' ;
2024-02-12 00:23:19 +05:30
import { LOCALSTORAGE } from 'constants/localStorage' ;
import { QueryParams } from 'constants/query' ;
import {
initialQueryBuilderFormValuesMap ,
PANEL_TYPES ,
} from 'constants/queryBuilder' ;
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys' ;
import ROUTES from 'constants/routes' ;
import {
constructCompositeQuery ,
defaultLiveQueryDataConfig ,
} from 'container/LiveLogs/constants' ;
import { QueryHistoryState } from 'container/LiveLogs/types' ;
2024-06-04 14:03:49 +05:30
import NewExplorerCTA from 'container/NewExplorerCTA' ;
2024-02-12 00:23:19 +05:30
import dayjs , { Dayjs } from 'dayjs' ;
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder' ;
2025-02-14 08:24:49 +04:30
import { useSafeNavigate } from 'hooks/useSafeNavigate' ;
2024-02-12 00:23:19 +05:30
import useUrlQuery from 'hooks/useUrlQuery' ;
2024-07-25 23:23:01 +05:30
import GetMinMax , { isValidTimeFormat } from 'lib/getMinMax' ;
2024-02-12 00:23:19 +05:30
import getTimeString from 'lib/getTimeString' ;
import { isObject } from 'lodash-es' ;
2024-09-04 21:26:10 +05:30
import { Check , Copy , Info , Send , Undo } from 'lucide-react' ;
2024-12-16 10:27:20 +04:30
import { useTimezone } from 'providers/Timezone' ;
2025-03-06 10:45:11 +04:30
import { useCallback , useEffect , useState } from 'react' ;
2024-02-12 00:23:19 +05:30
import { useQueryClient } from 'react-query' ;
2025-02-14 08:24:49 +04:30
import { connect , useDispatch , useSelector } from 'react-redux' ;
2024-02-12 00:23:19 +05:30
import { RouteComponentProps , withRouter } from 'react-router-dom' ;
2025-02-14 08:24:49 +04:30
import { useNavigationType } from 'react-router-dom-v5-compat' ;
2024-03-29 14:53:48 +05:30
import { useCopyToClipboard } from 'react-use' ;
2024-02-12 00:23:19 +05:30
import { bindActionCreators , Dispatch } from 'redux' ;
import { ThunkDispatch } from 'redux-thunk' ;
import { GlobalTimeLoading , UpdateTimeInterval } from 'store/actions' ;
import { AppState } from 'store/reducers' ;
import AppActions from 'types/actions' ;
import { ErrorResponse , SuccessResponse } from 'types/api' ;
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange' ;
import { GlobalReducer } from 'types/reducer/globalTime' ;
2025-02-14 08:24:49 +04:30
import { normalizeTimeToMs } from 'utils/timeUtils' ;
2024-02-12 00:23:19 +05:30
import AutoRefresh from '../AutoRefreshV2' ;
import { DateTimeRangeType } from '../CustomDateTimeModal' ;
2024-09-04 21:26:10 +05:30
import { RelativeTimeMap } from '../DateTimeSelection/config' ;
2024-02-12 00:23:19 +05:30
import {
2024-03-29 14:53:48 +05:30
convertOldTimeToNewValidCustomTimeFormat ,
CustomTimeType ,
2024-02-12 00:23:19 +05:30
getDefaultOption ,
getOptions ,
LocalStorageTimeRange ,
2024-03-29 14:53:48 +05:30
OLD_RELATIVE_TIME_VALUES ,
2024-02-12 00:23:19 +05:30
Time ,
TimeRange ,
} from './config' ;
import RefreshText from './Refresh' ;
import { Form , FormContainer , FormItem } from './styles' ;
function DateTimeSelection ( {
showAutoRefresh ,
2024-09-06 10:24:47 +05:30
showRefreshText = true ,
2024-05-28 19:09:04 +05:30
hideShareModal = false ,
2024-02-12 00:23:19 +05:30
location ,
updateTimeInterval ,
globalTimeLoading ,
2024-09-04 21:26:10 +05:30
showResetButton = false ,
2024-06-04 14:03:49 +05:30
showOldExplorerCTA = false ,
2024-09-04 21:26:10 +05:30
defaultRelativeTime = RelativeTimeMap [ '6hr' ] as Time ,
2024-11-21 21:53:05 +05:30
isModalTimeSelection = false ,
onTimeChange ,
modalSelectedInterval ,
2025-04-26 09:43:05 +05:30
modalInitialStartTime ,
modalInitialEndTime ,
2024-02-12 00:23:19 +05:30
} : Props ) : JSX . Element {
const [ formSelector ] = Form . useForm ( ) ;
2025-02-14 08:24:49 +04:30
const { safeNavigate } = useSafeNavigate ( ) ;
const navigationType = useNavigationType ( ) ; // Returns 'POP' for back/forward navigation
const dispatch = useDispatch ( ) ;
2024-02-12 00:23:19 +05:30
const [ hasSelectedTimeError , setHasSelectedTimeError ] = useState ( false ) ;
const [ isOpen , setIsOpen ] = useState < boolean > ( false ) ;
const urlQuery = useUrlQuery ( ) ;
const searchStartTime = urlQuery . get ( 'startTime' ) ;
const searchEndTime = urlQuery . get ( 'endTime' ) ;
2024-07-25 23:23:01 +05:30
const relativeTimeFromUrl = urlQuery . get ( QueryParams . relativeTime ) ;
2024-02-12 00:23:19 +05:30
const queryClient = useQueryClient ( ) ;
2024-03-29 14:53:48 +05:30
const [ enableAbsoluteTime , setEnableAbsoluteTime ] = useState ( false ) ;
const [ isValidteRelativeTime , setIsValidteRelativeTime ] = useState ( false ) ;
const [ , handleCopyToClipboard ] = useCopyToClipboard ( ) ;
const [ isURLCopied , setIsURLCopied ] = useState ( false ) ;
2024-02-12 00:23:19 +05:30
2025-04-26 09:43:05 +05:30
// Prioritize props for initial modal time, fallback to URL params
let initialModalStartTime = 0 ;
if ( modalInitialStartTime !== undefined ) {
initialModalStartTime = modalInitialStartTime ;
} else if ( searchStartTime ) {
initialModalStartTime = parseInt ( searchStartTime , 10 ) ;
}
let initialModalEndTime = 0 ;
if ( modalInitialEndTime !== undefined ) {
initialModalEndTime = modalInitialEndTime ;
} else if ( searchEndTime ) {
initialModalEndTime = parseInt ( searchEndTime , 10 ) ;
}
const [ modalStartTime , setModalStartTime ] = useState < number > (
initialModalStartTime ,
) ;
const [ modalEndTime , setModalEndTime ] = useState < number > ( initialModalEndTime ) ;
// Effect to update modal time state when props change
useEffect ( ( ) = > {
if ( modalInitialStartTime !== undefined ) {
setModalStartTime ( modalInitialStartTime ) ;
}
if ( modalInitialEndTime !== undefined ) {
setModalEndTime ( modalInitialEndTime ) ;
}
} , [ modalInitialStartTime , modalInitialEndTime ] ) ;
2024-02-12 00:23:19 +05:30
const {
localstorageStartTime ,
localstorageEndTime ,
} = ( ( ) : LocalStorageTimeRange = > {
const routes = getLocalStorageKey ( LOCALSTORAGE . METRICS_TIME_IN_DURATION ) ;
if ( routes !== null ) {
const routesObject = JSON . parse ( routes || '{}' ) ;
const selectedTime = routesObject [ location . pathname ] ;
if ( selectedTime ) {
let parsedSelectedTime : TimeRange ;
try {
parsedSelectedTime = JSON . parse ( selectedTime ) ;
} catch {
parsedSelectedTime = selectedTime ;
}
if ( isObject ( parsedSelectedTime ) ) {
return {
localstorageStartTime : parsedSelectedTime.startTime ,
localstorageEndTime : parsedSelectedTime.endTime ,
} ;
}
return { localstorageStartTime : null , localstorageEndTime : null } ;
}
}
return { localstorageStartTime : null , localstorageEndTime : null } ;
} ) ( ) ;
const getTime = useCallback ( ( ) : [ number , number ] | undefined = > {
if ( searchEndTime && searchStartTime ) {
const startDate = dayjs (
new Date ( parseInt ( getTimeString ( searchStartTime ) , 10 ) ) ,
) ;
const endDate = dayjs ( new Date ( parseInt ( getTimeString ( searchEndTime ) , 10 ) ) ) ;
return [ startDate . toDate ( ) . getTime ( ) || 0 , endDate . toDate ( ) . getTime ( ) || 0 ] ;
}
if ( localstorageStartTime && localstorageEndTime ) {
const startDate = dayjs ( localstorageStartTime ) ;
const endDate = dayjs ( localstorageEndTime ) ;
return [ startDate . toDate ( ) . getTime ( ) || 0 , endDate . toDate ( ) . getTime ( ) || 0 ] ;
}
return undefined ;
} , [
localstorageEndTime ,
localstorageStartTime ,
searchEndTime ,
searchStartTime ,
] ) ;
const [ options , setOptions ] = useState ( getOptions ( location . pathname ) ) ;
const [ refreshButtonHidden , setRefreshButtonHidden ] = useState < boolean > ( false ) ;
const [ customDateTimeVisible , setCustomDTPickerVisible ] = useState < boolean > (
false ,
) ;
const { stagedQuery , initQueryBuilderData , panelType } = useQueryBuilder ( ) ;
const handleGoLive = useCallback ( ( ) = > {
if ( ! stagedQuery ) return ;
setIsOpen ( false ) ;
let queryHistoryState : QueryHistoryState | null = null ;
const compositeQuery = constructCompositeQuery ( {
query : stagedQuery ,
initialQueryData : initialQueryBuilderFormValuesMap.logs ,
customQueryData : defaultLiveQueryDataConfig ,
} ) ;
const isListView =
panelType === PANEL_TYPES . LIST && stagedQuery . builder . queryData [ 0 ] ;
if ( isListView ) {
const [ graphQuery , listQuery ] = queryClient . getQueriesData <
SuccessResponse < MetricRangePayloadProps > | ErrorResponse
> ( {
queryKey : REACT_QUERY_KEY.GET_QUERY_RANGE ,
active : true ,
} ) ;
queryHistoryState = {
graphQueryPayload :
graphQuery && graphQuery [ 1 ]
? graphQuery [ 1 ] . payload ? . data . result || [ ]
: [ ] ,
listQueryPayload :
listQuery && listQuery [ 1 ]
2024-06-04 00:15:37 +05:30
? listQuery [ 1 ] . payload ? . data ? . newResult ? . data ? . result || [ ]
2024-02-12 00:23:19 +05:30
: [ ] ,
} ;
}
const JSONCompositeQuery = encodeURIComponent ( JSON . stringify ( compositeQuery ) ) ;
const path = ` ${ ROUTES . LIVE_LOGS } ? ${ QueryParams . compositeQuery } = ${ JSONCompositeQuery } ` ;
2025-02-14 08:24:49 +04:30
safeNavigate ( path , { state : queryHistoryState } ) ;
} , [ panelType , queryClient , safeNavigate , stagedQuery ] ) ;
2024-02-12 00:23:19 +05:30
const { maxTime , minTime , selectedTime } = useSelector <
AppState ,
GlobalReducer
> ( ( state ) = > state . globalTime ) ;
const getInputLabel = (
startTime? : Dayjs ,
endTime? : Dayjs ,
2024-03-29 14:53:48 +05:30
timeInterval : Time | CustomTimeType = '15m' ,
2024-02-12 00:23:19 +05:30
) : string | Time = > {
if ( startTime && endTime && timeInterval === 'custom' ) {
2025-01-27 16:28:54 +04:30
const format = DATE_TIME_FORMATS . UK_DATETIME ;
2024-02-12 00:23:19 +05:30
const startString = startTime . format ( format ) ;
const endString = endTime . format ( format ) ;
return ` ${ startString } - ${ endString } ` ;
}
return timeInterval ;
} ;
useEffect ( ( ) = > {
if ( selectedTime === 'custom' ) {
setRefreshButtonHidden ( true ) ;
setCustomDTPickerVisible ( true ) ;
} else {
setRefreshButtonHidden ( false ) ;
setCustomDTPickerVisible ( false ) ;
}
} , [ selectedTime ] ) ;
2024-11-21 21:53:05 +05:30
useEffect ( ( ) = > {
if ( isModalTimeSelection && modalSelectedInterval === 'custom' ) {
setCustomDTPickerVisible ( true ) ;
}
} , [ isModalTimeSelection , modalSelectedInterval ] ) ;
2024-02-12 00:23:19 +05:30
const getDefaultTime = ( pathName : string ) : Time = > {
const defaultSelectedOption = getDefaultOption ( pathName ) ;
const routes = getLocalStorageKey ( LOCALSTORAGE . METRICS_TIME_IN_DURATION ) ;
if ( routes !== null ) {
const routesObject = JSON . parse ( routes || '{}' ) ;
const selectedTime = routesObject [ pathName ] ;
2024-02-14 14:00:17 +05:30
if ( selectedTime ) {
let parsedSelectedTime : TimeRange ;
try {
parsedSelectedTime = JSON . parse ( selectedTime ) ;
} catch {
parsedSelectedTime = selectedTime ;
}
if ( isObject ( parsedSelectedTime ) ) {
return 'custom' ;
}
2024-02-14 12:56:54 +05:30
2024-02-14 14:00:17 +05:30
return selectedTime ;
}
2024-02-12 00:23:19 +05:30
}
return defaultSelectedOption ;
} ;
2024-09-04 21:26:10 +05:30
const updateLocalStorageForRoutes = useCallback (
( value : Time | string ) : void = > {
const preRoutes = getLocalStorageKey ( LOCALSTORAGE . METRICS_TIME_IN_DURATION ) ;
if ( preRoutes !== null ) {
const preRoutesObject = JSON . parse ( preRoutes ) ;
const preRoute = {
. . . preRoutesObject ,
} ;
preRoute [ location . pathname ] = value ;
setLocalStorageKey (
LOCALSTORAGE . METRICS_TIME_IN_DURATION ,
JSON . stringify ( preRoute ) ,
) ;
}
} ,
[ location . pathname ] ,
) ;
2024-02-12 00:23:19 +05:30
const onLastRefreshHandler = useCallback ( ( ) = > {
const currentTime = dayjs ( ) ;
const lastRefresh = dayjs (
selectedTime === 'custom' ? minTime / 1000000 : maxTime / 1000000 ,
) ;
const secondsDiff = currentTime . diff ( lastRefresh , 'seconds' ) ;
const minutedDiff = currentTime . diff ( lastRefresh , 'minutes' ) ;
const hoursDiff = currentTime . diff ( lastRefresh , 'hours' ) ;
const daysDiff = currentTime . diff ( lastRefresh , 'days' ) ;
const monthsDiff = currentTime . diff ( lastRefresh , 'months' ) ;
if ( monthsDiff > 0 ) {
return ` Refreshed ${ monthsDiff } months ago ` ;
}
if ( daysDiff > 0 ) {
return ` Refreshed ${ daysDiff } days ago ` ;
}
if ( hoursDiff > 0 ) {
return ` Refreshed ${ hoursDiff } hrs ago ` ;
}
if ( minutedDiff > 0 ) {
return ` Refreshed ${ minutedDiff } mins ago ` ;
}
return ` Refreshed ${ secondsDiff } sec ago ` ;
} , [ maxTime , minTime , selectedTime ] ) ;
2024-09-04 21:26:10 +05:30
const onSelectHandler = useCallback (
( value : Time | CustomTimeType ) : void = > {
2024-11-21 21:53:05 +05:30
if ( isModalTimeSelection ) {
if ( value === 'custom' ) {
setCustomDTPickerVisible ( true ) ;
setIsValidteRelativeTime ( false ) ;
return ;
}
onTimeChange ? . ( value ) ;
return ;
}
2024-09-04 21:26:10 +05:30
if ( value !== 'custom' ) {
setIsOpen ( false ) ;
updateTimeInterval ( value ) ;
updateLocalStorageForRoutes ( value ) ;
setIsValidteRelativeTime ( true ) ;
if ( refreshButtonHidden ) {
setRefreshButtonHidden ( false ) ;
}
} else {
setRefreshButtonHidden ( true ) ;
setCustomDTPickerVisible ( true ) ;
setIsValidteRelativeTime ( false ) ;
setEnableAbsoluteTime ( false ) ;
2024-03-29 14:53:48 +05:30
2024-09-04 21:26:10 +05:30
return ;
}
2024-02-12 00:23:19 +05:30
2025-03-06 10:45:11 +04:30
urlQuery . delete ( 'startTime' ) ;
urlQuery . delete ( 'endTime' ) ;
2024-03-29 14:53:48 +05:30
2025-03-06 10:45:11 +04:30
urlQuery . set ( QueryParams . relativeTime , value ) ;
2025-06-03 15:27:39 +05:30
// Remove Hidden Filters from URL query parameters on time change
urlQuery . delete ( QueryParams . activeLogId ) ;
2024-03-29 14:53:48 +05:30
2025-03-06 10:45:11 +04:30
const generatedUrl = ` ${ location . pathname } ? ${ urlQuery . toString ( ) } ` ;
safeNavigate ( generatedUrl ) ;
2024-02-12 00:23:19 +05:30
2024-09-04 21:26:10 +05:30
// For logs explorer - time range handling is managed in useCopyLogLink.ts:52
2024-03-29 14:53:48 +05:30
2024-09-04 21:26:10 +05:30
if ( ! stagedQuery ) {
return ;
}
// the second boolean param directs the qb about the time change so to merge the query and retain the current state
// we removed update step interval to stop auto updating the value on time change
initQueryBuilderData ( stagedQuery , true ) ;
} ,
[
initQueryBuilderData ,
2024-11-21 21:53:05 +05:30
isModalTimeSelection ,
2024-09-04 21:26:10 +05:30
location . pathname ,
2024-11-21 21:53:05 +05:30
onTimeChange ,
2024-09-04 21:26:10 +05:30
refreshButtonHidden ,
2025-02-14 08:24:49 +04:30
safeNavigate ,
2024-09-04 21:26:10 +05:30
stagedQuery ,
updateLocalStorageForRoutes ,
updateTimeInterval ,
urlQuery ,
] ,
) ;
2024-02-12 00:23:19 +05:30
const onRefreshHandler = ( ) : void = > {
onSelectHandler ( selectedTime ) ;
onLastRefreshHandler ( ) ;
} ;
2024-09-04 21:26:10 +05:30
const handleReset = useCallback ( ( ) = > {
if ( defaultRelativeTime ) {
onSelectHandler ( defaultRelativeTime ) ;
}
} , [ defaultRelativeTime , onSelectHandler ] ) ;
2024-02-12 00:23:19 +05:30
2024-11-21 21:53:05 +05:30
// eslint-disable-next-line sonarjs/cognitive-complexity
2024-03-11 14:39:17 +05:30
const onCustomDateHandler = ( dateTimeRange : DateTimeRangeType ) : void = > {
2024-02-12 00:23:19 +05:30
if ( dateTimeRange !== null ) {
const [ startTimeMoment , endTimeMoment ] = dateTimeRange ;
2024-11-21 21:53:05 +05:30
if ( isModalTimeSelection ) {
if ( ! startTimeMoment || ! endTimeMoment ) {
setHasSelectedTimeError ( true ) ;
return ;
}
const startTs = startTimeMoment . toDate ( ) . getTime ( ) ;
const endTs = endTimeMoment . toDate ( ) . getTime ( ) ;
if ( startTs >= endTs ) {
setHasSelectedTimeError ( true ) ;
return ;
}
setCustomDTPickerVisible ( false ) ;
setHasSelectedTimeError ( false ) ;
setModalStartTime ( startTs ) ;
setModalEndTime ( endTs ) ;
onTimeChange ? . ( 'custom' , [ startTs , endTs ] ) ;
return ;
}
2024-02-12 00:23:19 +05:30
if ( startTimeMoment && endTimeMoment ) {
2024-03-11 14:39:17 +05:30
const startTime = startTimeMoment ;
const endTime = endTimeMoment ;
2024-02-12 00:23:19 +05:30
setCustomDTPickerVisible ( false ) ;
2024-03-29 14:53:48 +05:30
2024-02-12 00:23:19 +05:30
updateTimeInterval ( 'custom' , [
2024-02-22 16:32:30 +05:30
startTime . toDate ( ) . getTime ( ) ,
endTime . toDate ( ) . getTime ( ) ,
2024-02-12 00:23:19 +05:30
] ) ;
2024-03-29 14:53:48 +05:30
2024-02-22 16:32:30 +05:30
setLocalStorageKey ( 'startTime' , startTime . toString ( ) ) ;
setLocalStorageKey ( 'endTime' , endTime . toString ( ) ) ;
2024-03-29 14:53:48 +05:30
2024-02-22 16:32:30 +05:30
updateLocalStorageForRoutes ( JSON . stringify ( { startTime , endTime } ) ) ;
2024-02-12 00:23:19 +05:30
2025-03-06 10:45:11 +04:30
urlQuery . set (
QueryParams . startTime ,
startTime ? . toDate ( ) . getTime ( ) . toString ( ) ,
) ;
urlQuery . set ( QueryParams . endTime , endTime ? . toDate ( ) . getTime ( ) . toString ( ) ) ;
urlQuery . delete ( QueryParams . relativeTime ) ;
const generatedUrl = ` ${ location . pathname } ? ${ urlQuery . toString ( ) } ` ;
safeNavigate ( generatedUrl ) ;
2024-02-12 00:23:19 +05:30
}
}
} ;
2024-03-29 14:53:48 +05:30
const onValidCustomDateHandler = ( dateTimeStr : CustomTimeType ) : void = > {
2024-11-21 21:53:05 +05:30
if ( isModalTimeSelection ) {
onTimeChange ? . ( dateTimeStr ) ;
return ;
}
2024-03-29 14:53:48 +05:30
setIsOpen ( false ) ;
updateTimeInterval ( dateTimeStr ) ;
updateLocalStorageForRoutes ( dateTimeStr ) ;
urlQuery . delete ( 'startTime' ) ;
urlQuery . delete ( 'endTime' ) ;
setIsValidteRelativeTime ( true ) ;
2025-03-06 10:45:11 +04:30
urlQuery . delete ( 'startTime' ) ;
urlQuery . delete ( 'endTime' ) ;
2024-03-29 14:53:48 +05:30
2025-03-06 10:45:11 +04:30
urlQuery . set ( QueryParams . relativeTime , dateTimeStr ) ;
2024-03-29 14:53:48 +05:30
2025-03-06 10:45:11 +04:30
const generatedUrl = ` ${ location . pathname } ? ${ urlQuery . toString ( ) } ` ;
safeNavigate ( generatedUrl ) ;
2024-03-29 14:53:48 +05:30
if ( ! stagedQuery ) {
return ;
}
// the second boolean param directs the qb about the time change so to merge the query and retain the current state
2024-06-12 17:38:05 +05:30
// we removed update step interval to stop auto updating the value on time change
initQueryBuilderData ( stagedQuery , true ) ;
2024-03-29 14:53:48 +05:30
} ;
const getCustomOrIntervalTime = (
time : Time ,
currentRoute : string ,
) : Time | CustomTimeType = > {
2024-07-25 23:23:01 +05:30
// if the relativeTime param is present in the url give top most preference to the same
// if the relativeTime param is not valid then move to next preference
if ( relativeTimeFromUrl != null && isValidTimeFormat ( relativeTimeFromUrl ) ) {
return relativeTimeFromUrl as Time ;
}
// if the startTime and endTime params are present in the url give next preference to the them.
2024-03-29 14:53:48 +05:30
if ( searchEndTime !== null && searchStartTime !== null ) {
return 'custom' ;
}
2024-07-25 23:23:01 +05:30
// if nothing is present in the url for time range then rely on the local storage values
2024-03-29 14:53:48 +05:30
if (
( localstorageEndTime === null || localstorageStartTime === null ) &&
time === 'custom'
) {
return getDefaultOption ( currentRoute ) ;
}
2024-07-25 23:23:01 +05:30
// if not present in the local storage as well then rely on the defaults set for the page
2024-03-29 14:53:48 +05:30
if ( OLD_RELATIVE_TIME_VALUES . indexOf ( time ) > - 1 ) {
return convertOldTimeToNewValidCustomTimeFormat ( time ) ;
}
return time ;
} ;
2025-02-14 08:24:49 +04:30
const handleAbsoluteTimeSync = useCallback (
(
startTime : string ,
endTime : string ,
currentMinTime : number ,
currentMaxTime : number ,
) : void = > {
const startTs = normalizeTimeToMs ( startTime ) ;
const endTs = normalizeTimeToMs ( endTime ) ;
const timeComparison = {
url : {
start : dayjs ( startTs ) . startOf ( 'minute' ) ,
end : dayjs ( endTs ) . startOf ( 'minute' ) ,
} ,
current : {
start : dayjs ( normalizeTimeToMs ( currentMinTime ) ) . startOf ( 'minute' ) ,
end : dayjs ( normalizeTimeToMs ( currentMaxTime ) ) . startOf ( 'minute' ) ,
} ,
} ;
const hasTimeChanged =
! timeComparison . current . start . isSame ( timeComparison . url . start ) ||
! timeComparison . current . end . isSame ( timeComparison . url . end ) ;
if ( hasTimeChanged ) {
dispatch ( UpdateTimeInterval ( 'custom' , [ startTs , endTs ] ) ) ;
}
} ,
[ dispatch ] ,
) ;
const handleRelativeTimeSync = useCallback (
( relativeTime : string ) : void = > {
updateTimeInterval ( relativeTime as Time ) ;
setIsValidteRelativeTime ( true ) ;
setRefreshButtonHidden ( false ) ;
} ,
[ updateTimeInterval ] ,
) ;
// Sync time picker state with URL on browser navigation
useEffect ( ( ) = > {
if ( navigationType !== 'POP' ) return ;
if ( searchStartTime && searchEndTime ) {
handleAbsoluteTimeSync ( searchStartTime , searchEndTime , minTime , maxTime ) ;
return ;
}
if (
relativeTimeFromUrl &&
isValidTimeFormat ( relativeTimeFromUrl ) &&
relativeTimeFromUrl !== selectedTime
) {
handleRelativeTimeSync ( relativeTimeFromUrl ) ;
}
} , [
navigationType ,
searchStartTime ,
searchEndTime ,
relativeTimeFromUrl ,
selectedTime ,
minTime ,
maxTime ,
dispatch ,
updateTimeInterval ,
handleAbsoluteTimeSync ,
handleRelativeTimeSync ,
] ) ;
2024-02-12 00:23:19 +05:30
// this is triggred when we change the routes and based on that we are changing the default options
useEffect ( ( ) = > {
const metricsTimeDuration = getLocalStorageKey (
LOCALSTORAGE . METRICS_TIME_IN_DURATION ,
) ;
if ( metricsTimeDuration === null ) {
setLocalStorageKey (
LOCALSTORAGE . METRICS_TIME_IN_DURATION ,
JSON . stringify ( { } ) ,
) ;
}
const currentRoute = location . pathname ;
2024-09-04 21:26:10 +05:30
2025-02-14 08:24:49 +04:30
// Give priority to relativeTime from URL if it exists and start /end time are not present in the url, to sync the relative time in URL param with the time picker
if (
! searchStartTime &&
! searchEndTime &&
relativeTimeFromUrl &&
isValidTimeFormat ( relativeTimeFromUrl )
) {
handleRelativeTimeSync ( relativeTimeFromUrl ) ;
}
2024-09-04 21:26:10 +05:30
// set the default relative time for alert history and overview pages if relative time is not specified
if (
( ! urlQuery . has ( QueryParams . startTime ) ||
! urlQuery . has ( QueryParams . endTime ) ) &&
! urlQuery . has ( QueryParams . relativeTime ) &&
( currentRoute === ROUTES . ALERT_OVERVIEW ||
currentRoute === ROUTES . ALERT_HISTORY )
) {
updateTimeInterval ( defaultRelativeTime ) ;
urlQuery . set ( QueryParams . relativeTime , defaultRelativeTime ) ;
const generatedUrl = ` ${ location . pathname } ? ${ urlQuery . toString ( ) } ` ;
2025-02-14 08:24:49 +04:30
safeNavigate ( generatedUrl ) ;
2024-09-04 21:26:10 +05:30
return ;
}
2024-02-12 00:23:19 +05:30
const time = getDefaultTime ( currentRoute ) ;
const currentOptions = getOptions ( currentRoute ) ;
setOptions ( currentOptions ) ;
2024-03-29 14:53:48 +05:30
const updatedTime = getCustomOrIntervalTime ( time , currentRoute ) ;
2024-02-12 00:23:19 +05:30
2024-03-29 14:53:48 +05:30
setIsValidteRelativeTime ( updatedTime !== 'custom' ) ;
2024-02-12 00:23:19 +05:30
const [ preStartTime = 0 , preEndTime = 0 ] = getTime ( ) || [ ] ;
setRefreshButtonHidden ( updatedTime === 'custom' ) ;
2024-07-25 23:23:01 +05:30
if ( updatedTime !== 'custom' ) {
updateTimeInterval ( updatedTime ) ;
} else {
updateTimeInterval ( updatedTime , [ preStartTime , preEndTime ] ) ;
}
2024-02-14 14:00:17 +05:30
if ( updatedTime !== 'custom' ) {
2024-03-29 14:53:48 +05:30
urlQuery . delete ( 'startTime' ) ;
urlQuery . delete ( 'endTime' ) ;
urlQuery . set ( QueryParams . relativeTime , updatedTime ) ;
2024-02-14 14:00:17 +05:30
} else {
2024-03-29 14:53:48 +05:30
const startTime = preStartTime . toString ( ) ;
const endTime = preEndTime . toString ( ) ;
urlQuery . set ( QueryParams . startTime , startTime ) ;
urlQuery . set ( QueryParams . endTime , endTime ) ;
2024-08-26 19:26:34 +05:30
urlQuery . delete ( QueryParams . relativeTime ) ;
2024-02-14 14:00:17 +05:30
}
const generatedUrl = ` ${ location . pathname } ? ${ urlQuery . toString ( ) } ` ;
2025-02-14 08:24:49 +04:30
safeNavigate ( generatedUrl ) ;
2024-02-12 00:23:19 +05:30
// eslint-disable-next-line react-hooks/exhaustive-deps
} , [ location . pathname , updateTimeInterval , globalTimeLoading ] ) ;
2024-03-29 14:53:48 +05:30
// 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 >
) ;
} ;
2024-12-16 10:27:20 +04:30
const { timezone } = useTimezone ( ) ;
2024-02-12 00:23:19 +05:30
return (
< div className = "date-time-selector" >
2024-09-04 21:26:10 +05:30
{ showResetButton && selectedTime !== defaultRelativeTime && (
< FormItem >
< Button
type = "default"
className = "reset-button"
onClick = { handleReset }
title = { ` Reset to ${ defaultRelativeTime } ` }
icon = { < Undo size = { 14 } / > }
>
Reset
< / Button >
< / FormItem >
) }
2024-06-04 14:03:49 +05:30
{ showOldExplorerCTA && (
< div style = { { marginRight : 12 } } >
< NewExplorerCTA / >
< / div >
) }
2024-09-06 10:24:47 +05:30
{ ! hasSelectedTimeError && ! refreshButtonHidden && showRefreshText && (
2024-02-12 00:23:19 +05:30
< RefreshText
{ . . . {
onLastRefreshHandler ,
} }
refreshButtonHidden = { refreshButtonHidden }
/ >
) }
< Form
form = { formSelector }
layout = "inline"
initialValues = { { interval : selectedTime } }
>
< FormContainer >
< CustomTimePicker
open = { isOpen }
setOpen = { setIsOpen }
onSelect = { ( value : unknown ) : void = > {
onSelectHandler ( value as Time ) ;
} }
onError = { ( hasError : boolean ) : void = > {
setHasSelectedTimeError ( hasError ) ;
} }
2024-11-21 21:53:05 +05:30
selectedTime = {
isModalTimeSelection ? ( modalSelectedInterval as Time ) : selectedTime
}
2024-03-29 14:53:48 +05:30
onValidCustomDateChange = { ( dateTime ) : void = > {
onValidCustomDateHandler ( dateTime . timeStr as CustomTimeType ) ;
} }
onCustomTimeStatusUpdate = { ( isValid : boolean ) : void = > {
setIsValidteRelativeTime ( isValid ) ;
} }
2024-02-12 00:23:19 +05:30
selectedValue = { getInputLabel (
2024-12-16 10:27:20 +04:30
dayjs ( isModalTimeSelection ? modalStartTime : minTime / 1000000 ) . tz (
timezone . value ,
) ,
dayjs ( isModalTimeSelection ? modalEndTime : maxTime / 1000000 ) . tz (
timezone . value ,
) ,
2024-11-21 21:53:05 +05:30
isModalTimeSelection ? modalSelectedInterval : selectedTime ,
2024-02-12 00:23:19 +05:30
) }
data - testid = "dropDown"
items = { options }
newPopover
handleGoLive = { handleGoLive }
onCustomDateHandler = { onCustomDateHandler }
customDateTimeVisible = { customDateTimeVisible }
setCustomDTPickerVisible = { setCustomDTPickerVisible }
2025-05-29 10:53:47 +07:00
onTimeChange = { onTimeChange }
2024-02-12 00:23:19 +05:30
/ >
{ showAutoRefresh && selectedTime !== 'custom' && (
< div className = "refresh-actions" >
< FormItem hidden = { refreshButtonHidden } className = "refresh-btn" >
< Button icon = { < SyncOutlined / > } onClick = { onRefreshHandler } / >
< / FormItem >
< FormItem >
< AutoRefresh
disabled = { refreshButtonHidden }
showAutoRefreshBtnPrimary = { false }
/ >
< / FormItem >
< / div >
) }
2024-03-29 14:53:48 +05:30
2024-05-28 19:09:04 +05:30
{ ! hideShareModal && (
< Popover
rootClassName = "shareable-link-popover-root"
className = "shareable-link-popover"
placement = "bottomRight"
content = { shareModalContent }
arrow = { false }
trigger = { [ 'hover' ] }
2024-03-29 14:53:48 +05:30
>
2024-05-28 19:09:04 +05:30
< Button
className = "share-link-btn periscope-btn"
icon = { < Send size = { 14 } / > }
>
Share
< / Button >
< / Popover >
) }
2024-02-12 00:23:19 +05:30
< / FormContainer >
< / Form >
< / div >
) ;
}
interface DateTimeSelectionV2Props {
showAutoRefresh : boolean ;
2024-09-06 10:24:47 +05:30
showRefreshText? : boolean ;
2024-05-28 19:09:04 +05:30
hideShareModal? : boolean ;
2024-06-04 14:03:49 +05:30
showOldExplorerCTA? : boolean ;
2024-09-04 21:26:10 +05:30
showResetButton? : boolean ;
defaultRelativeTime? : Time ;
2024-11-21 21:53:05 +05:30
isModalTimeSelection? : boolean ;
onTimeChange ? : (
interval : Time | CustomTimeType ,
dateTimeRange ? : [ number , number ] ,
) = > void ;
modalSelectedInterval? : Time ;
2025-04-26 09:43:05 +05:30
modalInitialStartTime? : number ;
modalInitialEndTime? : number ;
2024-02-12 00:23:19 +05:30
}
2024-05-28 19:09:04 +05:30
DateTimeSelection . defaultProps = {
hideShareModal : false ,
2024-06-04 14:03:49 +05:30
showOldExplorerCTA : false ,
2024-09-06 10:24:47 +05:30
showRefreshText : true ,
2024-09-04 21:26:10 +05:30
showResetButton : false ,
defaultRelativeTime : RelativeTimeMap [ '6hr' ] as Time ,
2024-11-21 21:53:05 +05:30
isModalTimeSelection : false ,
onTimeChange : ( ) : void = > { } ,
modalSelectedInterval : RelativeTimeMap [ '5m' ] as Time ,
2025-04-26 09:43:05 +05:30
modalInitialStartTime : undefined ,
modalInitialEndTime : undefined ,
2024-05-28 19:09:04 +05:30
} ;
2024-02-12 00:23:19 +05:30
interface DispatchProps {
updateTimeInterval : (
2024-03-29 14:53:48 +05:30
interval : Time | CustomTimeType ,
2024-02-12 00:23:19 +05:30
dateTimeRange ? : [ number , number ] ,
) = > ( dispatch : Dispatch < AppActions > ) = > void ;
globalTimeLoading : ( ) = > void ;
}
const mapDispatchToProps = (
dispatch : ThunkDispatch < unknown , unknown , AppActions > ,
2024-11-21 21:53:05 +05:30
{ isModalTimeSelection } : DateTimeSelectionV2Props ,
2024-02-12 00:23:19 +05:30
) : DispatchProps = > ( {
2024-11-21 21:53:05 +05:30
updateTimeInterval : (
interval : Time | CustomTimeType ,
dateTimeRange ? : [ number , number ] ,
) : ( ( dispatch : Dispatch < AppActions > ) = > void ) = > {
/ * *
* Updates the global time interval only when not in modal view
*
* @param interval - Selected time interval or custom time range
* @param dateTimeRange - Optional tuple of [ startTime , endTime ]
* @returns Function that updates redux store with new time interval , or empty function for modal view
*
* When in modal view ( isModalTimeSelection = true ) , we don ' t want to update the global time state
* as the selection is temporary until the modal is confirmed
* /
if ( ! isModalTimeSelection ) {
return bindActionCreators ( UpdateTimeInterval , dispatch ) (
interval ,
dateTimeRange ,
) ;
}
// Return empty function for modal view as we don't want to update global state
return ( ) : void = > { } ;
} ,
2024-02-12 00:23:19 +05:30
globalTimeLoading : bindActionCreators ( GlobalTimeLoading , dispatch ) ,
} ) ;
type Props = DateTimeSelectionV2Props & DispatchProps & RouteComponentProps ;
export default connect ( null , mapDispatchToProps ) ( withRouter ( DateTimeSelection ) ) ;