-
Notifications
You must be signed in to change notification settings - Fork 25
feat(ui): add swimlane view for config changes #3013
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ba0d12b
4eaaff4
82c080c
4f1ba60
a9cf2f7
f1bea2b
649e4e9
5be31dd
836c31a
041040e
d4e0a6d
5b29640
6da4484
588febd
461f01e
bc52623
8bae873
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,15 @@ | ||
| import { useConfigChangesViewToggleState } from "@flanksource-ui/components/Configs/Changes/ConfigChangesViewToggle"; | ||
| import { TimeRangePicker } from "@flanksource-ui/ui/Dates/TimeRangePicker"; | ||
| import { parseDateMath } from "@flanksource-ui/ui/Dates/TimeRangePicker/parseDateMath"; | ||
| import useTimeRangeParams from "@flanksource-ui/ui/Dates/TimeRangePicker/useTimeRangeParams"; | ||
| import dayjs from "dayjs"; | ||
| import { useCallback, useEffect, useMemo } from "react"; | ||
| import { URLSearchParamsInit } from "react-router-dom"; | ||
| import { | ||
| RangeOptionsCategory, | ||
| TimeRangeOption, | ||
| timeRangeOptionsToAbsolute | ||
| } from "@flanksource-ui/ui/Dates/TimeRangePicker/rangeOptions"; | ||
|
|
||
| type Props = { | ||
| paramsToReset?: string[]; | ||
|
|
@@ -13,21 +22,137 @@ export const configChangesDefaultDateFilter: URLSearchParamsInit = { | |
| range: "now-2d" | ||
| }; | ||
|
|
||
| export const configChangesGraphDefaultDateFilter = { | ||
| type: "relative", | ||
| display: "2 hours", | ||
| range: "now-2h" | ||
| } satisfies TimeRangeOption; | ||
|
|
||
| export const configChangesGraphDefaultDateFilterParams: URLSearchParamsInit = { | ||
| rangeType: "relative", | ||
| display: configChangesGraphDefaultDateFilter.display, | ||
| range: configChangesGraphDefaultDateFilter.range | ||
| }; | ||
|
|
||
| const graphRangeOptionsCategories: RangeOptionsCategory[] = [ | ||
| { | ||
| name: "Relative time ranges", | ||
| type: "past", | ||
| options: [ | ||
| { type: "relative", display: "5 minutes", range: "now-5m" }, | ||
| { type: "relative", display: "15 minutes", range: "now-15m" }, | ||
| { type: "relative", display: "30 minutes", range: "now-30m" }, | ||
| { type: "relative", display: "1 hour", range: "now-1h" }, | ||
| { type: "relative", display: "2 hours", range: "now-2h" }, | ||
| { type: "relative", display: "3 hours", range: "now-3h" }, | ||
| { type: "relative", display: "6 hours", range: "now-6h" }, | ||
| { type: "relative", display: "12 hours", range: "now-12h" }, | ||
| { type: "relative", display: "24 hours", range: "now-24h" }, | ||
| { type: "relative", display: "2 days", range: "now-2d" }, | ||
| { type: "relative", display: "7 days", range: "now-7d" } | ||
| ] | ||
| } | ||
| ]; | ||
|
|
||
| const MAX_GRAPH_RANGE_DAYS = 7; | ||
| const MAX_GRAPH_RANGE_MS = MAX_GRAPH_RANGE_DAYS * 24 * 60 * 60 * 1000; | ||
|
|
||
| const mappedRangesOverGraphLimit = new Set([ | ||
| "Previous month", | ||
| "Previous year", | ||
| "This month", | ||
| "This month so far", | ||
| "This year", | ||
| "This year so far" | ||
| ]); | ||
|
|
||
| function resolveDate(value: string, roundUp = false) { | ||
| if (value === "now") { | ||
| return dayjs(); | ||
| } | ||
| if (value.startsWith("now")) { | ||
| return dayjs(parseDateMath(value, roundUp)); | ||
| } | ||
| return dayjs(value); | ||
| } | ||
|
|
||
| function isRangeOverGraphLimit(range?: TimeRangeOption) { | ||
| if (!range) { | ||
| return false; | ||
| } | ||
| if ( | ||
| range.type === "mapped" && | ||
| mappedRangesOverGraphLimit.has(range.display) | ||
| ) { | ||
| return true; | ||
| } | ||
|
|
||
| const { from, to } = timeRangeOptionsToAbsolute(range); | ||
| const fromDate = resolveDate(from); | ||
| const toDate = resolveDate(to, true); | ||
|
|
||
| if (!fromDate.isValid() || !toDate.isValid()) { | ||
| return false; | ||
| } | ||
|
|
||
| return toDate.diff(fromDate) > MAX_GRAPH_RANGE_MS; | ||
| } | ||
|
|
||
| export default function ConfigChangesDateRangeFilter({ | ||
| paramsToReset = [], | ||
| paramPrefix | ||
| }: Props) { | ||
| const view = useConfigChangesViewToggleState(); | ||
| const isGraphView = view === "Graph"; | ||
| const defaultDateFilter = useMemo( | ||
| () => | ||
| isGraphView | ||
| ? configChangesGraphDefaultDateFilterParams | ||
| : configChangesDefaultDateFilter, | ||
| [isGraphView] | ||
| ); | ||
| const { setTimeRangeParams, getTimeRangeFromUrl } = useTimeRangeParams( | ||
| configChangesDefaultDateFilter, | ||
| defaultDateFilter, | ||
| paramPrefix | ||
| ); | ||
|
|
||
| const timeRangeValue = getTimeRangeFromUrl(); | ||
|
|
||
| const validateGraphTimeRange = useCallback( | ||
| (timeRange: TimeRangeOption) => { | ||
| if (isGraphView && isRangeOverGraphLimit(timeRange)) { | ||
| return `Graph mode supports a maximum time range of ${MAX_GRAPH_RANGE_DAYS} days.`; | ||
| } | ||
| }, | ||
| [isGraphView] | ||
| ); | ||
|
|
||
| const setValidTimeRangeParams = useCallback( | ||
| (timeRange: TimeRangeOption) => { | ||
| setTimeRangeParams( | ||
| validateGraphTimeRange(timeRange) | ||
| ? configChangesGraphDefaultDateFilter | ||
| : timeRange, | ||
| paramsToReset | ||
| ); | ||
| }, | ||
| [paramsToReset, setTimeRangeParams, validateGraphTimeRange] | ||
| ); | ||
|
|
||
| useEffect(() => { | ||
| if (isGraphView && isRangeOverGraphLimit(timeRangeValue)) { | ||
| setTimeRangeParams(configChangesGraphDefaultDateFilter, paramsToReset); | ||
| } | ||
| }, [isGraphView, paramsToReset, setTimeRangeParams, timeRangeValue]); | ||
|
Comment on lines
+142
to
+146
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid overwriting the shared time-range params on a view toggle. This effect turns a non-graph range into 🤖 Prompt for AI Agents |
||
|
|
||
| return ( | ||
| <TimeRangePicker | ||
| onChange={(timeRange) => setTimeRangeParams(timeRange, paramsToReset)} | ||
| onChange={setValidTimeRangeParams} | ||
| value={timeRangeValue} | ||
| rangeOptionsCategories={ | ||
| isGraphView ? graphRangeOptionsCategories : undefined | ||
| } | ||
| validateRange={validateGraphTimeRange} | ||
| /> | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't hard-code
This month so far/This year so faras over-limit.These labels are not always longer than 7 days. On May 6, 2026,
This month so farspans May 1, 2026 → May 6, 2026, so Line 79 rejects a valid graph range before the absolute-duration check runs.This year so farhas the same problem during January 1–7. Please rely on the resolvedfrom/tobounds for these dynamic mapped ranges instead of the display text.💡 Minimal fix
Also applies to: 77-82
🤖 Prompt for AI Agents