diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction.tsx
index e89b97691538..7c48a63a0b92 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction.tsx
@@ -15,6 +15,7 @@ import { DELETE_MAX_COUNT } from '@/object-record/constants/DeleteMaxCount';
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
import { FilterOperand } from '@/object-record/object-filter-dropdown/types/FilterOperand';
+import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
@@ -52,10 +53,13 @@ export const useDeleteMultipleRecordsAction = ({
contextStoreFiltersComponentState,
);
+ const { filterValueDependencies } = useFilterValueDependencies();
+
const graphqlFilter = computeContextStoreFilters(
contextStoreTargetedRecordsRule,
contextStoreFilters,
objectMetadataItem,
+ filterValueDependencies,
);
const deletedAtFieldMetadata = objectMetadataItem.fields.find(
diff --git a/packages/twenty-front/src/modules/context-store/hooks/useFindManyRecordsSelectedInContextStore.ts b/packages/twenty-front/src/modules/context-store/hooks/useFindManyRecordsSelectedInContextStore.ts
index 30b095a30189..5743292a5633 100644
--- a/packages/twenty-front/src/modules/context-store/hooks/useFindManyRecordsSelectedInContextStore.ts
+++ b/packages/twenty-front/src/modules/context-store/hooks/useFindManyRecordsSelectedInContextStore.ts
@@ -4,6 +4,7 @@ import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/s
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
+import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const useFindManyRecordsSelectedInContextStore = ({
@@ -30,10 +31,13 @@ export const useFindManyRecordsSelectedInContextStore = ({
instanceId,
);
+ const { filterValueDependencies } = useFilterValueDependencies();
+
const queryFilter = computeContextStoreFilters(
contextStoreTargetedRecordsRule,
contextStoreFilters,
objectMetadataItem,
+ filterValueDependencies,
);
const { records, loading, totalCount } = useFindManyRecords({
diff --git a/packages/twenty-front/src/modules/context-store/utils/__tests__/computeContextStoreFilters.test.ts b/packages/twenty-front/src/modules/context-store/utils/__tests__/computeContextStoreFilters.test.ts
index 4ea9f4a7a35d..af1fe415130e 100644
--- a/packages/twenty-front/src/modules/context-store/utils/__tests__/computeContextStoreFilters.test.ts
+++ b/packages/twenty-front/src/modules/context-store/utils/__tests__/computeContextStoreFilters.test.ts
@@ -1,14 +1,20 @@
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
+import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { expect } from '@storybook/test';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
+
describe('computeContextStoreFilters', () => {
const personObjectMetadataItem = generatedMockObjectMetadataItems.find(
(item) => item.nameSingular === 'person',
)!;
+ const mockFilterValueDependencies: FilterValueDependencies = {
+ currentWorkspaceMemberId: '32219445-f587-4c40-b2b1-6d3205ed96da',
+ };
+
it('should work for selection mode', () => {
const contextStoreTargetedRecordsRule: ContextStoreTargetedRecordsRule = {
mode: 'selection',
@@ -19,6 +25,7 @@ describe('computeContextStoreFilters', () => {
contextStoreTargetedRecordsRule,
[],
personObjectMetadataItem,
+ mockFilterValueDependencies,
);
expect(filters).toEqual({
@@ -61,6 +68,7 @@ describe('computeContextStoreFilters', () => {
contextStoreTargetedRecordsRule,
contextStoreFilters,
personObjectMetadataItem,
+ mockFilterValueDependencies,
);
expect(filters).toEqual({
diff --git a/packages/twenty-front/src/modules/context-store/utils/computeContextStoreFilters.ts b/packages/twenty-front/src/modules/context-store/utils/computeContextStoreFilters.ts
index e685c35f59aa..91ede0aab9a9 100644
--- a/packages/twenty-front/src/modules/context-store/utils/computeContextStoreFilters.ts
+++ b/packages/twenty-front/src/modules/context-store/utils/computeContextStoreFilters.ts
@@ -2,6 +2,7 @@ import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextS
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
+import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
@@ -9,12 +10,14 @@ export const computeContextStoreFilters = (
contextStoreTargetedRecordsRule: ContextStoreTargetedRecordsRule,
contextStoreFilters: Filter[],
objectMetadataItem: ObjectMetadataItem,
+ filterValueDependencies: FilterValueDependencies,
) => {
let queryFilter: RecordGqlOperationFilter | undefined;
if (contextStoreTargetedRecordsRule.mode === 'exclusion') {
queryFilter = makeAndFilterVariables([
computeViewRecordGqlOperationFilter(
+ filterValueDependencies,
contextStoreFilters,
objectMetadataItem?.fields ?? [],
[],
@@ -39,6 +42,7 @@ export const computeContextStoreFilters = (
},
}
: computeViewRecordGqlOperationFilter(
+ filterValueDependencies,
contextStoreFilters,
objectMetadataItem?.fields ?? [],
[],
diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordPinnedItems.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordPinnedItems.tsx
new file mode 100644
index 000000000000..fa975fabc28b
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordPinnedItems.tsx
@@ -0,0 +1,45 @@
+import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip';
+import { SelectableItem } from '@/object-record/select/types/SelectableItem';
+import styled from '@emotion/styled';
+import { MenuItemMultiSelectAvatar } from 'twenty-ui';
+
+const StyledPinnedItemsContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ padding: ${({ theme }) => theme.spacing(1)};
+`;
+
+export const ObjectFilterDropdownRecordPinnedItems = (props: {
+ selectableItems: SelectableItem[];
+ onChange: (
+ selectableItem: SelectableItem,
+ isNewCheckedValue: boolean,
+ ) => void;
+}) => {
+ return (
+
+ {props.selectableItems.map((selectableItem) => {
+ return (
+ {
+ props.onChange(selectableItem, newCheckedValue);
+ }}
+ avatar={
+
+ }
+ />
+ );
+ })}
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx
index 9a61611745bb..67f9ff7069d2 100644
--- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx
+++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx
@@ -2,16 +2,27 @@ import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { v4 } from 'uuid';
+import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
+import { ObjectFilterDropdownRecordPinnedItems } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordPinnedItems';
+import { CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID } from '@/object-record/object-filter-dropdown/constants/CurrentWorkspaceMemberSelectableItemId';
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
+import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
+import { RelationFilterValue } from '@/views/view-filter-value/types/RelationFilterValue';
+import { relationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/relationFilterValueSchema';
+import { IconUserCircle } from 'twenty-ui';
import { isDefined } from '~/utils/isDefined';
-export const EMPTY_FILTER_VALUE = '[]';
+export const EMPTY_FILTER_VALUE: string = JSON.stringify({
+ isCurrentWorkspaceMemberSelected: false,
+ selectedRecordIds: [],
+} satisfies RelationFilterValue);
+
export const MAX_RECORDS_TO_DISPLAY = 3;
type ObjectFilterDropdownRecordSelectProps = {
@@ -54,9 +65,27 @@ export const ObjectFilterDropdownRecordSelect = ({
const selectedFilter = useRecoilValue(selectedFilterState);
+ const { isCurrentWorkspaceMemberSelected, selectedRecordIds } =
+ relationFilterValueSchema
+ .catch({
+ isCurrentWorkspaceMemberSelected: false,
+ selectedRecordIds: [],
+ })
+ .parse(selectedFilter?.value);
+
const objectNameSingular =
filterDefinitionUsedInDropdown?.relationObjectMetadataNameSingular;
+ if (!isDefined(objectNameSingular)) {
+ throw new Error('relationObjectMetadataNameSingular is not defined');
+ }
+
+ const { objectMetadataItem } = useObjectMetadataItem({
+ objectNameSingular: objectNameSingular,
+ });
+
+ const objectLabelPlural = objectMetadataItem?.labelPlural;
+
if (!isDefined(objectNameSingular)) {
throw new Error('objectNameSingular is not defined');
}
@@ -69,27 +98,53 @@ export const ObjectFilterDropdownRecordSelect = ({
limit: 10,
});
+ const currentWorkspaceMemberSelectableItem: SelectableItem = {
+ id: CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID,
+ name: 'Me',
+ isSelected: isCurrentWorkspaceMemberSelected,
+ AvatarIcon: IconUserCircle,
+ };
+
+ const pinnedSelectableItems: SelectableItem[] =
+ objectNameSingular === 'workspaceMember'
+ ? [currentWorkspaceMemberSelectableItem]
+ : [];
+
+ const filteredPinnedSelectableItems = pinnedSelectableItems.filter((item) =>
+ item.name
+ .toLowerCase()
+ .includes(objectFilterDropdownSearchInput.toLowerCase()),
+ );
+
const handleMultipleRecordSelectChange = (
- recordToSelect: SelectableItem,
- newSelectedValue: boolean,
+ itemToSelect: SelectableItem,
+ isNewSelectedValue: boolean,
) => {
if (loading) {
return;
}
- const newSelectedRecordIds = newSelectedValue
- ? [...objectFilterDropdownSelectedRecordIds, recordToSelect.id]
- : objectFilterDropdownSelectedRecordIds.filter(
- (id) => id !== recordToSelect.id,
- );
+ const isItemCurrentWorkspaceMember =
+ itemToSelect.id === CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID;
- if (newSelectedRecordIds.length === 0) {
- emptyFilterButKeepDefinition();
- deleteCombinedViewFilter(fieldId);
- return;
- }
+ const selectedRecordIdsWithAddedRecord = [
+ ...objectFilterDropdownSelectedRecordIds,
+ itemToSelect.id,
+ ];
+ const selectedRecordIdsWithRemovedRecord =
+ objectFilterDropdownSelectedRecordIds.filter(
+ (id) => id !== itemToSelect.id,
+ );
+
+ const newSelectedRecordIds = isItemCurrentWorkspaceMember
+ ? objectFilterDropdownSelectedRecordIds
+ : isNewSelectedValue
+ ? selectedRecordIdsWithAddedRecord
+ : selectedRecordIdsWithRemovedRecord;
- setObjectFilterDropdownSelectedRecordIds(newSelectedRecordIds);
+ const newIsCurrentWorkspaceMemberSelected = isItemCurrentWorkspaceMember
+ ? isNewSelectedValue
+ : isCurrentWorkspaceMemberSelected;
const selectedRecordNames = [
...recordsToSelect,
@@ -103,19 +158,32 @@ export const ObjectFilterDropdownRecordSelect = ({
.filter((record) => newSelectedRecordIds.includes(record.id))
.map((record) => record.name);
+ const selectedPinnedItemNames = newIsCurrentWorkspaceMemberSelected
+ ? [currentWorkspaceMemberSelectableItem.name]
+ : [];
+
+ const selectedItemNames = [
+ ...selectedPinnedItemNames,
+ ...selectedRecordNames,
+ ];
+
const filterDisplayValue =
- selectedRecordNames.length > MAX_RECORDS_TO_DISPLAY
- ? `${selectedRecordNames.length} companies`
- : selectedRecordNames.join(', ');
+ selectedItemNames.length > MAX_RECORDS_TO_DISPLAY
+ ? `${selectedItemNames.length} ${objectLabelPlural.toLowerCase()}`
+ : selectedItemNames.join(', ');
if (
isDefined(filterDefinitionUsedInDropdown) &&
isDefined(selectedOperandInDropdown)
) {
const newFilterValue =
- newSelectedRecordIds.length > 0
- ? JSON.stringify(newSelectedRecordIds)
- : EMPTY_FILTER_VALUE;
+ newSelectedRecordIds.length > 0 || newIsCurrentWorkspaceMemberSelected
+ ? JSON.stringify({
+ isCurrentWorkspaceMemberSelected:
+ newIsCurrentWorkspaceMemberSelected,
+ selectedRecordIds: newSelectedRecordIds,
+ } satisfies RelationFilterValue)
+ : '';
const viewFilter =
currentViewWithCombinedFiltersAndSorts?.viewFilters.find(
@@ -139,15 +207,26 @@ export const ObjectFilterDropdownRecordSelect = ({
};
return (
-
+ <>
+ {filteredPinnedSelectableItems.length > 0 && (
+ <>
+
+
+ >
+ )}
+
+ >
);
};
diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/constants/CurrentWorkspaceMemberSelectableItemId.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/constants/CurrentWorkspaceMemberSelectableItemId.ts
new file mode 100644
index 000000000000..b8f7455e347d
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/constants/CurrentWorkspaceMemberSelectableItemId.ts
@@ -0,0 +1,2 @@
+export const CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID =
+ 'CURRENT_WORKSPACE_MEMBER';
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAggregateRecordsForRecordBoardColumn.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAggregateRecordsForRecordBoardColumn.ts
index eabd56cf6eba..2775359bde43 100644
--- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAggregateRecordsForRecordBoardColumn.ts
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAggregateRecordsForRecordBoardColumn.ts
@@ -3,6 +3,7 @@ import { RecordBoardContext } from '@/object-record/record-board/contexts/Record
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { buildRecordGqlFieldsAggregateForRecordBoard } from '@/object-record/record-board/record-board-column/utils/buildRecordGqlFieldsAggregateForRecordBoard';
import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel';
+import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
@@ -53,7 +54,11 @@ export const useAggregateRecordsForRecordBoardColumn = () => {
);
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
+
+ const { filterValueDependencies } = useFilterValueDependencies();
+
const requestFilters = computeViewRecordGqlOperationFilter(
+ filterValueDependencies,
recordIndexFilters,
objectMetadataItem.fields,
recordIndexViewFilterGroups,
diff --git a/packages/twenty-front/src/modules/object-record/record-filter/hooks/useFilterValueDependencies.ts b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useFilterValueDependencies.ts
new file mode 100644
index 000000000000..e1f423041792
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useFilterValueDependencies.ts
@@ -0,0 +1,16 @@
+import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
+import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
+import { useRecoilValue } from 'recoil';
+
+export const useFilterValueDependencies = (): {
+ filterValueDependencies: FilterValueDependencies;
+} => {
+ const { id: currentWorkspaceMemberId } =
+ useRecoilValue(currentWorkspaceMemberState) ?? {};
+
+ return {
+ filterValueDependencies: {
+ currentWorkspaceMemberId,
+ },
+ };
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-filter/types/FilterValueDependencies.ts b/packages/twenty-front/src/modules/object-record/record-filter/types/FilterValueDependencies.ts
new file mode 100644
index 000000000000..0014490eb946
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-filter/types/FilterValueDependencies.ts
@@ -0,0 +1,3 @@
+export interface FilterValueDependencies {
+ currentWorkspaceMemberId?: string;
+}
diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts
index 31e31f86afb3..694107ae3c12 100644
--- a/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts
+++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts
@@ -1,4 +1,5 @@
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
+import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { getCompaniesMock } from '~/testing/mock-data/companies';
@@ -14,6 +15,10 @@ const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
(item) => item.nameSingular === 'person',
)!;
+const mockFilterValueDependencies: FilterValueDependencies = {
+ currentWorkspaceMemberId: '32219445-f587-4c40-b2b1-6d3205ed96da',
+};
+
jest.useFakeTimers().setSystemTime(new Date('2020-01-01'));
describe('computeViewRecordGqlOperationFilter', () => {
@@ -38,6 +43,7 @@ describe('computeViewRecordGqlOperationFilter', () => {
};
const result = computeViewRecordGqlOperationFilter(
+ mockFilterValueDependencies,
[nameFilter],
companyMockObjectMetadataItem.fields,
[],
@@ -90,6 +96,7 @@ describe('computeViewRecordGqlOperationFilter', () => {
};
const result = computeViewRecordGqlOperationFilter(
+ mockFilterValueDependencies,
[nameFilter, employeesFilter],
companyMockObjectMetadataItem.fields,
[],
@@ -176,6 +183,7 @@ describe('should work as expected for the different field types', () => {
};
const result = computeViewRecordGqlOperationFilter(
+ mockFilterValueDependencies,
[
addressFilterContains,
addressFilterDoesNotContain,
@@ -558,6 +566,7 @@ describe('should work as expected for the different field types', () => {
};
const result = computeViewRecordGqlOperationFilter(
+ mockFilterValueDependencies,
[
phonesFilterContains,
phonesFilterDoesNotContain,
@@ -759,6 +768,7 @@ describe('should work as expected for the different field types', () => {
};
const result = computeViewRecordGqlOperationFilter(
+ mockFilterValueDependencies,
[
emailsFilterContains,
emailsFilterDoesNotContain,
@@ -914,6 +924,7 @@ describe('should work as expected for the different field types', () => {
};
const result = computeViewRecordGqlOperationFilter(
+ mockFilterValueDependencies,
[
dateFilterIsAfter,
dateFilterIsBefore,
@@ -1030,6 +1041,7 @@ describe('should work as expected for the different field types', () => {
};
const result = computeViewRecordGqlOperationFilter(
+ mockFilterValueDependencies,
[
employeesFilterIsGreaterThan,
employeesFilterIsLessThan,
diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts
index 3a342704439e..756ca6206cb4 100644
--- a/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts
+++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts
@@ -28,14 +28,17 @@ import {
convertRatingToRatingValue,
} from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput';
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
+import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
import { getEmptyRecordGqlOperationFilter } from '@/object-record/record-filter/utils/getEmptyRecordGqlOperationFilter';
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator';
import { resolveFilterValue } from '@/views/view-filter-value/utils/resolveFilterValue';
+import { relationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/relationFilterValueSchema';
import { endOfDay, roundToNearestMinutes, startOfDay } from 'date-fns';
import { z } from 'zod';
const computeFilterRecordGqlOperationFilter = (
+ filterValueDependencies: FilterValueDependencies,
filter: Filter,
fields: Pick[],
): RecordGqlOperationFilter | undefined => {
@@ -303,32 +306,41 @@ const computeFilterRecordGqlOperationFilter = (
}
case 'RELATION': {
if (!isEmptyOperand) {
- try {
- JSON.parse(filter.value);
- } catch (e) {
- throw new Error(
- `Cannot parse filter value for RELATION filter : "${filter.value}"`,
- );
- }
+ const { isCurrentWorkspaceMemberSelected, selectedRecordIds } =
+ relationFilterValueSchema.parse(filter.value);
- const parsedRecordIds = JSON.parse(filter.value) as string[];
+ const recordIds = isCurrentWorkspaceMemberSelected
+ ? [
+ ...selectedRecordIds,
+ filterValueDependencies.currentWorkspaceMemberId,
+ ]
+ : selectedRecordIds;
- if (parsedRecordIds.length === 0) return;
+ if (recordIds.length === 0) return;
switch (filter.operand) {
case ViewFilterOperand.Is:
return {
[correspondingField.name + 'Id']: {
- in: parsedRecordIds,
+ in: recordIds,
} as RelationFilter,
};
case ViewFilterOperand.IsNot: {
- if (parsedRecordIds.length === 0) return;
+ if (recordIds.length === 0) return;
return {
- not: {
- [correspondingField.name + 'Id']: {
- in: parsedRecordIds,
- } as RelationFilter,
- },
+ or: [
+ {
+ not: {
+ [correspondingField.name + 'Id']: {
+ in: recordIds,
+ } as RelationFilter,
+ },
+ },
+ {
+ [correspondingField.name + 'Id']: {
+ is: 'NULL',
+ } as RelationFilter,
+ },
+ ],
};
}
default:
@@ -869,6 +881,7 @@ const computeFilterRecordGqlOperationFilter = (
};
const computeViewFilterGroupRecordGqlOperationFilter = (
+ filterValueDependencies: FilterValueDependencies,
filters: Filter[],
fields: Pick[],
viewFilterGroups: ViewFilterGroup[],
@@ -887,7 +900,13 @@ const computeViewFilterGroupRecordGqlOperationFilter = (
);
const groupRecordGqlOperationFilters = groupFilters
- .map((filter) => computeFilterRecordGqlOperationFilter(filter, fields))
+ .map((filter) =>
+ computeFilterRecordGqlOperationFilter(
+ filterValueDependencies,
+ filter,
+ fields,
+ ),
+ )
.filter(isDefined);
const subGroupRecordGqlOperationFilters = viewFilterGroups
@@ -897,6 +916,7 @@ const computeViewFilterGroupRecordGqlOperationFilter = (
)
.map((subViewFilterGroup) =>
computeViewFilterGroupRecordGqlOperationFilter(
+ filterValueDependencies,
filters,
fields,
viewFilterGroups,
@@ -932,6 +952,7 @@ const computeViewFilterGroupRecordGqlOperationFilter = (
};
export const computeViewRecordGqlOperationFilter = (
+ filterValueDependencies: FilterValueDependencies,
filters: Filter[],
fields: Pick[],
viewFilterGroups: ViewFilterGroup[],
@@ -939,7 +960,11 @@ export const computeViewRecordGqlOperationFilter = (
const regularRecordGqlOperationFilter: RecordGqlOperationFilter[] = filters
.filter((filter) => !filter.viewFilterGroupId)
.map((regularFilter) =>
- computeFilterRecordGqlOperationFilter(regularFilter, fields),
+ computeFilterRecordGqlOperationFilter(
+ filterValueDependencies,
+ regularFilter,
+ fields,
+ ),
)
.filter(isDefined);
@@ -949,6 +974,7 @@ export const computeViewRecordGqlOperationFilter = (
const advancedRecordGqlOperationFilter =
computeViewFilterGroupRecordGqlOperationFilter(
+ filterValueDependencies,
filters,
fields,
viewFilterGroups,
diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect.tsx
index 375e5cfc02c2..28445351179f 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect.tsx
@@ -5,6 +5,7 @@ import { computeContextStoreFilters } from '@/context-store/utils/computeContext
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
+import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@@ -40,6 +41,8 @@ export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect =
contextStoreFiltersComponentState,
);
+ const { filterValueDependencies } = useFilterValueDependencies();
+
const { totalCount } = useFindManyRecords({
...findManyRecordsParams,
recordGqlFields: {
@@ -49,6 +52,7 @@ export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect =
contextStoreTargetedRecordsRule,
contextStoreFilters,
objectMetadataItem,
+ filterValueDependencies,
),
limit: 1,
skip: contextStoreTargetedRecordsRule.mode === 'selection',
diff --git a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts
index 985d2262846d..7acaf23599fc 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts
@@ -8,6 +8,7 @@ import { computeContextStoreFilters } from '@/context-store/utils/computeContext
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/object-options-dropdown/constants/ExportTableDataDefaultPageSize';
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
+import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
@@ -71,10 +72,13 @@ export const useExportFetchRecords = ({
contextStoreFiltersComponentState,
);
+ const { filterValueDependencies } = useFilterValueDependencies();
+
const queryFilter = computeContextStoreFilters(
contextStoreTargetedRecordsRule,
contextStoreFilters,
objectMetadataItem,
+ filterValueDependencies,
);
const findManyRecordsParams = useFindManyRecordIndexTableParams(
diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useFindManyRecordIndexTableParams.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useFindManyRecordIndexTableParams.ts
index c05d39917037..df15ba3715c5 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useFindManyRecordIndexTableParams.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useFindManyRecordIndexTableParams.ts
@@ -1,5 +1,6 @@
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
+import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
@@ -35,7 +36,10 @@ export const useFindManyRecordIndexTableParams = (
recordTableId,
);
+ const { filterValueDependencies } = useFilterValueDependencies();
+
const stateFilter = computeViewRecordGqlOperationFilter(
+ filterValueDependencies,
tableFilters,
objectMetadataItem?.fields ?? [],
tableViewFilterGroups,
diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts
index fee8cc97aa9a..9df7f7a2919f 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts
@@ -7,6 +7,7 @@ import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils
import { useSetRecordBoardRecordIds } from '@/object-record/record-board/hooks/useSetRecordBoardRecordIds';
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
+import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
@@ -56,7 +57,11 @@ export const useLoadRecordIndexBoard = ({
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
const recordIndexSorts = useRecoilValue(recordIndexSortsState);
+
+ const { filterValueDependencies } = useFilterValueDependencies();
+
const requestFilters = computeViewRecordGqlOperationFilter(
+ filterValueDependencies,
recordIndexFilters,
objectMetadataItem?.fields ?? [],
recordIndexViewFilterGroups,
diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts
index 2738118054fe..ff60d05d014c 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts
@@ -5,6 +5,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/useSetRecordIdsForColumn';
+import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
@@ -43,7 +44,10 @@ export const useLoadRecordIndexBoardColumn = ({
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
const recordIndexSorts = useRecoilValue(recordIndexSortsState);
+ const { filterValueDependencies } = useFilterValueDependencies();
+
const requestFilters = computeViewRecordGqlOperationFilter(
+ filterValueDependencies,
recordIndexFilters,
objectMetadataItem?.fields ?? [],
recordIndexViewFilterGroups,
diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx
index c9851f04d118..c9217a78aa94 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx
@@ -1,5 +1,6 @@
import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel';
+import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
@@ -26,7 +27,11 @@ export const useAggregateRecordsForRecordTableColumnFooter = (
);
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
+
+ const { filterValueDependencies } = useFilterValueDependencies();
+
const requestFilters = computeViewRecordGqlOperationFilter(
+ filterValueDependencies,
recordIndexFilters,
objectMetadataItem.fields,
recordIndexViewFilterGroups,
diff --git a/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx b/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx
index 3a96cc523630..bb88fbdecea3 100644
--- a/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx
+++ b/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx
@@ -1,9 +1,9 @@
-import styled from '@emotion/styled';
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
-import { AvatarChip, MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui';
+import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui';
+import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip';
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@@ -13,16 +13,6 @@ import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/inter
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
-const StyledAvatarChip = styled(AvatarChip)`
- &.avatar-icon-container {
- color: ${({ theme }) => theme.font.color.secondary};
- gap: ${({ theme }) => theme.spacing(2)};
- padding-left: 0px;
- padding-right: 0px;
- font-size: ${({ theme }) => theme.font.size.md};
- }
-`;
-
export const MultipleSelectDropdown = ({
selectableListId,
hotkeyScope,
@@ -129,7 +119,7 @@ export const MultipleSelectDropdown = ({
handleItemSelectChange(item, newCheckedValue);
}}
avatar={
- theme.font.color.secondary};
+ gap: ${({ theme }) => theme.spacing(2)};
+ padding-left: 0px;
+ padding-right: 0px;
+ font-size: ${({ theme }) => theme.font.size.md};
+ }
+`;
diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterEffect.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterEffect.tsx
index 4e3836f6ef62..7d8817adf247 100644
--- a/packages/twenty-front/src/modules/views/components/ViewBarFilterEffect.tsx
+++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterEffect.tsx
@@ -10,6 +10,7 @@ import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-sta
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
+import { relationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/relationFilterValueSchema';
import { isDefined } from '~/utils/isDefined';
type ViewBarFilterEffectProps = {
@@ -69,12 +70,14 @@ export const ViewBarFilterEffect = ({
filterDefinitionUsedInDropdown?.fieldMetadataId,
);
- const viewFilterSelectedRecords = isNonEmptyString(
- viewFilterUsedInDropdown?.value,
- )
- ? JSON.parse(viewFilterUsedInDropdown.value)
- : [];
- setObjectFilterDropdownSelectedRecordIds(viewFilterSelectedRecords);
+ const { selectedRecordIds } = relationFilterValueSchema
+ .catch({
+ isCurrentWorkspaceMemberSelected: false,
+ selectedRecordIds: [],
+ })
+ .parse(viewFilterUsedInDropdown?.value);
+
+ setObjectFilterDropdownSelectedRecordIds(selectedRecordIds);
} else if (
isDefined(filterDefinitionUsedInDropdown) &&
['SELECT', 'MULTI_SELECT'].includes(filterDefinitionUsedInDropdown.type)
diff --git a/packages/twenty-front/src/modules/views/hooks/useQueryVariablesFromActiveFieldsOfViewOrDefaultView.ts b/packages/twenty-front/src/modules/views/hooks/useQueryVariablesFromActiveFieldsOfViewOrDefaultView.ts
index 4b28e5e781f9..7e31b313f885 100644
--- a/packages/twenty-front/src/modules/views/hooks/useQueryVariablesFromActiveFieldsOfViewOrDefaultView.ts
+++ b/packages/twenty-front/src/modules/views/hooks/useQueryVariablesFromActiveFieldsOfViewOrDefaultView.ts
@@ -1,5 +1,6 @@
import { useActiveFieldMetadataItems } from '@/object-metadata/hooks/useActiveFieldMetadataItems';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
+import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { useViewOrDefaultViewFromPrefetchedViews } from '@/views/hooks/useViewOrDefaultViewFromPrefetchedViews';
import { getQueryVariablesFromView } from '@/views/utils/getQueryVariablesFromView';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
@@ -22,11 +23,14 @@ export const useQueryVariablesFromActiveFieldsOfViewOrDefaultView = ({
const isJsonFilterEnabled = useIsFeatureEnabled('IS_JSON_FILTER_ENABLED');
+ const { filterValueDependencies } = useFilterValueDependencies();
+
const { filter, orderBy } = getQueryVariablesFromView({
fieldMetadataItems: activeFieldMetadataItems,
objectMetadataItem,
view,
isJsonFilterEnabled,
+ filterValueDependencies,
});
return {
diff --git a/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts b/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts
index 7768035b6359..75eb01302fd2 100644
--- a/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts
+++ b/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts
@@ -3,6 +3,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { formatFieldMetadataItemsAsFilterDefinitions } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { formatFieldMetadataItemsAsSortDefinitions } from '@/object-metadata/utils/formatFieldMetadataItemsAsSortDefinitions';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
+import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { View } from '@/views/types/View';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
@@ -14,11 +15,13 @@ export const getQueryVariablesFromView = ({
fieldMetadataItems,
objectMetadataItem,
isJsonFilterEnabled,
+ filterValueDependencies,
}: {
view: View | null | undefined;
fieldMetadataItems: FieldMetadataItem[];
objectMetadataItem: ObjectMetadataItem;
isJsonFilterEnabled: boolean;
+ filterValueDependencies: FilterValueDependencies;
}) => {
if (!isDefined(view)) {
return {
@@ -39,6 +42,7 @@ export const getQueryVariablesFromView = ({
});
const filter = computeViewRecordGqlOperationFilter(
+ filterValueDependencies,
mapViewFiltersToFilters(viewFilters, filterDefinitions),
objectMetadataItem?.fields ?? [],
viewFilterGroups ?? [],
diff --git a/packages/twenty-front/src/modules/views/view-filter-value/types/RelationFilterValue.ts b/packages/twenty-front/src/modules/views/view-filter-value/types/RelationFilterValue.ts
new file mode 100644
index 000000000000..321fa8a93432
--- /dev/null
+++ b/packages/twenty-front/src/modules/views/view-filter-value/types/RelationFilterValue.ts
@@ -0,0 +1,4 @@
+import { relationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/relationFilterValueSchema';
+import { z } from 'zod';
+
+export type RelationFilterValue = z.infer;
diff --git a/packages/twenty-front/src/modules/views/view-filter-value/validation-schemas/relationFilterValueSchema.ts b/packages/twenty-front/src/modules/views/view-filter-value/validation-schemas/relationFilterValueSchema.ts
new file mode 100644
index 000000000000..e8bbfa87fbbc
--- /dev/null
+++ b/packages/twenty-front/src/modules/views/view-filter-value/validation-schemas/relationFilterValueSchema.ts
@@ -0,0 +1,21 @@
+import { z } from 'zod';
+
+export const relationFilterValueSchema = z
+ .string()
+ .transform((value, ctx) => {
+ try {
+ return JSON.parse(value);
+ } catch (error) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message: (error as Error).message,
+ });
+ return z.NEVER;
+ }
+ })
+ .pipe(
+ z.object({
+ isCurrentWorkspaceMemberSelected: z.boolean(),
+ selectedRecordIds: z.array(z.string()),
+ }),
+ );