diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRelationValueInputClickableSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRelationValueInputClickableSelect.tsx new file mode 100644 index 0000000000..61fcf935cc --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRelationValueInputClickableSelect.tsx @@ -0,0 +1,23 @@ +import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; +import { SelectControl } from '@/ui/input/components/SelectControl'; +import { useComputeRecordRelationFilterDisplayValue } from '@/views/hooks/useComputeRecordRelationFilterDisplayValue'; + +type AdvancedFilterRelationValueInputClickableSelectProps = { + recordFilter: RecordFilter; +}; + +export const AdvancedFilterRelationValueInputClickableSelect = ({ + recordFilter, +}: AdvancedFilterRelationValueInputClickableSelectProps) => { + const { displayValue } = useComputeRecordRelationFilterDisplayValue({ + recordFilter, + }); + + return ( + + ); +}; 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 cdb62c0635..f65735041a 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 @@ -1,3 +1,4 @@ +import { AdvancedFilterRelationValueInputClickableSelect } from '@/object-record/advanced-filter/components/AdvancedFilterRelationValueInputClickableSelect'; import { getAdvancedFilterInputPlaceholderText } from '@/object-record/advanced-filter/utils/getAdvancedFilterInputPlacedholderText'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { SelectControl } from '@/ui/input/components/SelectControl'; @@ -70,6 +71,18 @@ export const AdvancedFilterValueInputDropdownButtonClickableSelect = ({ const isDateTimeType = recordFilter?.type === 'DATE' || recordFilter?.type === 'DATE_TIME'; + if ( + recordFilter?.type === 'RELATION' && + !shouldUsePlaceholder && + isDefined(recordFilter.fieldMetadataId) + ) { + return ( + + ); + } + return isDateTimeType ? ( {advancedFilterInputText} ) : ( diff --git a/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterDisplayValue.tsx b/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterDisplayValue.tsx new file mode 100644 index 0000000000..2cc1ec4a8a --- /dev/null +++ b/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterDisplayValue.tsx @@ -0,0 +1,96 @@ +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { getRelationObjectMetadataNameSingular } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { getFieldMetadataItemByIdOrThrow } from '@/object-metadata/utils/getFieldMetadataItemByIdOrThrow'; +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 { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; +import { t } from '@lingui/core/macro'; +import { + arrayOfUuidOrVariableSchema, + isDefined, + jsonRelationFilterValueSchema, +} from 'twenty-shared/utils'; + +type UseComputeRecordRelationFilterDisplayValueParams = { + recordFilter: RecordFilter; +}; + +// The stored displayValue is deprecated and empty for filters created without one, so compute the label at runtime. +export const useComputeRecordRelationFilterDisplayValue = ({ + recordFilter, +}: UseComputeRecordRelationFilterDisplayValueParams) => { + const allowRequestsToTwentyIcons = useAtomStateValue( + allowRequestsToTwentyIconsState, + ); + + const { objectMetadataItems } = useObjectMetadataItems(); + + if (!isDefined(recordFilter.fieldMetadataId)) { + throw new Error('recordFilter.fieldMetadataId is not defined'); + } + + // Nested relation filters resolve records from the leaf relation's target object, direct filters from the source. + const { fieldMetadataItem } = getFieldMetadataItemByIdOrThrow({ + fieldMetadataId: + recordFilter.relationTargetFieldMetadataId ?? + recordFilter.fieldMetadataId, + objectMetadataItems, + }); + + const relationObjectMetadataNameSingular = + getRelationObjectMetadataNameSingular({ + field: fieldMetadataItem, + }); + + if (!isDefined(relationObjectMetadataNameSingular)) { + throw new Error('relationObjectMetadataNameSingular is not defined'); + } + + const relationObjectMetadataItem = objectMetadataItems.find( + (objectMetadataItem) => + objectMetadataItem.nameSingular === relationObjectMetadataNameSingular, + ); + + if (!isDefined(relationObjectMetadataItem)) { + throw new Error('relationObjectMetadataItem is not defined'); + } + + const relationObjectLabelPlural = relationObjectMetadataItem.labelPlural; + + const { isCurrentWorkspaceMemberSelected, selectedRecordIds } = + jsonRelationFilterValueSchema + .catch({ + isCurrentWorkspaceMemberSelected: false, + selectedRecordIds: arrayOfUuidOrVariableSchema.parse( + recordFilter.value, + ), + }) + .parse(recordFilter.value); + + const { selectedRecords, loading } = useRecordsForSelect({ + searchFilterText: '', + selectedIds: selectedRecordIds, + objectNameSingular: relationObjectMetadataNameSingular, + limit: 10, + allowRequestsToTwentyIcons, + }); + + if (loading) { + return { displayValue: t`Loading...`, loading }; + } + + const labelValueItems = [ + ...(isCurrentWorkspaceMemberSelected ? [t`Me`] : []), + ...selectedRecords.map((record) => record.name), + ]; + + const displayValue = + labelValueItems.length > MAX_RECORDS_TO_DISPLAY + ? `${labelValueItems.length} ${relationObjectLabelPlural.toLowerCase()}` + : labelValueItems.join(', '); + + return { displayValue, loading }; +}; diff --git a/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx b/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx index eee38bff62..f25b383f59 100644 --- a/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx +++ b/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx @@ -1,19 +1,9 @@ -import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { getRelationObjectMetadataNameSingular } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; -import { getFieldMetadataItemByIdOrThrow } from '@/object-metadata/utils/getFieldMetadataItemByIdOrThrow'; -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 { useComputeRecordRelationFilterDisplayValue } from '@/views/hooks/useComputeRecordRelationFilterDisplayValue'; import { useGetRecordFilterChipLabelValue } from '@/views/hooks/useGetRecordFilterChipLabelValue'; +import { isNonEmptyString } from '@sniptt/guards'; import { t } from '@lingui/core/macro'; -import { - arrayOfUuidOrVariableSchema, - isDefined, - jsonRelationFilterValueSchema, -} from 'twenty-shared/utils'; -import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; type ObjectFilterDropdownRecordSelectProps = { recordFilter: RecordFilter; @@ -23,86 +13,26 @@ type ObjectFilterDropdownRecordSelectProps = { export const useComputeRecordRelationFilterLabelValue = ({ recordFilter, }: ObjectFilterDropdownRecordSelectProps) => { - const allowRequestsToTwentyIcons = useAtomStateValue( - allowRequestsToTwentyIconsState, - ); - - const { objectMetadataItems } = useObjectMetadataItems(); - const { getRecordFilterChipLabelValue } = useGetRecordFilterChipLabelValue(); - if (!isDefined(recordFilter.fieldMetadataId)) { - throw new Error('fieldMetadataItemUsedInFilterDropdown is not defined'); - } - - const { fieldMetadataItem } = getFieldMetadataItemByIdOrThrow({ - fieldMetadataId: recordFilter.fieldMetadataId, - objectMetadataItems, - }); - - const relationObjectMetadataNameSingular = - getRelationObjectMetadataNameSingular({ - field: fieldMetadataItem, - }); - - if (!isDefined(relationObjectMetadataNameSingular)) { - throw new Error('relationObjectMetadataNameSingular is not defined'); - } - - const relationObjectMetadataItem = objectMetadataItems.find( - (objectMetadataItem) => - objectMetadataItem.nameSingular === relationObjectMetadataNameSingular, - ); - - if (!isDefined(relationObjectMetadataItem)) { - throw new Error('relationObjectMetadataItem is not defined'); - } - - const relationObjectLabelPlural = relationObjectMetadataItem.labelPlural; - - const { isCurrentWorkspaceMemberSelected, selectedRecordIds } = - jsonRelationFilterValueSchema - .catch({ - isCurrentWorkspaceMemberSelected: false, - selectedRecordIds: arrayOfUuidOrVariableSchema.parse( - recordFilter.value, - ), - }) - .parse(recordFilter.value); - - const { selectedRecords, loading } = useRecordsForSelect({ - searchFilterText: '', - selectedIds: selectedRecordIds, - objectNameSingular: relationObjectMetadataNameSingular, - limit: 10, - allowRequestsToTwentyIcons, + const { displayValue, loading } = useComputeRecordRelationFilterDisplayValue({ + recordFilter, }); if (loading) { return { labelValue: t`: Loading...` }; } - const labelValueItems = [ - ...(isCurrentWorkspaceMemberSelected ? [t`Me`] : []), - ...selectedRecords.map((record) => record.name), - ]; - - const filterDisplayValue = - labelValueItems.length > MAX_RECORDS_TO_DISPLAY - ? `${labelValueItems.length} ${relationObjectLabelPlural.toLowerCase()}` - : labelValueItems.join(', '); - return { - labelValue: - labelValueItems.length > 0 - ? getRecordFilterChipLabelValue({ - recordFilter: { - ...recordFilter, - displayValue: filterDisplayValue, - }, - }) - : getRecordFilterChipLabelValue({ - recordFilter, - }), + labelValue: isNonEmptyString(displayValue) + ? getRecordFilterChipLabelValue({ + recordFilter: { + ...recordFilter, + displayValue, + }, + }) + : getRecordFilterChipLabelValue({ + recordFilter, + }), }; };