diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 8b634e8387dec..b40b6792c4216 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -248,6 +248,7 @@ const CONST = { POPOVER_MENU_MAX_HEIGHT: 496, POPOVER_MENU_MAX_HEIGHT_MOBILE: 432, POPOVER_DATE_WIDTH: 338, + POPOVER_DATE_RANGE_WIDTH: 672, POPOVER_DATE_MAX_HEIGHT: 366, POPOVER_DATE_MIN_HEIGHT: 322, TOOLTIP_ANIMATION_DURATION: 500, @@ -7408,6 +7409,7 @@ const CONST = { EQUAL_TO: 'eq', CONTAINS: 'contains', NOT_EQUAL_TO: 'neq', + RANGE: 'range', GREATER_THAN: 'gt', GREATER_THAN_OR_EQUAL_TO: 'gte', LOWER_THAN: 'lt', @@ -7482,6 +7484,7 @@ const CONST = { ON_PREFIX: 'reportFieldOn-', AFTER_PREFIX: 'reportFieldAfter-', BEFORE_PREFIX: 'reportFieldBefore-', + RANGE_PREFIX: 'reportFieldRange-', }, TAG_EMPTY_VALUE: 'none', CATEGORY_EMPTY_VALUE: 'none', @@ -7610,6 +7613,7 @@ const CONST = { ON: 'On', AFTER: 'After', BEFORE: 'Before', + RANGE: 'Range', }, AMOUNT_MODIFIERS: { LESS_THAN: 'LessThan', diff --git a/src/components/DatePicker/CalendarPicker/Day.tsx b/src/components/DatePicker/CalendarPicker/Day.tsx index 3db884bb7fdc0..ed57ddb5bf025 100644 --- a/src/components/DatePicker/CalendarPicker/Day.tsx +++ b/src/components/DatePicker/CalendarPicker/Day.tsx @@ -27,13 +27,9 @@ function Day({disabled, selected, pressed, hovered, children}: DayProps) { const StyleUtils = useStyleUtils(); return ( - {children} + {children} ); } diff --git a/src/components/DatePicker/CalendarPicker/index.tsx b/src/components/DatePicker/CalendarPicker/index.tsx index b520650c8c74c..0364dc61c5ec9 100644 --- a/src/components/DatePicker/CalendarPicker/index.tsx +++ b/src/components/DatePicker/CalendarPicker/index.tsx @@ -1,6 +1,7 @@ import {addMonths, endOfDay, endOfMonth, format, getYear, isSameDay, parseISO, setDate, setYear, startOfDay, startOfMonth, subMonths} from 'date-fns'; import {Str} from 'expensify-common'; import React, {useCallback, useEffect, useRef, useState} from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; @@ -35,6 +36,9 @@ type CalendarPickerProps = { /** A function called when the date is selected */ onSelected?: (selectedDate: string) => void; + + /** Optional style override for the header container */ + headerContainerStyle?: StyleProp; }; function getInitialCurrentDateView(value: Date | string, minDate: Date, maxDate: Date) { @@ -56,6 +60,7 @@ function CalendarPicker({ onSelected, DayComponent = Day, selectableDates, + headerContainerStyle, }: CalendarPickerProps) { // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth} = useResponsiveLayout(); @@ -175,13 +180,14 @@ function CalendarPicker({ const webOnlyMarginStyle = isSmallScreenWidth ? {} : styles.mh1; const calendarContainerStyle = isSmallScreenWidth ? [webOnlyMarginStyle, themeStyles.calendarBodyContainer] : [webOnlyMarginStyle, animatedStyle]; + const headerPaddingStyle = headerContainerStyle ?? themeStyles.ph5; const getAccessibilityState = useCallback((isSelected: boolean) => ({selected: isSelected}), []); return ( (null); + const [shouldShowRangeError, setShouldShowRangeError] = useState(false); const dateOnKey = dateKey.startsWith(CONST.SEARCH.REPORT_FIELD.GLOBAL_PREFIX) ? (dateKey.replace(CONST.SEARCH.REPORT_FIELD.DEFAULT_PREFIX, CONST.SEARCH.REPORT_FIELD.ON_PREFIX) as ReportFieldDateKey) @@ -43,19 +47,36 @@ function DateFilterBase({title, dateKey, back, onSubmit}: DateFilterBaseProps) { ? (dateKey.replace(CONST.SEARCH.REPORT_FIELD.DEFAULT_PREFIX, CONST.SEARCH.REPORT_FIELD.AFTER_PREFIX) as ReportFieldDateKey) : (`${dateKey}${CONST.SEARCH.DATE_MODIFIERS.AFTER}` as const); + const dateRangeKey = dateKey.startsWith(CONST.SEARCH.REPORT_FIELD.GLOBAL_PREFIX) + ? (`${CONST.SEARCH.REPORT_FIELD.RANGE_PREFIX}${dateKey.replace(CONST.SEARCH.REPORT_FIELD.DEFAULT_PREFIX, '')}` as ReportFieldDateKey) + : (`${dateKey}${CONST.SEARCH.DATE_MODIFIERS.RANGE}` as const); + const dateOnValue = searchAdvancedFiltersForm?.[dateOnKey]; const dateBeforeValue = searchAdvancedFiltersForm?.[dateBeforeKey]; const dateAfterValue = searchAdvancedFiltersForm?.[dateAfterKey]; + const dateRangeValue = searchAdvancedFiltersForm?.[dateRangeKey]; const defaultDateValues = useMemo( () => ({ [CONST.SEARCH.DATE_MODIFIERS.ON]: dateOnValue, [CONST.SEARCH.DATE_MODIFIERS.BEFORE]: dateBeforeValue, [CONST.SEARCH.DATE_MODIFIERS.AFTER]: dateAfterValue, + [CONST.SEARCH.DATE_MODIFIERS.RANGE]: dateRangeValue, }), - [dateAfterValue, dateBeforeValue, dateOnValue], + [dateAfterValue, dateBeforeValue, dateOnValue, dateRangeValue], + ); + const [rangeDisplayText, setRangeDisplayText] = useState(() => + getDateRangeDisplayValueFromFormValue( + defaultDateValues[CONST.SEARCH.DATE_MODIFIERS.RANGE], + defaultDateValues[CONST.SEARCH.DATE_MODIFIERS.AFTER], + defaultDateValues[CONST.SEARCH.DATE_MODIFIERS.BEFORE], + ), ); + const handleDateValuesChange = useCallback(() => { + setRangeDisplayText(searchDatePresetFilterBaseRef.current?.getRangeDisplayText() ?? ''); + }, []); + const presets = useMemo(() => { const hasFeed = !!searchAdvancedFiltersForm?.feed?.length; return getDatePresets(dateKey, hasFeed); @@ -76,11 +97,14 @@ function DateFilterBase({title, dateKey, back, onSubmit}: DateFilterBaseProps) { if (selectedDateModifier) { searchDatePresetFilterBaseRef.current.clearDateValueOfSelectedDateModifier(); + setRangeDisplayText(searchDatePresetFilterBaseRef.current.getRangeDisplayText()); setSelectedDateModifier(null); + setShouldShowRangeError(false); return; } searchDatePresetFilterBaseRef.current.clearDateValues(); + setRangeDisplayText(''); }, [selectedDateModifier]); const save = useCallback(() => { @@ -89,36 +113,49 @@ function DateFilterBase({title, dateKey, back, onSubmit}: DateFilterBaseProps) { } if (selectedDateModifier) { + if (!searchDatePresetFilterBaseRef.current.validate()) { + return; + } + searchDatePresetFilterBaseRef.current.setDateValueOfSelectedDateModifier(); + setRangeDisplayText(searchDatePresetFilterBaseRef.current.getRangeDisplayText()); setSelectedDateModifier(null); + setShouldShowRangeError(false); return; } const dateValues = searchDatePresetFilterBaseRef.current.getDateValues(); - onSubmit({ [dateOnKey]: dateValues[CONST.SEARCH.DATE_MODIFIERS.ON] ?? null, [dateBeforeKey]: dateValues[CONST.SEARCH.DATE_MODIFIERS.BEFORE] ?? null, [dateAfterKey]: dateValues[CONST.SEARCH.DATE_MODIFIERS.AFTER] ?? null, + [dateRangeKey]: dateValues[CONST.SEARCH.DATE_MODIFIERS.RANGE] ?? null, }); - }, [selectedDateModifier, dateOnKey, dateBeforeKey, dateAfterKey, onSubmit]); + }, [selectedDateModifier, dateOnKey, dateBeforeKey, dateAfterKey, dateRangeKey, onSubmit]); const goBack = () => { if (selectedDateModifier) { + if (searchDatePresetFilterBaseRef.current && selectedDateModifier === CONST.SEARCH.DATE_MODIFIERS.RANGE) { + searchDatePresetFilterBaseRef.current.resetDateValuesToDefault(); + setRangeDisplayText(searchDatePresetFilterBaseRef.current.getRangeDisplayText()); + } setSelectedDateModifier(null); + setShouldShowRangeError(false); return; } back(); }; + const hasRangeInput = !!rangeDisplayText; + return ( - <> + - + + {selectedDateModifier === CONST.SEARCH.DATE_MODIFIERS.RANGE && hasRangeInput && ( + + {`${translate('common.range')}: `} + {rangeDisplayText} + + )} + +