From 91cadb8825c03247ab720d2eb13f91608f532e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Oberm=C3=BCller?= Date: Mon, 6 Nov 2023 15:19:56 +0000 Subject: [PATCH] fix(insights): fix date range changes for standalone queries (#18257) --- .../IntervalFilter/IntervalFilter.tsx | 12 +- .../IntervalFilter/intervalFilterLogic.ts | 144 ------------------ .../InsightDateFilter/InsightDateFilter.tsx | 16 +- .../insightDateFilterLogic.ts | 37 ----- .../scenes/insights/insightCommandLogic.ts | 13 +- .../insights/insightVizDataLogic.test.ts | 104 ++++++++++++- .../scenes/insights/insightVizDataLogic.ts | 128 +++++++++++++++- 7 files changed, 245 insertions(+), 209 deletions(-) delete mode 100644 frontend/src/lib/components/IntervalFilter/intervalFilterLogic.ts delete mode 100644 frontend/src/scenes/insights/filters/InsightDateFilter/insightDateFilterLogic.ts diff --git a/frontend/src/lib/components/IntervalFilter/IntervalFilter.tsx b/frontend/src/lib/components/IntervalFilter/IntervalFilter.tsx index 2a0b327000aa2..b8a95dc839191 100644 --- a/frontend/src/lib/components/IntervalFilter/IntervalFilter.tsx +++ b/frontend/src/lib/components/IntervalFilter/IntervalFilter.tsx @@ -1,8 +1,8 @@ -import { intervalFilterLogic } from './intervalFilterLogic' import { useActions, useValues } from 'kea' -import { IntervalType } from '~/types' import { insightLogic } from 'scenes/insights/insightLogic' import { LemonSelect } from '@posthog/lemon-ui' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { InsightQueryNode } from '~/queries/schema' interface IntervalFilterProps { disabled?: boolean @@ -10,8 +10,8 @@ interface IntervalFilterProps { export function IntervalFilter({ disabled }: IntervalFilterProps): JSX.Element { const { insightProps } = useValues(insightLogic) - const { interval, enabledIntervals } = useValues(intervalFilterLogic(insightProps)) - const { setInterval } = useActions(intervalFilterLogic(insightProps)) + const { interval, enabledIntervals } = useValues(insightVizDataLogic(insightProps)) + const { updateQuerySource } = useActions(insightVizDataLogic(insightProps)) return ( <> @@ -24,9 +24,7 @@ export function IntervalFilter({ disabled }: IntervalFilterProps): JSX.Element { value={interval || 'day'} dropdownMatchSelectWidth={false} onChange={(value) => { - if (value) { - setInterval(String(value) as IntervalType) - } + updateQuerySource({ interval: value } as Partial) }} data-attr="interval-filter" options={Object.entries(enabledIntervals).map(([value, { label, disabledReason }]) => ({ diff --git a/frontend/src/lib/components/IntervalFilter/intervalFilterLogic.ts b/frontend/src/lib/components/IntervalFilter/intervalFilterLogic.ts deleted file mode 100644 index d2136d8d8a682..0000000000000 --- a/frontend/src/lib/components/IntervalFilter/intervalFilterLogic.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { kea, props, key, path, connect, actions, reducers, listeners } from 'kea' -import { objectsEqual, dateMapping } from 'lib/utils' -import type { intervalFilterLogicType } from './intervalFilterLogicType' -import { IntervalKeyType, Intervals, intervals } from 'lib/components/IntervalFilter/intervals' -import { BaseMathType, InsightLogicProps, IntervalType } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { dayjs } from 'lib/dayjs' -import { InsightQueryNode, TrendsQuery } from '~/queries/schema' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { BASE_MATH_DEFINITIONS } from 'scenes/trends/mathsLogic' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' - -export const intervalFilterLogic = kea([ - props({} as InsightLogicProps), - key(keyForInsightLogicProps('new')), - path((key) => ['lib', 'components', 'IntervalFilter', 'intervalFilterLogic', key]), - connect((props: InsightLogicProps) => ({ - actions: [insightVizDataLogic(props), ['updateQuerySource']], - values: [insightVizDataLogic(props), ['interval', 'querySource']], - })), - actions(() => ({ - setInterval: (interval: IntervalKeyType) => ({ interval }), - setEnabledIntervals: (enabledIntervals: Intervals) => ({ enabledIntervals }), - })), - reducers(() => ({ - enabledIntervals: [ - { ...intervals } as Intervals, - { - setEnabledIntervals: (_, { enabledIntervals }) => enabledIntervals, - }, - ], - })), - listeners(({ values, actions, selectors }) => ({ - setInterval: ({ interval }) => { - if (values.interval !== interval) { - actions.updateQuerySource({ interval } as Partial) - } - }, - updateQuerySource: ({ querySource }, _, __, previousState) => { - const { date_from, date_to } = querySource.dateRange || {} - const previousDateRange = selectors.querySource(previousState)?.dateRange || {} - - let activeUsersMath: BaseMathType.WeeklyActiveUsers | BaseMathType.MonthlyActiveUsers | null = null - - // We disallow grouping by certain intervals for weekly active users and monthly active users views - // e.g. WAUs grouped by month. Here, look for the first event/action running WAUs/MAUs math and - // pass that down to the interval filter to determine what groupings are allowed. - for (const series of (values.querySource as TrendsQuery)?.series || []) { - if (series.math === BaseMathType.WeeklyActiveUsers) { - activeUsersMath = BaseMathType.WeeklyActiveUsers - break - } - - if (series.math === BaseMathType.MonthlyActiveUsers) { - activeUsersMath = BaseMathType.MonthlyActiveUsers - break - } - } - - const enabledIntervals: Intervals = { ...intervals } - - if (activeUsersMath) { - // Disallow grouping by hour for WAUs/MAUs as it's an expensive query that produces a view that's not useful for users - enabledIntervals.hour = { - ...enabledIntervals.hour, - disabledReason: - 'Grouping by hour is not supported on insights with weekly or monthly active users series.', - } - - // Disallow grouping by month for WAUs as the resulting view is misleading to users - if (activeUsersMath === BaseMathType.WeeklyActiveUsers) { - enabledIntervals.month = { - ...enabledIntervals.month, - disabledReason: - 'Grouping by month is not supported on insights with weekly active users series.', - } - } - } - - actions.setEnabledIntervals(enabledIntervals) - - // If the user just flipped an event action to use WAUs/MAUs math and their - // current interval is unsupported by the math type, switch their interval - // to an appropriate allowed interval and inform them of the change via a toast - if ( - activeUsersMath && - (values.querySource as TrendsQuery)?.interval && - enabledIntervals[(values.querySource as TrendsQuery).interval as IntervalType].disabledReason - ) { - if (values.interval === 'hour') { - lemonToast.info( - `Switched to grouping by day, because "${BASE_MATH_DEFINITIONS[activeUsersMath].name}" does not support grouping by ${values.interval}.` - ) - actions.updateQuerySource({ interval: 'day' } as Partial) - } else { - lemonToast.info( - `Switched to grouping by week, because "${BASE_MATH_DEFINITIONS[activeUsersMath].name}" does not support grouping by ${values.interval}.` - ) - actions.updateQuerySource({ interval: 'week' } as Partial) - } - return - } - - if ( - !date_from || - (objectsEqual(date_from, previousDateRange.date_from) && - objectsEqual(date_to, previousDateRange.date_to)) - ) { - return - } - - // automatically set an interval for fixed date ranges - if ( - date_from && - date_to && - dayjs(querySource.dateRange?.date_from).isValid() && - dayjs(querySource.dateRange?.date_to).isValid() - ) { - if (dayjs(date_to).diff(dayjs(date_from), 'day') <= 3) { - actions.updateQuerySource({ interval: 'hour' } as Partial) - } else if (dayjs(date_to).diff(dayjs(date_from), 'month') <= 3) { - actions.updateQuerySource({ interval: 'day' } as Partial) - } else { - actions.updateQuerySource({ interval: 'month' } as Partial) - } - return - } - // get a defaultInterval for dateOptions that have a default value - let interval: IntervalType = 'day' - for (const { key, values, defaultInterval } of dateMapping) { - if ( - values[0] === date_from && - values[1] === (date_to || undefined) && - key !== 'Custom' && - defaultInterval - ) { - interval = defaultInterval - break - } - } - actions.updateQuerySource({ interval } as Partial) - }, - })), -]) diff --git a/frontend/src/scenes/insights/filters/InsightDateFilter/InsightDateFilter.tsx b/frontend/src/scenes/insights/filters/InsightDateFilter/InsightDateFilter.tsx index 1076b578691e8..1412a7d7a0e3d 100644 --- a/frontend/src/scenes/insights/filters/InsightDateFilter/InsightDateFilter.tsx +++ b/frontend/src/scenes/insights/filters/InsightDateFilter/InsightDateFilter.tsx @@ -1,9 +1,9 @@ import { useValues, useActions } from 'kea' -import { insightDateFilterLogic } from './insightDateFilterLogic' import { DateFilter } from 'lib/components/DateFilter/DateFilter' import { insightLogic } from 'scenes/insights/insightLogic' import { CalendarOutlined, InfoCircleOutlined } from '@ant-design/icons' import { Tooltip } from 'antd' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' type InsightDateFilterProps = { disabled: boolean @@ -11,18 +11,16 @@ type InsightDateFilterProps = { export function InsightDateFilter({ disabled }: InsightDateFilterProps): JSX.Element { const { insightProps } = useValues(insightLogic) - const { - dates: { dateFrom, dateTo }, - } = useValues(insightDateFilterLogic(insightProps)) - const { setDates } = useActions(insightDateFilterLogic(insightProps)) + const { dateRange } = useValues(insightVizDataLogic(insightProps)) + const { updateDateRange } = useActions(insightVizDataLogic(insightProps)) return ( { - setDates(changedDateFrom, changedDateTo) + onChange={(date_from, date_to) => { + updateDateRange({ date_from, date_to }) }} makeLabel={(key) => ( <> diff --git a/frontend/src/scenes/insights/filters/InsightDateFilter/insightDateFilterLogic.ts b/frontend/src/scenes/insights/filters/InsightDateFilter/insightDateFilterLogic.ts deleted file mode 100644 index 76c8b4da0321c..0000000000000 --- a/frontend/src/scenes/insights/filters/InsightDateFilter/insightDateFilterLogic.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { kea, props, key, path, connect, actions, selectors, listeners } from 'kea' -import type { insightDateFilterLogicType } from './insightDateFilterLogicType' -import { InsightLogicProps } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' - -export const insightDateFilterLogic = kea([ - props({} as InsightLogicProps), - key(keyForInsightLogicProps('new')), - path((key) => ['scenes', 'insights', 'InsightDateFilter', 'insightDateFilterLogic', key]), - connect((props: InsightLogicProps) => ({ - actions: [insightVizDataLogic(props), ['updateQuerySource']], - values: [insightVizDataLogic(props), ['dateRange']], - })), - actions(() => ({ - setDates: (dateFrom: string | undefined | null, dateTo: string | undefined | null) => ({ - dateFrom, - dateTo, - }), - })), - selectors({ - dates: [ - (s) => [s.dateRange], - (dateRange) => ({ dateFrom: dateRange?.date_from || null, dateTo: dateRange?.date_to || null }), - ], - }), - listeners(({ actions }) => ({ - setDates: ({ dateFrom, dateTo }) => { - actions.updateQuerySource({ - dateRange: { - date_from: dateFrom || null, - date_to: dateTo || null, - }, - }) - }, - })), -]) diff --git a/frontend/src/scenes/insights/insightCommandLogic.ts b/frontend/src/scenes/insights/insightCommandLogic.ts index 21141090ba69a..7a46c062c51d2 100644 --- a/frontend/src/scenes/insights/insightCommandLogic.ts +++ b/frontend/src/scenes/insights/insightCommandLogic.ts @@ -6,7 +6,7 @@ import { RiseOutlined } from '@ant-design/icons' import { dateMapping } from 'lib/utils' import { InsightLogicProps } from '~/types' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { insightDateFilterLogic } from 'scenes/insights/filters/InsightDateFilter/insightDateFilterLogic' +import { insightVizDataLogic } from './insightVizDataLogic' const INSIGHT_COMMAND_SCOPE = 'insights' @@ -15,11 +15,7 @@ export const insightCommandLogic = kea([ key(keyForInsightLogicProps('new')), path((key) => ['scenes', 'insights', 'insightCommandLogic', key]), - connect((props: InsightLogicProps) => [ - commandPaletteLogic, - compareFilterLogic(props), - insightDateFilterLogic(props), - ]), + connect((props: InsightLogicProps) => [commandPaletteLogic, compareFilterLogic(props), insightVizDataLogic(props)]), events(({ props }) => ({ afterMount: () => { const funnelCommands: Command[] = [ @@ -37,7 +33,10 @@ export const insightCommandLogic = kea([ icon: RiseOutlined, display: `Set Time Range to ${key}`, executor: () => { - insightDateFilterLogic(props).actions.setDates(values[0], values[1]) + insightVizDataLogic(props).actions.updateDateRange({ + date_from: values[0], + date_to: values[1], + }) }, })), ], diff --git a/frontend/src/scenes/insights/insightVizDataLogic.test.ts b/frontend/src/scenes/insights/insightVizDataLogic.test.ts index 55e98f7dec9cb..1eb6bac3b5ddc 100644 --- a/frontend/src/scenes/insights/insightVizDataLogic.test.ts +++ b/frontend/src/scenes/insights/insightVizDataLogic.test.ts @@ -1,14 +1,14 @@ import { expectLogic } from 'kea-test-utils' import { initKeaTests } from '~/test/init' -import { ChartDisplayType, InsightShortId } from '~/types' +import { BaseMathType, ChartDisplayType, InsightShortId } from '~/types' import { insightDataLogic } from './insightDataLogic' import { useMocks } from '~/mocks/jest' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { trendsQueryDefault, funnelsQueryDefault } from '~/queries/nodes/InsightQuery/defaults' -import { NodeKind } from '~/queries/schema' +import { NodeKind, TrendsQuery } from '~/queries/schema' import { FunnelLayout } from 'lib/constants' const Insight123 = '123' as InsightShortId @@ -90,6 +90,7 @@ describe('insightVizDataLogic', () => { kind: NodeKind.InsightVizNode, source: { ...trendsQueryDefault, + interval: 'day', // side effect dateRange: { date_from: '-7d', date_to: null, @@ -113,6 +114,7 @@ describe('insightVizDataLogic', () => { kind: NodeKind.InsightVizNode, source: { ...trendsQueryDefault, + interval: 'day', // side effect dateRange: { date_from: '-7d', date_to: '-3d', @@ -249,4 +251,102 @@ describe('insightVizDataLogic', () => { }) }) }) + + describe('activeUsersMath', () => { + it('returns null without active users math', () => { + expectLogic(builtInsightVizDataLogic, () => { + builtInsightVizDataLogic.actions.updateQuerySource({ + series: [ + { + kind: NodeKind.EventsNode, + name: '$pageview', + event: '$pageview', + math: BaseMathType.TotalCount, + }, + ], + } as Partial) + }).toMatchValues({ activeUsersMath: null }) + }) + + it('returns weekly active users math type', () => { + expectLogic(builtInsightVizDataLogic, () => { + builtInsightVizDataLogic.actions.updateQuerySource({ + series: [ + { + kind: NodeKind.EventsNode, + name: '$pageview', + event: '$pageview', + math: BaseMathType.WeeklyActiveUsers, + }, + ], + } as Partial) + }).toMatchValues({ activeUsersMath: BaseMathType.WeeklyActiveUsers }) + }) + + it('returns monthly active users math type', () => { + expectLogic(builtInsightVizDataLogic, () => { + builtInsightVizDataLogic.actions.updateQuerySource({ + series: [ + { + kind: NodeKind.EventsNode, + name: '$pageview', + event: '$pageview', + math: BaseMathType.TotalCount, + }, + { + kind: NodeKind.EventsNode, + name: '$pageview', + event: '$pageview', + math: BaseMathType.MonthlyActiveUsers, + }, + ], + } as Partial) + }).toMatchValues({ activeUsersMath: BaseMathType.MonthlyActiveUsers }) + }) + }) + + describe('enabledIntervals', () => { + it('returns all intervals', () => { + expectLogic(builtInsightVizDataLogic).toMatchValues({ + enabledIntervals: { + day: { label: 'day', newDateFrom: undefined }, + hour: { label: 'hour', newDateFrom: 'dStart' }, + month: { label: 'month', newDateFrom: '-90d' }, + week: { label: 'week', newDateFrom: '-30d' }, + }, + }) + }) + + it('adds a disabled reason with active users math', () => { + expectLogic(builtInsightVizDataLogic, () => { + builtInsightVizDataLogic.actions.updateQuerySource({ + series: [ + { + kind: 'EventsNode', + name: '$pageview', + event: '$pageview', + math: BaseMathType.WeeklyActiveUsers, + }, + ], + } as Partial) + }).toMatchValues({ + enabledIntervals: { + day: { label: 'day', newDateFrom: undefined }, + hour: { + label: 'hour', + newDateFrom: 'dStart', + disabledReason: + 'Grouping by hour is not supported on insights with weekly or monthly active users series.', + }, + month: { + label: 'month', + newDateFrom: '-90d', + disabledReason: + 'Grouping by month is not supported on insights with weekly active users series.', + }, + week: { label: 'week', newDateFrom: '-30d' }, + }, + }) + }) + }) }) diff --git a/frontend/src/scenes/insights/insightVizDataLogic.ts b/frontend/src/scenes/insights/insightVizDataLogic.ts index a15328138ccef..9a4439294ccb5 100644 --- a/frontend/src/scenes/insights/insightVizDataLogic.ts +++ b/frontend/src/scenes/insights/insightVizDataLogic.ts @@ -1,6 +1,6 @@ import posthog from 'posthog-js' import { actions, connect, kea, key, listeners, path, props, selectors, reducers } from 'kea' -import { ChartDisplayType, InsightLogicProps } from '~/types' +import { BaseMathType, ChartDisplayType, InsightLogicProps, IntervalType } from '~/types' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { BreakdownFilter, @@ -41,6 +41,7 @@ import { getShowValueOnSeries, } from '~/queries/nodes/InsightViz/utils' import { DISPLAY_TYPES_WITHOUT_LEGEND } from 'lib/components/InsightLegend/utils' +import { Intervals, intervals } from 'lib/components/IntervalFilter/intervals' import { insightDataLogic, queryFromKind } from 'scenes/insights/insightDataLogic' import { sceneLogic } from 'scenes/sceneLogic' @@ -48,9 +49,15 @@ import { sceneLogic } from 'scenes/sceneLogic' import type { insightVizDataLogicType } from './insightVizDataLogicType' import { parseProperties } from 'lib/components/PropertyFilters/utils' import { filterTestAccountsDefaultsLogic } from 'scenes/project/Settings/filterTestAccountDefaultsLogic' +import { BASE_MATH_DEFINITIONS } from 'scenes/trends/mathsLogic' +import { lemonToast } from '@posthog/lemon-ui' +import { dayjs } from 'lib/dayjs' +import { dateMapping } from 'lib/utils' const SHOW_TIMEOUT_MESSAGE_AFTER = 5000 +export type QuerySourceUpdate = Omit, 'kind'> + export const insightVizDataLogic = kea([ props({} as InsightLogicProps), key(keyForInsightLogicProps('new')), @@ -73,7 +80,7 @@ export const insightVizDataLogic = kea([ actions({ saveInsight: (redirectToViewMode = true) => ({ redirectToViewMode }), - updateQuerySource: (querySource: Omit, 'kind'>) => ({ querySource }), + updateQuerySource: (querySource: QuerySourceUpdate) => ({ querySource }), updateInsightFilter: (insightFilter: InsightFilter) => ({ insightFilter }), updateDateRange: (dateRange: DateRange) => ({ dateRange }), updateBreakdown: (breakdown: BreakdownFilter) => ({ breakdown }), @@ -184,6 +191,38 @@ export const insightVizDataLogic = kea([ hasFormula: [(s) => [s.formula], (formula) => formula !== undefined], + activeUsersMath: [ + (s) => [s.series], + (series): BaseMathType.MonthlyActiveUsers | BaseMathType.WeeklyActiveUsers | null => + getActiveUsersMath(series), + ], + enabledIntervals: [ + (s) => [s.activeUsersMath], + (activeUsersMath) => { + const enabledIntervals: Intervals = { ...intervals } + + if (activeUsersMath) { + // Disallow grouping by hour for WAUs/MAUs as it's an expensive query that produces a view that's not useful for users + enabledIntervals.hour = { + ...enabledIntervals.hour, + disabledReason: + 'Grouping by hour is not supported on insights with weekly or monthly active users series.', + } + + // Disallow grouping by month for WAUs as the resulting view is misleading to users + if (activeUsersMath === BaseMathType.WeeklyActiveUsers) { + enabledIntervals.month = { + ...enabledIntervals.month, + disabledReason: + 'Grouping by month is not supported on insights with weekly active users series.', + } + } + } + + return enabledIntervals + }, + ], + erroredQueryId: [ (s) => [s.insightDataError], (insightDataError) => { @@ -213,7 +252,10 @@ export const insightVizDataLogic = kea([ updateQuerySource: ({ querySource }) => { actions.setQuery({ ...values.query, - source: { ...values.querySource, ...querySource }, + source: { + ...values.querySource, + ...handleQuerySourceUpdateSideEffects(querySource, values.querySource as InsightQueryNode), + }, } as Node) }, setQuery: ({ query }) => { @@ -249,3 +291,83 @@ export const insightVizDataLogic = kea([ }, })), ]) + +const getActiveUsersMath = ( + series: TrendsQuery['series'] | null | undefined +): BaseMathType.WeeklyActiveUsers | BaseMathType.MonthlyActiveUsers | null => { + for (const seriesItem of series || []) { + if (seriesItem.math === BaseMathType.WeeklyActiveUsers) { + return BaseMathType.WeeklyActiveUsers + } + + if (seriesItem.math === BaseMathType.MonthlyActiveUsers) { + return BaseMathType.MonthlyActiveUsers + } + } + + return null +} + +const handleQuerySourceUpdateSideEffects = ( + update: QuerySourceUpdate, + currentState: InsightQueryNode +): QuerySourceUpdate => { + const mergedUpdate = { ...update } + + const maybeChangedSeries = (update as TrendsQuery).series || null + const maybeChangedActiveUsersMath = maybeChangedSeries ? getActiveUsersMath(maybeChangedSeries) : null + const interval = (currentState as TrendsQuery).interval + + // If the user just flipped an event action to use WAUs/MAUs math and their + // current interval is unsupported by the math type, switch their interval + // to an appropriate allowed interval and inform them of the change via a toast + if (maybeChangedActiveUsersMath !== null && (interval === 'hour' || interval === 'month')) { + if (interval === 'hour') { + lemonToast.info( + `Switched to grouping by day, because "${BASE_MATH_DEFINITIONS[maybeChangedActiveUsersMath].name}" does not support grouping by ${interval}.` + ) + ;(mergedUpdate as Partial).interval = 'day' + } else if (interval === 'month' && maybeChangedActiveUsersMath === BaseMathType.WeeklyActiveUsers) { + lemonToast.info( + `Switched to grouping by week, because "${BASE_MATH_DEFINITIONS[maybeChangedActiveUsersMath].name}" does not support grouping by ${interval}.` + ) + ;(mergedUpdate as Partial).interval = 'week' + } + } + + if ( + update.dateRange && + update.dateRange.date_from && + (update.dateRange.date_from !== currentState.dateRange?.date_from || + update.dateRange.date_to !== currentState.dateRange?.date_to) + ) { + const { date_from, date_to } = { ...currentState.dateRange, ...update.dateRange } + + if (date_from && date_to && dayjs(date_from).isValid() && dayjs(date_to).isValid()) { + if (dayjs(date_to).diff(dayjs(date_from), 'day') <= 3) { + ;(mergedUpdate as Partial).interval = 'hour' + } else if (dayjs(date_to).diff(dayjs(date_from), 'month') <= 3) { + ;(mergedUpdate as Partial).interval = 'day' + } else { + ;(mergedUpdate as Partial).interval = 'month' + } + } else { + // get a defaultInterval for dateOptions that have a default value + let newDefaultInterval: IntervalType = 'day' + for (const { key, values, defaultInterval } of dateMapping) { + if ( + values[0] === date_from && + values[1] === (date_to || undefined) && + key !== 'Custom' && + defaultInterval + ) { + newDefaultInterval = defaultInterval + break + } + } + ;(mergedUpdate as Partial).interval = newDefaultInterval + } + } + + return mergedUpdate +}