Implement sub-field filtering on CURRENCY field type (#11726)

This PR implements sub-field filtering on CURRENCY field type and
improves many related zones.

- Created a ObjectFilterDropdownCurrencySelect dropdown component for
filtering on multiple currencies
- Added currencyCode sub-field to CurrencyFilter type
- Created getDefaultSubFieldNameForCompositeFilterableFieldType to avoid
situation where we don't have any sub field name in sub field filtering
situations.
- Implemented filtering for currencyCode in
computeFilterRecordGqlOperationFilter
- Implemented CURRENCY type in getRecordFilterOperands
- Implemented isMatchingCurrencyFilter for using in
isRecordMatchingFilter for proper optimistic rendering
- Created turnCurrencyIntoSelectableItem to help
ObjectFilterDropdownCurrencySelect

Testing : 
- Added test for currency sub fields in getOperandsForFilterType
- Completely reworked isMatchingCurrencyFilter test

Improvements : 
- Created a unique CURRENCIES constant to avoid re-creating it at
various places
- Derive the type FilterableFieldType from a constant array
FILTERABLE_FIELD_TYPES, so it's easier to work with
- Added areCompositeTypeSubFieldsFilterable
- Fixed a bug with empty value '[]' that was preventing the auto-removal
of a filter chip

Miscellaneous : 
- Created isExpectedSubFieldName util to do a type-safe check of a
subFieldName
- Better naming : renamed isCompositeField to isCompositeFieldType
- Created isCompositeTypeFilterableWithAny to specify which field types
are filterable by any sub field
- Better naming : renamed
ObjectFilterDropdownFilterSelectCompositeFieldSubMenu to
ObjectFilterDropdownSubFieldSelect
- Better naming : renamed ObjectFilterDropdownFilterSelect to
ObjectFilterDropdownFieldSelect
- Created isEmptinessOperand util instead of duplicating the same
hard-coded check in multiple places
- Better naming : used subFieldName instead of compositeFieldName for
consistency
- UseEffect removal : removed unnecessary useEffect in
MultipleSelectDropdown

Fixes a bug where Empty and Not weren't appearing in filter chip in
particular cases
Fixes https://github.com/twentyhq/core-team-issues/issues/498
Fixes https://github.com/twentyhq/twenty/issues/7558
This commit is contained in:
Lucas Bordeau
2025-04-25 19:33:00 +02:00
committed by GitHub
parent f201091c68
commit 50cb32d122
51 changed files with 1358 additions and 422 deletions

View File

@ -1,9 +1,10 @@
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
import { getOperandLabelShort } from '@/object-record/object-filter-dropdown/utils/getOperandLabel';
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
import { isFilterOperandExpectingValue } from '@/object-record/object-filter-dropdown/utils/isFilterOperandExpectingValue';
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { isEmptinessOperand } from '@/object-record/record-filter/utils/isEmptinessOperand';
import { isRecordFilterConsideredEmpty } from '@/object-record/record-filter/utils/isRecordFilterConsideredEmpty';
import { isValidSubFieldName } from '@/settings/data-model/utils/isValidSubFieldName';
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
import { isNonEmptyString } from '@sniptt/guards';
@ -27,11 +28,12 @@ export const EditableFilterChip = ({
const FieldMetadataItemIcon = getIcon(fieldMetadataItem.icon);
const operandLabelShort = getOperandLabelShort(recordFilter.operand);
const operandIsEmptiness = isEmptinessOperand(recordFilter.operand);
const recordFilterSubFieldName = recordFilter.subFieldName;
const subFieldLabel =
isCompositeField(fieldMetadataItem.type) &&
isCompositeFieldType(fieldMetadataItem.type) &&
isNonEmptyString(recordFilterSubFieldName) &&
isValidSubFieldName(recordFilterSubFieldName)
? getCompositeSubFieldLabel(
@ -44,11 +46,9 @@ export const EditableFilterChip = ({
? `${recordFilter.label} / ${subFieldLabel}`
: recordFilter.label;
const shouldDisplayOperandLabelShort =
isNonEmptyString(recordFilter.value) ||
!isFilterOperandExpectingValue(recordFilter.operand);
const recordFilterIsEmpty = isRecordFilterConsideredEmpty(recordFilter);
const labelKey = `${fieldNameLabel}${shouldDisplayOperandLabelShort ? operandLabelShort : ''}`;
const labelKey = `${fieldNameLabel}${!operandIsEmptiness && !recordFilterIsEmpty ? operandLabelShort : operandIsEmptiness ? ` ${operandLabelShort}` : ''}`;
return (
<SortOrFilterChip