11import { ConfigChange } from "@flanksource-ui/api/types/configs" ;
2+ import { parseDateMath } from "@flanksource-ui/ui/Dates/TimeRangePicker/parseDateMath" ;
3+ import useTimeRangeParams from "@flanksource-ui/ui/Dates/TimeRangePicker/useTimeRangeParams" ;
4+ import {
5+ TimeRangeOption ,
6+ displayTimeFormat ,
7+ timeRangeOptionsToAbsolute
8+ } from "@flanksource-ui/ui/Dates/TimeRangePicker/rangeOptions" ;
29import dayjs from "dayjs" ;
310import { useCallback , useMemo , useRef , useState } from "react" ;
411import ConfigChangesSwimlaneLegend from "./Swimlane/Legend" ;
12+ import {
13+ MORE_TIME_RANGE_BAR_WIDTH ,
14+ MoreTimeRangeBar
15+ } from "./Swimlane/MoreTimeRangeHandle" ;
516import {
617 EmptySwimlaneState ,
718 GroupParentRow ,
@@ -23,6 +34,64 @@ import {
2334 useResizableColumn
2435} from "./Swimlane/Utils" ;
2536
37+ const GRAPH_RANGE_PRESETS = [
38+ { display : "5 minutes" , range : "now-5m" , ms : 5 * 60 * 1000 } ,
39+ { display : "15 minutes" , range : "now-15m" , ms : 15 * 60 * 1000 } ,
40+ { display : "30 minutes" , range : "now-30m" , ms : 30 * 60 * 1000 } ,
41+ { display : "1 hour" , range : "now-1h" , ms : 60 * 60 * 1000 } ,
42+ { display : "2 hours" , range : "now-2h" , ms : 2 * 60 * 60 * 1000 } ,
43+ { display : "3 hours" , range : "now-3h" , ms : 3 * 60 * 60 * 1000 } ,
44+ { display : "6 hours" , range : "now-6h" , ms : 6 * 60 * 60 * 1000 } ,
45+ { display : "12 hours" , range : "now-12h" , ms : 12 * 60 * 60 * 1000 } ,
46+ { display : "24 hours" , range : "now-24h" , ms : 24 * 60 * 60 * 1000 } ,
47+ { display : "2 days" , range : "now-2d" , ms : 2 * 24 * 60 * 60 * 1000 } ,
48+ { display : "7 days" , range : "now-7d" , ms : 7 * 24 * 60 * 60 * 1000 }
49+ ] ;
50+
51+ const GRAPH_DEFAULT_RANGE : TimeRangeOption = {
52+ type : "relative" ,
53+ display : "2 hours" ,
54+ range : "now-2h"
55+ } ;
56+
57+ function resolveRangeDate ( value : string , roundUp = false ) {
58+ if ( value === "now" ) {
59+ return dayjs ( ) ;
60+ }
61+ if ( value . startsWith ( "now" ) ) {
62+ return dayjs ( parseDateMath ( value , roundUp ) ) ;
63+ }
64+ return dayjs ( value ) ;
65+ }
66+
67+ function getIncreasedTimeRange ( range ?: TimeRangeOption ) : TimeRangeOption {
68+ const currentRange = range ?? GRAPH_DEFAULT_RANGE ;
69+ const { from, to } = timeRangeOptionsToAbsolute ( currentRange ) ;
70+ const fromDate = resolveRangeDate ( from ) ;
71+ const toDate = resolveRangeDate ( to , true ) ;
72+ const durationMs = Math . max ( 0 , toDate . diff ( fromDate ) ) ;
73+ const nextPreset =
74+ GRAPH_RANGE_PRESETS . find ( ( preset ) => preset . ms > durationMs + 1000 ) ??
75+ GRAPH_RANGE_PRESETS [ GRAPH_RANGE_PRESETS . length - 1 ] ! ;
76+
77+ if ( currentRange . type === "relative" && to === "now" ) {
78+ return {
79+ type : "relative" ,
80+ display : nextPreset . display ,
81+ range : nextPreset . range
82+ } ;
83+ }
84+
85+ return {
86+ type : "absolute" ,
87+ display : "Custom" ,
88+ from : toDate
89+ . subtract ( nextPreset . ms , "millisecond" )
90+ . format ( displayTimeFormat ) ,
91+ to : toDate . format ( displayTimeFormat )
92+ } ;
93+ }
94+
2695type ConfigChangesSwimlaneProps = {
2796 changes : ConfigChange [ ] ;
2897 isLoading ?: boolean ;
@@ -39,6 +108,11 @@ export default function ConfigChangesSwimlane({
39108 ) ;
40109 const containerRef = useRef < HTMLDivElement | null > ( null ) ;
41110 const markersRef = useRef < HTMLDivElement | null > ( null ) ;
111+ const { setTimeRangeParams, getTimeRangeFromUrl } = useTimeRangeParams ( ) ;
112+ const timeRangeValue = getTimeRangeFromUrl ( ) ;
113+ const increaseTimeRange = useCallback ( ( ) => {
114+ setTimeRangeParams ( getIncreasedTimeRange ( timeRangeValue ) ) ;
115+ } , [ setTimeRangeParams , timeRangeValue ] ) ;
42116 const containerWidth = useContainerWidth ( containerRef ) ;
43117 const initialColumnWidth = Math . min (
44118 MAX_COLUMN_WIDTH ,
@@ -49,7 +123,7 @@ export default function ConfigChangesSwimlane({
49123 const markersWidth = useContainerWidth ( markersRef ) ;
50124 const numBuckets = Math . max ( 1 , Math . floor ( markersWidth / BUCKET_MIN_PX ) ) ;
51125
52- const { groups, min, max, ticks } = useMemo ( ( ) => {
126+ const { groups, min, max, ticks, hasPreRangeChanges } = useMemo ( ( ) => {
53127 const grouped = new Map < string , ConfigChange [ ] > ( ) ;
54128 for ( const c of changes ) {
55129 const key = c . config_id ?? c . config ?. id ?? c . id ;
@@ -102,7 +176,8 @@ export default function ConfigChangesSwimlane({
102176 groups : groupRowsByPath ( rows ) ,
103177 min,
104178 max,
105- ticks : generateTimeTicks ( min , max )
179+ ticks : generateTimeTicks ( min , max ) ,
180+ hasPreRangeChanges : rows . some ( ( row ) => row . preRangeBadge )
106181 } ;
107182 } , [ changes , numBuckets ] ) ;
108183
@@ -118,6 +193,10 @@ export default function ConfigChangesSwimlane({
118193 if ( isLoading ) return < LoadingSwimlaneState /> ;
119194 if ( changes . length === 0 ) return < EmptySwimlaneState /> ;
120195
196+ const timelineOffsetWidth = hasPreRangeChanges
197+ ? MORE_TIME_RANGE_BAR_WIDTH
198+ : 0 ;
199+
121200 let rowIdx = 0 ;
122201
123202 const renderGroup = (
@@ -135,6 +214,7 @@ export default function ConfigChangesSwimlane({
135214 numBuckets = { numBuckets }
136215 onItemClicked = { onItemClicked }
137216 onResizeMouseDown = { onResizeMouseDown }
217+ timelineOffsetWidth = { timelineOffsetWidth }
138218 min = { min }
139219 max = { max }
140220 indentLevel = { indentLevel }
@@ -156,6 +236,7 @@ export default function ConfigChangesSwimlane({
156236 numBuckets = { numBuckets }
157237 onItemClicked = { onItemClicked }
158238 onResizeMouseDown = { onResizeMouseDown }
239+ timelineOffsetWidth = { timelineOffsetWidth }
159240 min = { min }
160241 max = { max }
161242 indentLevel = { indentLevel }
@@ -174,13 +255,18 @@ export default function ConfigChangesSwimlane({
174255 < TimeAxisHeader
175256 columnWidth = { columnWidth }
176257 markersRef = { markersRef }
258+ timelineOffsetWidth = { timelineOffsetWidth }
177259 ticks = { ticks }
178260 min = { min }
179261 max = { max }
180262 />
181263
182264 < ConfigChangesSwimlaneLegend changes = { changes } />
183265
266+ { hasPreRangeChanges && (
267+ < MoreTimeRangeBar left = { columnWidth } onClick = { increaseTimeRange } />
268+ ) }
269+
184270 < div className = "flex-1" > { groups . map ( ( group ) => renderGroup ( group ) ) } </ div >
185271 </ div >
186272 ) ;
0 commit comments