diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx b/superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx index 4898ba8fdef51..d4ddecad9a25b 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx @@ -45,6 +45,7 @@ import { FRAME_OPTIONS, guessFrame, useDefaultTimeFilter, + useTimeFilterShortcuts, } from './utils'; import { CommonFrame, @@ -52,6 +53,7 @@ import { CustomFrame, AdvancedFrame, DateLabel, + ShortcutsFrame, } from './components'; const StyledRangeType = styled(Select)` @@ -159,12 +161,13 @@ export default function DateFilterLabel(props: DateFilterControlProps) { isOverflowingFilterBar = false, } = props; const defaultTimeFilter = useDefaultTimeFilter(); + const shortcuts = useTimeFilterShortcuts(); const value = props.value ?? defaultTimeFilter; const [actualTimeRange, setActualTimeRange] = useState(value); const [show, setShow] = useState(false); - const guessedFrame = useMemo(() => guessFrame(value), [value]); + const guessedFrame = useMemo(() => guessFrame(value, shortcuts), [value]); const [frame, setFrame] = useState(guessedFrame); const [lastFetchedTimeRange, setLastFetchedTimeRange] = useState(value); const [timeRangeValue, setTimeRangeValue] = useState(value); @@ -302,6 +305,9 @@ export default function DateFilterLabel(props: DateFilterControlProps) { {frame === 'Custom' && ( )} + {frame === 'Shortcuts' && ( + + )} {frame === 'No filter' && (
)} diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/components/ShortcutsFrame.tsx b/superset-frontend/src/explore/components/controls/DateFilterControl/components/ShortcutsFrame.tsx new file mode 100644 index 0000000000000..cb9b7a9182d74 --- /dev/null +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/components/ShortcutsFrame.tsx @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { t } from '@superset-ui/core'; +import { Radio } from 'src/components/Radio'; +import { useTimeFilterShortcuts } from 'src/explore/components/controls/DateFilterControl/utils'; +import { FrameComponentProps } from 'src/explore/components/controls/DateFilterControl/types'; + +export function ShortcutsFrame(props: FrameComponentProps) { + const shortCuts = useTimeFilterShortcuts(); + + return ( + <> +
{t('Configure Time Range:')}
+ props.onChange(e.target.value)} + > + {shortCuts.map(([key, value]: [string, string]) => ( + + {key} + + ))} + + + ); +} diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/components/index.ts b/superset-frontend/src/explore/components/controls/DateFilterControl/components/index.ts index 0d46ee7a97d19..7a1b49e76f071 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/components/index.ts +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/components/index.ts @@ -21,3 +21,4 @@ export { CalendarFrame } from './CalendarFrame'; export { CustomFrame } from './CustomFrame'; export { AdvancedFrame } from './AdvancedFrame'; export { DateLabel } from './DateLabel'; +export { ShortcutsFrame } from './ShortcutsFrame'; diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/types.ts b/superset-frontend/src/explore/components/controls/DateFilterControl/types.ts index ce5e8127698cb..4c7d7a426fab1 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/types.ts +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/types.ts @@ -26,7 +26,8 @@ export type FrameType = | 'Calendar' | 'Custom' | 'Advanced' - | 'No filter'; + | 'No filter' + | 'Shortcuts'; export type DateTimeGrainType = | 'second' @@ -98,4 +99,5 @@ export interface DateFilterControlProps { onClosePopover?: () => void; overlayStyle?: 'Modal' | 'Popover'; isOverflowingFilterBar?: boolean; + timeFilterShortcuts?: [string, string][]; } diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts b/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts index dad77fb9cb821..1faa46bc2c7f2 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts @@ -32,6 +32,7 @@ export const FRAME_OPTIONS: SelectOptionType[] = [ { value: 'Calendar', label: t('Previous') }, { value: 'Custom', label: t('Custom') }, { value: 'Advanced', label: t('Advanced') }, + { value: 'Shortcuts', label: t('Shortcuts') }, { value: 'No filter', label: t('No filter') }, ]; diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/utils/dateFilterUtils.ts b/superset-frontend/src/explore/components/controls/DateFilterControl/utils/dateFilterUtils.ts index 08162cedf02d0..213bb3f36faa5 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/utils/dateFilterUtils.ts +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/utils/dateFilterUtils.ts @@ -47,7 +47,10 @@ export const formatTimeRange = ( )} ≤ ${columnPlaceholder} < ${formatDateEndpoint(splitDateRange[1])}`; }; -export const guessFrame = (timeRange: string): FrameType => { +export const guessFrame = ( + timeRange: string, + shortcuts: [string, string][], +): FrameType => { if (COMMON_RANGE_VALUES_SET.has(timeRange)) { return 'Common'; } @@ -60,6 +63,9 @@ export const guessFrame = (timeRange: string): FrameType => { if (customTimeRangeDecode(timeRange).matchedFlag) { return 'Custom'; } + if (shortcuts.filter(s => s[1] === timeRange)) { + return 'Shortcuts'; + } return 'Advanced'; }; @@ -93,3 +99,11 @@ export function useDefaultTimeFilter() { ) ?? NO_TIME_RANGE ); } + +export function useTimeFilterShortcuts(): [string, string][] { + return ( + useSelector( + (state: JsonObject) => state?.common?.conf?.TIME_FILTER_SHORTCUTS, + ) ?? [] + ); +} diff --git a/superset/config.py b/superset/config.py index 401dfd2f3d995..30839d42af9f5 100644 --- a/superset/config.py +++ b/superset/config.py @@ -153,6 +153,11 @@ def _try_json_readsha(filepath: str, length: int) -> str | None: # default time filter in explore # values may be "Last day", "Last week", " : now", etc. DEFAULT_TIME_FILTER = NO_TIME_RANGE +TIME_FILTER_SHORTCUTS = [ + ["Current week", "DATETRUNC(DATETIME('today'), WEEK) : today"], + ["Current month(MTD)", "DATETRUNC(DATETIME('today'), MONTH) : today"], + ["Current year(YTD)", "DATETRUNC(DATETIME('today'), YEAR) : today"], +] SUPERSET_WEBSERVER_PROTOCOL = "http" SUPERSET_WEBSERVER_ADDRESS = "0.0.0.0" diff --git a/superset/views/base.py b/superset/views/base.py index 8f8b4c1648689..68fbcea9ea47b 100644 --- a/superset/views/base.py +++ b/superset/views/base.py @@ -123,6 +123,7 @@ "NATIVE_FILTER_DEFAULT_ROW_LIMIT", "PREVENT_UNSAFE_DEFAULT_URLS_ON_DATASET", "JWT_ACCESS_CSRF_COOKIE_NAME", + "TIME_FILTER_SHORTCUTS", ) logger = logging.getLogger(__name__)