diff --git a/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx b/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx
index 65520b84be..d71ba52777 100644
--- a/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx
+++ b/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx
@@ -8,6 +8,7 @@ import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRe
import { isRecordFilterConsideredEmpty } from '@/object-record/record-filter/utils/isRecordFilterConsideredEmpty';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { EditableFilterChipDropdownContent } from '@/views/components/EditableFilterChipDropdownContent';
+import { EditableRelationFilterChip } from '@/views/components/EditableRelationFilterChip';
import { useClearVectorSearchInput } from '@/views/hooks/useClearVectorSearchInput';
import { useSetEditableFilterChipDropdownStates } from '@/views/hooks/useSetEditableFilterChipDropdownStates';
@@ -54,11 +55,19 @@ export const EditableFilterDropdownButton = ({
+ recordFilter.type === 'RELATION' ? (
+
+ ) : (
+
+ )
}
dropdownComponents={
diff --git a/packages/twenty-front/src/modules/views/components/EditableRelationFilterChip.tsx b/packages/twenty-front/src/modules/views/components/EditableRelationFilterChip.tsx
new file mode 100644
index 0000000000..f62dff4504
--- /dev/null
+++ b/packages/twenty-front/src/modules/views/components/EditableRelationFilterChip.tsx
@@ -0,0 +1,42 @@
+import { useFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useFieldMetadataItemByIdOrThrow';
+import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
+import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
+import { useComputeRecordRelationFilterLabelValue } from '@/views/hooks/useComputeRecordRelationFilterLabelValue';
+import { useIcons } from 'twenty-ui/display';
+
+type EditableRelationFilterChipProps = {
+ recordFilter: RecordFilter;
+ onRemove: () => void;
+ onClick?: () => void;
+};
+
+export const EditableRelationFilterChip = ({
+ recordFilter,
+ onRemove,
+ onClick,
+}: EditableRelationFilterChipProps) => {
+ const { getIcon } = useIcons();
+
+ const { fieldMetadataItem } = useFieldMetadataItemByIdOrThrow(
+ recordFilter.fieldMetadataId,
+ );
+
+ const FieldMetadataItemIcon = getIcon(fieldMetadataItem.icon);
+
+ const { labelValue } = useComputeRecordRelationFilterLabelValue({
+ recordFilter,
+ });
+
+ return (
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/views/components/SortOrFilterChip.tsx b/packages/twenty-front/src/modules/views/components/SortOrFilterChip.tsx
index 19993b7744..eab9512f06 100644
--- a/packages/twenty-front/src/modules/views/components/SortOrFilterChip.tsx
+++ b/packages/twenty-front/src/modules/views/components/SortOrFilterChip.tsx
@@ -95,6 +95,10 @@ const StyledSortValue = styled.span`
font-weight: ${({ theme }) => theme.font.weight.medium};
`;
+const StyledKeyLabelContainer = styled.div`
+ display: flex;
+`;
+
export type SortOrFilterChipVariant = 'default' | 'danger';
export type SortOrFilterChipType = 'sort' | 'filter';
@@ -134,12 +138,14 @@ export const SortOrFilterChip = ({
)}
- {labelKey && {labelKey}}
- {type === 'sort' ? (
- {labelValue}
- ) : (
- {labelValue}
- )}
+
+ {labelKey && {labelKey}}
+ {type === 'sort' ? (
+ {labelValue}
+ ) : (
+ {labelValue}
+ )}
+
{
mutation: CREATE_CORE_VIEW_FIELD,
variables: {
input: {
- id: viewField.id,
+ id: v4(),
fieldMetadataId: viewField.fieldMetadataId,
viewId: view.id,
isVisible: viewField.isVisible,
diff --git a/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx b/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx
new file mode 100644
index 0000000000..5b8e9d6859
--- /dev/null
+++ b/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx
@@ -0,0 +1,91 @@
+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 { getRecordFilterLabelValue } from '@/views/utils/getRecordFilterLabelValue';
+import { arrayOfUuidOrVariableSchema } from '@/views/view-filter-value/validation-schemas/arrayOfUuidsOrVariablesSchema';
+import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
+import { t } from '@lingui/core/macro';
+import { isDefined } from 'twenty-shared/utils';
+
+type ObjectFilterDropdownRecordSelectProps = {
+ recordFilter: RecordFilter;
+};
+
+export const useComputeRecordRelationFilterLabelValue = ({
+ recordFilter,
+}: ObjectFilterDropdownRecordSelectProps) => {
+ const { objectMetadataItems } = useObjectMetadataItems();
+
+ 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,
+ });
+
+ 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
+ ? getRecordFilterLabelValue({
+ ...recordFilter,
+ displayValue: filterDisplayValue,
+ })
+ : getRecordFilterLabelValue(recordFilter),
+ };
+};
diff --git a/packages/twenty-ui/package.json b/packages/twenty-ui/package.json
index 9a0cad138d..d36989f015 100644
--- a/packages/twenty-ui/package.json
+++ b/packages/twenty-ui/package.json
@@ -46,7 +46,6 @@
},
"files": [
"dist",
- "assets",
"accessibility",
"assets",
"components",
diff --git a/packages/twenty-ui/scripts/generateBarrels.ts b/packages/twenty-ui/scripts/generateBarrels.ts
index a9b44af58b..f0692d602d 100644
--- a/packages/twenty-ui/scripts/generateBarrels.ts
+++ b/packages/twenty-ui/scripts/generateBarrels.ts
@@ -243,7 +243,7 @@ const computePackageJsonFilesAndExportsConfig = (
return {
exports,
typesVersions: { '*': typesVersionsEntries },
- files: ['dist', 'assets', ...entrypoints],
+ files: ['dist', ...entrypoints],
};
};