From b33b38cb02a20818b2d93c7b68a95194d2f21a3e Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Mon, 3 Nov 2025 17:51:42 +0100 Subject: [PATCH] Follow-up high fixes on date refactor (#15553) This PR fixes important bugs on date filter handling following-up date refactor. Fixes : https://github.com/twentyhq/core-team-issues/issues/1814 --- ...alueInputDropdownButtonClickableSelect.tsx | 7 +- .../ObjectFilterDropdownDateInput.tsx | 8 -- .../ObjectFilterDropdownDateTimeInput.tsx | 16 +--- .../useApplyObjectFilterDropdownOperand.ts | 8 -- ...seGetNowInUserTimezoneForRelativeFilter.ts | 33 ------- .../hooks/useGetRecordFilterDisplayValue.ts} | 75 +++++++++------ .../record-filter/types/RecordFilter.ts | 1 + .../views/components/EditableFilterChip.tsx | 7 +- ...eComputeRecordRelationFilterLabelValue.tsx | 9 +- .../hooks/useGetRecordFilterChipLabelValue.ts | 33 +++++++ .../stringifyRelativeDateFilter.test.ts | 95 ++----------------- .../utils/stringifyRelativeDateFilter.ts | 12 +-- ...evaluate-relative-date-filter.util.spec.ts | 6 +- .../dates/utils/getStartUnitOfDateTime.ts | 8 ++ .../dates/utils/relativeDateFilterSchema.ts | 1 - .../relativeDateFilterStringifiedSchema.ts | 13 +-- 16 files changed, 119 insertions(+), 213 deletions(-) delete mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useGetNowInUserTimezoneForRelativeFilter.ts rename packages/twenty-front/src/modules/{views/hooks/useGetRecordFilterLabelValue.ts => object-record/record-filter/hooks/useGetRecordFilterDisplayValue.ts} (73%) create mode 100644 packages/twenty-front/src/modules/views/hooks/useGetRecordFilterChipLabelValue.ts diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInputDropdownButtonClickableSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInputDropdownButtonClickableSelect.tsx index f4c4a7b6a6..cb8d64eb33 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInputDropdownButtonClickableSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInputDropdownButtonClickableSelect.tsx @@ -8,6 +8,7 @@ import { isNonEmptyString } from '@sniptt/guards'; import styled from '@emotion/styled'; import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById'; +import { useGetRecordFilterDisplayValue } from '@/object-record/record-filter/hooks/useGetRecordFilterDisplayValue'; import { isDefined } from 'twenty-shared/utils'; // TODO: factorize this with https://github.com/twentyhq/core-team-issues/issues/752 @@ -38,6 +39,8 @@ export const AdvancedFilterValueInputDropdownButtonClickableSelect = ({ currentRecordFiltersComponentState, ); + const { getRecordFilterDisplayValue } = useGetRecordFilterDisplayValue(); + const recordFilter = currentRecordFilters.find( (recordFilter) => recordFilter.id === recordFilterId, ); @@ -56,9 +59,11 @@ export const AdvancedFilterValueInputDropdownButtonClickableSelect = ({ ? getAdvancedFilterInputPlaceholderText(fieldMetadataItem) : 'Enter filter'; + const recordFilterDisplayValue = getRecordFilterDisplayValue(recordFilter); + const advancedFilterInputText = shouldUsePlaceholder ? placeholderText - : (recordFilter?.displayValue ?? ''); + : (recordFilterDisplayValue ?? ''); const isDateTimeType = recordFilter?.type === 'DATE' || recordFilter?.type === 'DATE_TIME'; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput.tsx index b6a5cb3ab3..c7ba9dd460 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput.tsx @@ -6,7 +6,6 @@ import { type NonSystemCalendarStartDay, } from '@/localization/utils/detection/detectCalendarStartDay'; import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; -import { useGetNowInUserTimezoneForRelativeFilter } from '@/object-record/object-filter-dropdown/hooks/useGetNowInUserTimezoneForRelativeFilter'; import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { getRelativeDateDisplayValue } from '@/object-record/object-filter-dropdown/utils/getRelativeDateDisplayValue'; @@ -34,9 +33,6 @@ export const ObjectFilterDropdownDateInput = () => { selectedOperandInDropdownComponentState, ); - const { getNowInUserTimezoneForRelativeFilter } = - useGetNowInUserTimezoneForRelativeFilter(); - const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValue( objectFilterDropdownCurrentRecordFilterComponentState, ); @@ -62,9 +58,6 @@ export const ObjectFilterDropdownDateInput = () => { const handleRelativeDateChange = ( relativeDate: RelativeDateFilter | null, ) => { - const { dayAsStringInUserTimezone } = - getNowInUserTimezoneForRelativeFilter(); - const userDefinedCalendarStartDay = CalendarStartDay[ currentWorkspaceMember?.calendarStartDay ?? CalendarStartDay.SYSTEM @@ -81,7 +74,6 @@ export const ObjectFilterDropdownDateInput = () => { ? stringifyRelativeDateFilter({ ...relativeDate, timezone: timeZone, - referenceDayAsString: dayAsStringInUserTimezone, firstDayOfTheWeek: resolvedCalendarStartDay, }) : ''; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateTimeInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateTimeInput.tsx index 6b57dba421..1e41dc0680 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateTimeInput.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateTimeInput.tsx @@ -6,9 +6,7 @@ import { type NonSystemCalendarStartDay, } from '@/localization/utils/detection/detectCalendarStartDay'; import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; -import { useGetNowInUserTimezoneForRelativeFilter } from '@/object-record/object-filter-dropdown/hooks/useGetNowInUserTimezoneForRelativeFilter'; import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; -import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { getRelativeDateDisplayValue } from '@/object-record/object-filter-dropdown/utils/getRelativeDateDisplayValue'; import { DateTimePicker } from '@/ui/input/components/internal/date/components/DateTimePicker'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; @@ -31,10 +29,6 @@ export const ObjectFilterDropdownDateTimeInput = () => { const dateLocale = useRecoilValue(dateLocaleState); const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); - const selectedOperandInDropdown = useRecoilComponentValue( - selectedOperandInDropdownComponentState, - ); - const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValue( objectFilterDropdownCurrentRecordFilterComponentState, ); @@ -46,9 +40,6 @@ export const ObjectFilterDropdownDateTimeInput = () => { ? resolveDateTimeFilter(objectFilterDropdownCurrentRecordFilter) : null; - const { getNowInUserTimezoneForRelativeFilter } = - useGetNowInUserTimezoneForRelativeFilter(); - const [internalDate, setInternalDate] = useState( initialFilterValue instanceof Date ? initialFilterValue : null, ); @@ -74,9 +65,6 @@ export const ObjectFilterDropdownDateTimeInput = () => { const handleRelativeDateChange = ( relativeDate: RelativeDateFilter | null, ) => { - const { dayAsStringInUserTimezone } = - getNowInUserTimezoneForRelativeFilter(); - const userDefinedCalendarStartDay = CalendarStartDay[ currentWorkspaceMember?.calendarStartDay ?? CalendarStartDay.SYSTEM @@ -93,7 +81,6 @@ export const ObjectFilterDropdownDateTimeInput = () => { ? stringifyRelativeDateFilter({ ...relativeDate, timezone: timeZone, - referenceDayAsString: dayAsStringInUserTimezone, firstDayOfTheWeek: resolvedCalendarStartDay, }) : ''; @@ -106,7 +93,8 @@ export const ObjectFilterDropdownDateTimeInput = () => { }; const isRelativeOperand = - selectedOperandInDropdown === ViewFilterOperand.IS_RELATIVE; + objectFilterDropdownCurrentRecordFilter?.operand === + ViewFilterOperand.IS_RELATIVE; const handleClear = () => { isRelativeOperand diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand.ts index e65c992dea..9e7442e106 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand.ts @@ -1,6 +1,5 @@ import { DATE_OPERANDS_THAT_SHOULD_BE_INITIALIZED_WITH_NOW } from '@/object-record/object-filter-dropdown/constants/DateOperandsThatShouldBeInitializedWithNow'; import { useGetInitialFilterValue } from '@/object-record/object-filter-dropdown/hooks/useGetInitialFilterValue'; -import { useGetNowInUserTimezoneForRelativeFilter } from '@/object-record/object-filter-dropdown/hooks/useGetNowInUserTimezoneForRelativeFilter'; import { useUpsertObjectFilterDropdownCurrentFilter } from '@/object-record/object-filter-dropdown/hooks/useUpsertObjectFilterDropdownCurrentFilter'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; @@ -49,9 +48,6 @@ export const useApplyObjectFilterDropdownOperand = () => { const { userTimezone } = useUserTimezone(); - const { getNowInUserTimezoneForRelativeFilter } = - useGetNowInUserTimezoneForRelativeFilter(); - const applyObjectFilterDropdownOperand = ( newOperand: RecordFilterOperand, ) => { @@ -111,15 +107,11 @@ export const useApplyObjectFilterDropdownOperand = () => { recordFilterToUpsert.displayValue = displayValue; } else if (newOperand === RecordFilterOperand.IS_RELATIVE) { - const { dayAsStringInUserTimezone } = - getNowInUserTimezoneForRelativeFilter(); - const defaultRelativeDate: RelativeDateFilter = { direction: 'THIS' as RelativeDateFilterDirection, amount: 1, unit: 'DAY' as RelativeDateFilterUnit, timezone: userTimezone, - referenceDayAsString: dayAsStringInUserTimezone, }; recordFilterToUpsert.value = diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useGetNowInUserTimezoneForRelativeFilter.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useGetNowInUserTimezoneForRelativeFilter.ts deleted file mode 100644 index 473620b0ff..0000000000 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useGetNowInUserTimezoneForRelativeFilter.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone'; -import { TZDate } from '@date-fns/tz'; -import { format } from 'date-fns'; -import { DATE_TYPE_FORMAT } from 'twenty-shared/constants'; - -export const useGetNowInUserTimezoneForRelativeFilter = () => { - const { userTimezone } = useUserTimezone(); - - const getNowInUserTimezoneForRelativeFilter = () => { - const now = new Date(); - - const nowInUserTimezone = new TZDate( - now.getFullYear(), - now.getMonth(), - now.getDate(), - userTimezone, - ); - - const dayAsStringInUserTimezone = format( - nowInUserTimezone, - DATE_TYPE_FORMAT, - ); - - return { - nowInUserTimezone, - dayAsStringInUserTimezone, - }; - }; - - return { - getNowInUserTimezoneForRelativeFilter, - }; -}; diff --git a/packages/twenty-front/src/modules/views/hooks/useGetRecordFilterLabelValue.ts b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useGetRecordFilterDisplayValue.ts similarity index 73% rename from packages/twenty-front/src/modules/views/hooks/useGetRecordFilterLabelValue.ts rename to packages/twenty-front/src/modules/object-record/record-filter/hooks/useGetRecordFilterDisplayValue.ts index e2ac8d46d3..558b0d618c 100644 --- a/packages/twenty-front/src/modules/views/hooks/useGetRecordFilterLabelValue.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useGetRecordFilterDisplayValue.ts @@ -1,6 +1,5 @@ -import { type FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem'; +import { useGetFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useGetFieldMetadataItemById'; import { useGetDateTimeFilterDisplayValue } from '@/object-record/object-filter-dropdown/hooks/useGetDateTimeFilterDisplayValue'; -import { getOperandLabelShort } from '@/object-record/object-filter-dropdown/utils/getOperandLabel'; import { getRelativeDateDisplayValue } from '@/object-record/object-filter-dropdown/utils/getRelativeDateDisplayValue'; import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand'; @@ -10,8 +9,10 @@ import { UserContext } from '@/users/contexts/UserContext'; import { isValid } from 'date-fns'; import { useContext } from 'react'; import { useRecoilValue } from 'recoil'; +import { type Nullable } from 'twenty-shared/types'; import { getDateFromPlainDate, + isDefined, isEmptinessOperand, parseJson, relativeDateFilterStringifiedSchema, @@ -20,25 +21,30 @@ import { import { dateLocaleState } from '~/localization/states/dateLocaleState'; import { formatDateString } from '~/utils/string/formatDateString'; -export const useGetRecordFilterLabelValue = () => { +// TODO: finish the implementation of this hook to obtain filter display value and remove deprecated display value property +export const useGetRecordFilterDisplayValue = () => { const { dateFormat, timeZone } = useContext(UserContext); const dateLocale = useRecoilValue(dateLocaleState); const { userTimezone } = useUserTimezone(); const { getDateTimeFilterDisplayValue } = useGetDateTimeFilterDisplayValue(); - const getRecordFilterLabelValue = ({ - recordFilter, - fieldMetadataOptions, - }: { - recordFilter: RecordFilter; - fieldMetadataOptions?: FieldMetadataItemOption[]; - }) => { - const operandLabelShort = getOperandLabelShort(recordFilter.operand); + const { getFieldMetadataItemByIdOrThrow } = + useGetFieldMetadataItemByIdOrThrow(); + + const getRecordFilterDisplayValue = ( + recordFilter?: Nullable, + ) => { + if (!isDefined(recordFilter)) { + return ''; + } + + const filterType = recordFilter.type; + const operandIsEmptiness = isEmptinessOperand(recordFilter.operand); const recordFilterIsEmpty = isRecordFilterConsideredEmpty(recordFilter); - if (recordFilter.type === 'DATE') { + if (filterType === 'DATE') { switch (recordFilter.operand) { case RecordFilterOperand.IS: { const date = getDateFromPlainDate(recordFilter.value); @@ -51,7 +57,7 @@ export const useGetRecordFilterLabelValue = () => { ); if (!isValid(date)) { - return `${operandLabelShort}`; + return ''; } const formattedDate = formatDateString({ @@ -61,28 +67,28 @@ export const useGetRecordFilterLabelValue = () => { localeCatalog: dateLocale.localeCatalog, }); - return `${operandLabelShort} ${formattedDate}`; + return `${formattedDate}`; } case RecordFilterOperand.IS_RELATIVE: { const relativeDateFilter = relativeDateFilterStringifiedSchema.safeParse(recordFilter.value); if (!relativeDateFilter.success) { - return `${operandLabelShort}`; + return ``; } const relativeDateDisplayValue = getRelativeDateDisplayValue( relativeDateFilter.data, ); - return `${operandLabelShort} ${relativeDateDisplayValue}`; + return ` ${relativeDateDisplayValue}`; } case RecordFilterOperand.IS_TODAY: case RecordFilterOperand.IS_IN_FUTURE: case RecordFilterOperand.IS_IN_PAST: - return operandLabelShort; + return ''; default: - return `${operandLabelShort} ${recordFilter.displayValue}`; + return ` ${recordFilter.displayValue}`; } } else if (recordFilter.type === 'DATE_TIME') { switch (recordFilter.operand) { @@ -93,28 +99,28 @@ export const useGetRecordFilterLabelValue = () => { const { displayValue } = getDateTimeFilterDisplayValue(pointInTime); - return `${operandLabelShort} ${displayValue}`; + return `${displayValue}`; } case RecordFilterOperand.IS_RELATIVE: { const relativeDateFilter = relativeDateFilterStringifiedSchema.safeParse(recordFilter.value); if (!relativeDateFilter.success) { - return `${operandLabelShort}`; + return ``; } const relativeDateDisplayValue = getRelativeDateDisplayValue( relativeDateFilter.data, ); - return `${operandLabelShort} ${relativeDateDisplayValue}`; + return `${relativeDateDisplayValue}`; } case RecordFilterOperand.IS_TODAY: case RecordFilterOperand.IS_IN_FUTURE: case RecordFilterOperand.IS_IN_PAST: - return operandLabelShort; + return ''; default: - return `${operandLabelShort} ${recordFilter.displayValue}`; + return `${recordFilter.displayValue}`; } } else if ( recordFilter.type === 'SELECT' || @@ -126,24 +132,33 @@ export const useGetRecordFilterLabelValue = () => { return ''; } - const optionLabels = valueArray.map( - (value) => - fieldMetadataOptions?.find((option) => option.value === value)?.label, + const { fieldMetadataItem } = getFieldMetadataItemByIdOrThrow( + recordFilter.fieldMetadataId, ); - return `${operandLabelShort} ${optionLabels.join(', ')}`; + const fieldMetadataItemOptions = fieldMetadataItem.options; + + const optionLabels = valueArray.map( + (value) => + fieldMetadataItemOptions?.find((option) => option.value === value) + ?.label, + ); + + return `${optionLabels.join(', ')}`; } if (!operandIsEmptiness && !recordFilterIsEmpty) { - return `${operandLabelShort} ${recordFilter.displayValue}`; + return `${recordFilter.displayValue}`; } if (operandIsEmptiness) { - return `${operandLabelShort}`; + return ``; } return recordFilter.displayValue; }; - return { getRecordFilterLabelValue }; + return { + getRecordFilterDisplayValue, + }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts index c70a76cf0c..2ea165036e 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts @@ -9,6 +9,7 @@ export type RecordFilter = { id: string; fieldMetadataId: string; value: string; + /** @deprecated We shouldn't implement new features with this field and instead try to create utils to obtain the displayValue of a filter type at runtime */ displayValue: string; type: FilterableAndTSVectorFieldType; recordFilterGroupId?: string; diff --git a/packages/twenty-front/src/modules/views/components/EditableFilterChip.tsx b/packages/twenty-front/src/modules/views/components/EditableFilterChip.tsx index 1fc333670b..959047fd8c 100644 --- a/packages/twenty-front/src/modules/views/components/EditableFilterChip.tsx +++ b/packages/twenty-front/src/modules/views/components/EditableFilterChip.tsx @@ -4,7 +4,7 @@ import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/uti import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { isValidSubFieldName } from '@/settings/data-model/utils/isValidSubFieldName'; import { SortOrFilterChip } from '@/views/components/SortOrFilterChip'; -import { useGetRecordFilterLabelValue } from '@/views/hooks/useGetRecordFilterLabelValue'; +import { useGetRecordFilterChipLabelValue } from '@/views/hooks/useGetRecordFilterChipLabelValue'; import { isNonEmptyString } from '@sniptt/guards'; import { useIcons } from 'twenty-ui/display'; @@ -26,7 +26,7 @@ export const EditableFilterChip = ({ recordFilter.fieldMetadataId, ); - const { getRecordFilterLabelValue } = useGetRecordFilterLabelValue(); + const { getRecordFilterChipLabelValue } = useGetRecordFilterChipLabelValue(); const FieldMetadataItemIcon = getIcon(fieldMetadataItem.icon); @@ -47,9 +47,8 @@ export const EditableFilterChip = ({ : recordFilter.label; const labelKey = `${fieldNameLabel}`; - const labelValue = getRecordFilterLabelValue({ + const labelValue = getRecordFilterChipLabelValue({ recordFilter, - fieldMetadataOptions: fieldMetadataItem.options ?? [], }); return ( diff --git a/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx b/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx index e335bd1405..3c642e08ff 100644 --- a/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx +++ b/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx @@ -4,7 +4,7 @@ import { getFieldMetadataItemByIdOrThrow } from '@/object-metadata/utils/getFiel import { MAX_RECORDS_TO_DISPLAY } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect'; import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect'; -import { useGetRecordFilterLabelValue } from '@/views/hooks/useGetRecordFilterLabelValue'; +import { useGetRecordFilterChipLabelValue } from '@/views/hooks/useGetRecordFilterChipLabelValue'; import { t } from '@lingui/core/macro'; import { @@ -17,12 +17,13 @@ type ObjectFilterDropdownRecordSelectProps = { recordFilter: RecordFilter; }; +// TODO: refactor this with new useGetRecordFilterDisplayValue export const useComputeRecordRelationFilterLabelValue = ({ recordFilter, }: ObjectFilterDropdownRecordSelectProps) => { const { objectMetadataItems } = useObjectMetadataItems(); - const { getRecordFilterLabelValue } = useGetRecordFilterLabelValue(); + const { getRecordFilterChipLabelValue } = useGetRecordFilterChipLabelValue(); if (!isDefined(recordFilter.fieldMetadataId)) { throw new Error('fieldMetadataItemUsedInFilterDropdown is not defined'); @@ -87,13 +88,13 @@ export const useComputeRecordRelationFilterLabelValue = ({ return { labelValue: labelValueItems.length > 0 - ? getRecordFilterLabelValue({ + ? getRecordFilterChipLabelValue({ recordFilter: { ...recordFilter, displayValue: filterDisplayValue, }, }) - : getRecordFilterLabelValue({ + : getRecordFilterChipLabelValue({ recordFilter, }), }; diff --git a/packages/twenty-front/src/modules/views/hooks/useGetRecordFilterChipLabelValue.ts b/packages/twenty-front/src/modules/views/hooks/useGetRecordFilterChipLabelValue.ts new file mode 100644 index 0000000000..d4075cec49 --- /dev/null +++ b/packages/twenty-front/src/modules/views/hooks/useGetRecordFilterChipLabelValue.ts @@ -0,0 +1,33 @@ +import { getOperandLabelShort } from '@/object-record/object-filter-dropdown/utils/getOperandLabel'; +import { useGetRecordFilterDisplayValue } from '@/object-record/record-filter/hooks/useGetRecordFilterDisplayValue'; +import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; +import { isRecordFilterConsideredEmpty } from '@/object-record/record-filter/utils/isRecordFilterConsideredEmpty'; +import { isEmptinessOperand } from 'twenty-shared/utils'; + +export const useGetRecordFilterChipLabelValue = () => { + const { getRecordFilterDisplayValue } = useGetRecordFilterDisplayValue(); + + const getRecordFilterChipLabelValue = ({ + recordFilter, + }: { + recordFilter: RecordFilter; + }) => { + const operandLabelShort = getOperandLabelShort(recordFilter.operand); + const operandIsEmptiness = isEmptinessOperand(recordFilter.operand); + const recordFilterIsEmpty = isRecordFilterConsideredEmpty(recordFilter); + + const recordFilterDisplayValue = getRecordFilterDisplayValue(recordFilter); + + if (!operandIsEmptiness && !recordFilterIsEmpty) { + return `${operandLabelShort} ${recordFilterDisplayValue}`; + } + + if (operandIsEmptiness) { + return `${operandLabelShort}`; + } + + return recordFilterDisplayValue; + }; + + return { getRecordFilterChipLabelValue }; +}; diff --git a/packages/twenty-front/src/modules/views/view-filter-value/utils/__tests__/stringifyRelativeDateFilter.test.ts b/packages/twenty-front/src/modules/views/view-filter-value/utils/__tests__/stringifyRelativeDateFilter.test.ts index 011e2a03df..a65133bb82 100644 --- a/packages/twenty-front/src/modules/views/view-filter-value/utils/__tests__/stringifyRelativeDateFilter.test.ts +++ b/packages/twenty-front/src/modules/views/view-filter-value/utils/__tests__/stringifyRelativeDateFilter.test.ts @@ -160,7 +160,7 @@ describe('stringifyRelativeDateFilter', () => { }); describe('timezone handling', () => { - it('should append timezone when provided', () => { + it('should append timezone when provided and default first day of the week', () => { const filter: RelativeDateFilter = { direction: 'PAST', amount: 5, @@ -169,7 +169,7 @@ describe('stringifyRelativeDateFilter', () => { }; expect(stringifyRelativeDateFilter(filter)).toBe( - 'PAST_5_DAY;;America/New_York;;', + 'PAST_5_DAY;;America/New_York;;MONDAY;;', ); }); @@ -206,74 +206,18 @@ describe('stringifyRelativeDateFilter', () => { }); }); - describe('referenceDayAsString handling', () => { - it('should append referenceDayAsString when timezone is provided', () => { - const filter: RelativeDateFilter = { - direction: 'PAST', - amount: 5, - unit: 'DAY', - timezone: 'America/New_York', - referenceDayAsString: '2024-01-15', - }; - - expect(stringifyRelativeDateFilter(filter)).toBe( - 'PAST_5_DAY;;America/New_York;;2024-01-15;;MONDAY;;', - ); - }); - - it('should not append referenceDayAsString when timezone is not provided', () => { - const filter: RelativeDateFilter = { - direction: 'PAST', - amount: 5, - unit: 'DAY', - referenceDayAsString: '2024-01-15', - }; - - expect(stringifyRelativeDateFilter(filter)).toBe('PAST_5_DAY'); - }); - - it('should not append referenceDayAsString when it is empty', () => { - const filter: RelativeDateFilter = { - direction: 'PAST', - amount: 5, - unit: 'DAY', - timezone: 'America/New_York', - referenceDayAsString: '', - }; - - expect(stringifyRelativeDateFilter(filter)).toBe( - 'PAST_5_DAY;;America/New_York;;', - ); - }); - - it('should not append referenceDayAsString when it is null', () => { - const filter: RelativeDateFilter = { - direction: 'PAST', - amount: 5, - unit: 'DAY', - timezone: 'America/New_York', - referenceDayAsString: null, - }; - - expect(stringifyRelativeDateFilter(filter)).toBe( - 'PAST_5_DAY;;America/New_York;;', - ); - }); - }); - describe('firstDayOfTheWeek handling', () => { - it('should append firstDayOfTheWeek when timezone and referenceDayAsString are provided', () => { + it('should append firstDayOfTheWeek when timezone is provided', () => { const filter: RelativeDateFilter = { direction: 'PAST', amount: 5, unit: 'DAY', timezone: 'America/New_York', - referenceDayAsString: '2024-01-15', firstDayOfTheWeek: 'SUNDAY', }; expect(stringifyRelativeDateFilter(filter)).toBe( - 'PAST_5_DAY;;America/New_York;;2024-01-15;;SUNDAY;;', + 'PAST_5_DAY;;America/New_York;;SUNDAY;;', ); }); @@ -285,11 +229,10 @@ describe('stringifyRelativeDateFilter', () => { amount: 5, unit: 'DAY', timezone: 'America/New_York', - referenceDayAsString: '2024-01-15', }; expect(stringifyRelativeDateFilter(filter)).toBe( - 'PAST_5_DAY;;America/New_York;;2024-01-15;;MONDAY;;', + 'PAST_5_DAY;;America/New_York;;MONDAY;;', ); expect(mockDetectCalendarStartDay).toHaveBeenCalledTimes(1); }); @@ -302,37 +245,20 @@ describe('stringifyRelativeDateFilter', () => { amount: 5, unit: 'DAY', timezone: 'America/New_York', - referenceDayAsString: '2024-01-15', firstDayOfTheWeek: null, }; expect(stringifyRelativeDateFilter(filter)).toBe( - 'PAST_5_DAY;;America/New_York;;2024-01-15;;SATURDAY;;', + 'PAST_5_DAY;;America/New_York;;SATURDAY;;', ); expect(mockDetectCalendarStartDay).toHaveBeenCalledTimes(1); }); - it('should not append firstDayOfTheWeek when referenceDayAsString is not provided', () => { - const filter: RelativeDateFilter = { - direction: 'PAST', - amount: 5, - unit: 'DAY', - timezone: 'America/New_York', - firstDayOfTheWeek: 'SUNDAY', - }; - - expect(stringifyRelativeDateFilter(filter)).toBe( - 'PAST_5_DAY;;America/New_York;;', - ); - expect(mockDetectCalendarStartDay).not.toHaveBeenCalled(); - }); - it('should not append firstDayOfTheWeek when timezone is not provided', () => { const filter: RelativeDateFilter = { direction: 'PAST', amount: 5, unit: 'DAY', - referenceDayAsString: '2024-01-15', firstDayOfTheWeek: 'SUNDAY', }; @@ -348,11 +274,10 @@ describe('stringifyRelativeDateFilter', () => { amount: 5, unit: 'DAY', timezone: 'America/New_York', - referenceDayAsString: '2024-01-15', }; expect(stringifyRelativeDateFilter(filter)).toBe( - 'PAST_5_DAY;;America/New_York;;2024-01-15;;', + 'PAST_5_DAY;;America/New_York;;', ); }); }); @@ -364,12 +289,11 @@ describe('stringifyRelativeDateFilter', () => { amount: 10, unit: 'MONTH', timezone: 'Europe/London', - referenceDayAsString: '2024-12-25', firstDayOfTheWeek: 'MONDAY', }; expect(stringifyRelativeDateFilter(filter)).toBe( - 'NEXT_10_MONTH;;Europe/London;;2024-12-25;;MONDAY;;', + 'NEXT_10_MONTH;;Europe/London;;MONDAY;;', ); }); @@ -379,12 +303,11 @@ describe('stringifyRelativeDateFilter', () => { amount: 7, unit: 'WEEK', timezone: 'Asia/Tokyo', - referenceDayAsString: '2024-06-01', firstDayOfTheWeek: 'SUNDAY', }; expect(stringifyRelativeDateFilter(filter)).toBe( - 'THIS_1_WEEK;;Asia/Tokyo;;2024-06-01;;SUNDAY;;', + 'THIS_1_WEEK;;Asia/Tokyo;;SUNDAY;;', ); }); diff --git a/packages/twenty-front/src/modules/views/view-filter-value/utils/stringifyRelativeDateFilter.ts b/packages/twenty-front/src/modules/views/view-filter-value/utils/stringifyRelativeDateFilter.ts index c5920db267..a9d922fc40 100644 --- a/packages/twenty-front/src/modules/views/view-filter-value/utils/stringifyRelativeDateFilter.ts +++ b/packages/twenty-front/src/modules/views/view-filter-value/utils/stringifyRelativeDateFilter.ts @@ -21,15 +21,11 @@ export const stringifyRelativeDateFilter = ( if (isNonEmptyString(relativeDateFilter.timezone)) { relativeDateFilterStringified = `${relativeDateFilterStringified};;${relativeDateFilter.timezone};;`; - if (isNonEmptyString(relativeDateFilter.referenceDayAsString)) { - relativeDateFilterStringified = `${relativeDateFilterStringified}${relativeDateFilter.referenceDayAsString};;`; + const firstDayOfTheWeek = + relativeDateFilter.firstDayOfTheWeek ?? detectCalendarStartDay(); - const firstDayOfTheWeek = - relativeDateFilter.firstDayOfTheWeek ?? detectCalendarStartDay(); - - if (isNonEmptyString(firstDayOfTheWeek)) { - relativeDateFilterStringified = `${relativeDateFilterStringified}${firstDayOfTheWeek};;`; - } + if (isNonEmptyString(firstDayOfTheWeek)) { + relativeDateFilterStringified = `${relativeDateFilterStringified}${firstDayOfTheWeek};;`; } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/filter/utils/__tests__/evaluate-relative-date-filter.util.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/filter/utils/__tests__/evaluate-relative-date-filter.util.spec.ts index 1e07c5e36b..008f7ecd04 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/filter/utils/__tests__/evaluate-relative-date-filter.util.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/filter/utils/__tests__/evaluate-relative-date-filter.util.spec.ts @@ -8,10 +8,7 @@ import { subWeeks, subYears, } from 'date-fns'; -import { - getPlainDateFromDate, - type RelativeDateFilter, -} from 'twenty-shared/utils'; +import { type RelativeDateFilter } from 'twenty-shared/utils'; import { evaluateRelativeDateFilter, @@ -472,7 +469,6 @@ describe('Relative Date Filter Utils', () => { direction: 'THIS', unit: 'WEEK', firstDayOfTheWeek: 'MONDAY', - referenceDayAsString: getPlainDateFromDate(now), }; expect( diff --git a/packages/twenty-shared/src/utils/filter/dates/utils/getStartUnitOfDateTime.ts b/packages/twenty-shared/src/utils/filter/dates/utils/getStartUnitOfDateTime.ts index eb32b0f0ff..928e3abe5c 100644 --- a/packages/twenty-shared/src/utils/filter/dates/utils/getStartUnitOfDateTime.ts +++ b/packages/twenty-shared/src/utils/filter/dates/utils/getStartUnitOfDateTime.ts @@ -18,6 +18,14 @@ export const getStartUnitOfDateTime = ( const firstDayOfTheWeekAsDateFNSNumber = getFirstDayOfTheWeekAsANumberForDateFNS(firstDayOfTheWeek); + console.log({ + firstDayOfTheWeek, + firstDayOfTheWeekAsDateFNSNumber, + startOfWekk: startOfWeek(dateTime, { + weekStartsOn: firstDayOfTheWeekAsDateFNSNumber, + }), + }); + return startOfWeek(dateTime, { weekStartsOn: firstDayOfTheWeekAsDateFNSNumber, }); diff --git a/packages/twenty-shared/src/utils/filter/dates/utils/relativeDateFilterSchema.ts b/packages/twenty-shared/src/utils/filter/dates/utils/relativeDateFilterSchema.ts index a54c584f0f..524009f9aa 100644 --- a/packages/twenty-shared/src/utils/filter/dates/utils/relativeDateFilterSchema.ts +++ b/packages/twenty-shared/src/utils/filter/dates/utils/relativeDateFilterSchema.ts @@ -11,7 +11,6 @@ export const relativeDateFilterSchema = z amount: relativeDateFilterAmountSchema.nullish(), unit: relativeDateFilterUnitSchema, timezone: z.string().nullish(), - referenceDayAsString: z.string().nullish(), firstDayOfTheWeek: firstDayOfWeekSchema.nullish(), }) .refine((data) => !(data.amount === undefined && data.direction !== 'THIS'), { diff --git a/packages/twenty-shared/src/utils/filter/dates/utils/relativeDateFilterStringifiedSchema.ts b/packages/twenty-shared/src/utils/filter/dates/utils/relativeDateFilterStringifiedSchema.ts index 67edd9c00d..1bc8caf550 100644 --- a/packages/twenty-shared/src/utils/filter/dates/utils/relativeDateFilterStringifiedSchema.ts +++ b/packages/twenty-shared/src/utils/filter/dates/utils/relativeDateFilterStringifiedSchema.ts @@ -3,7 +3,7 @@ import { isNonEmptyArray } from '@sniptt/guards'; import z from 'zod'; const REGEX_FOR_RELATIVE_DATE_FILTER_STRINGIFIED_PARSING = - /((?:THIS)|(?:PAST)|(?:NEXT))_(\d*)_(DAY|MONTH|YEAR|WEEK)(?:(?:;;([^;;]*);;)?(?:(\d\d\d\d-\d\d-\d\d);;)?(?:(MONDAY|SUNDAY|SATURDAY);;)?)?/; + /((?:THIS)|(?:PAST)|(?:NEXT))_(\d*)_(DAY|MONTH|YEAR|WEEK)(?:(?:;;([^;;]*);;)?(?:(MONDAY|SUNDAY|SATURDAY);;)?)?/; export const relativeDateFilterStringifiedSchema = z .string() @@ -18,22 +18,13 @@ export const relativeDateFilterStringifiedSchema = z throw new Error(`Cannot parse stringified relative date filter`); } - const [ - _, - direction, - amount, - unit, - timezone, - referenceDayAsString, - firstDayOfTheWeek, - ] = result; + const [_, direction, amount, unit, timezone, firstDayOfTheWeek] = result; return relativeDateFilterSchema.parse({ direction, amount, unit, timezone, - referenceDayAsString, firstDayOfTheWeek, }); });