Removed availableFilterDefinitions as a state but kept its usage as a derived state of objectMetadataItems (#9972)

The global record filter refactor will derive everything at runtime from
objectMetadataItemsState, thus removing the need for a filter definition
concept.

Here we don't yet remove available filter definition usage but we
replace the available filter definitions states, we now derive the same
value from objectMetadataItemsState.

This will allow us to progressively remove the usage of the concept of
filter definition, at the end it will then be easy to just remove from
the codebase because nothing will use it anymore.
This commit is contained in:
Lucas Bordeau
2025-02-03 17:29:57 +01:00
committed by GitHub
parent c8af90dc01
commit b29ff9b4e6
32 changed files with 566 additions and 423 deletions

View File

@ -0,0 +1,41 @@
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { getFilterFilterableFieldMetadataItems } from '@/object-metadata/utils/getFilterFilterableFieldMetadataItems';
import { checkIfFeatureFlagIsEnabledOnWorkspace } from '@/workspace/utils/checkIfFeatureFlagIsEnabledOnWorkspace';
import { selectorFamily } from 'recoil';
import { isDefined } from 'twenty-shared';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
export const availableFieldMetadataItemsForFilterFamilySelector =
selectorFamily({
key: 'availableFieldMetadataItemsForFilterFamilySelector',
get:
({ objectMetadataItemId }: { objectMetadataItemId: string }) =>
({ get }) => {
const currentWorkspace = get(currentWorkspaceState);
const objectMetadataItems = get(objectMetadataItemsState);
const objectMetadataItem = objectMetadataItems.find(
(item) => item.id === objectMetadataItemId,
);
if (!isDefined(objectMetadataItem)) {
return [];
}
const isJsonFeatureFlagEnabled = checkIfFeatureFlagIsEnabledOnWorkspace(
FeatureFlagKey.IsJsonFilterEnabled,
currentWorkspace,
);
const filterFilterableFieldMetadataItems =
getFilterFilterableFieldMetadataItems({
isJsonFilterEnabled: isJsonFeatureFlagEnabled,
});
const availableFieldMetadataItemsForFilter =
objectMetadataItem.fields.filter(filterFilterableFieldMetadataItems);
return availableFieldMetadataItemsForFilter;
},
});

View File

@ -0,0 +1,52 @@
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import {
FieldMetadataType,
RelationDefinitionType,
} from '~/generated-metadata/graphql';
export const getFilterFilterableFieldMetadataItems = ({
isJsonFilterEnabled,
}: {
isJsonFilterEnabled: boolean;
}) => {
return (field: FieldMetadataItem) => {
const isSystemField = field.isSystem;
const isFieldActive = field.isActive;
const isRelationFieldHandled = !(
field.type === FieldMetadataType.RELATION &&
field.relationDefinition?.direction !==
RelationDefinitionType.MANY_TO_ONE &&
field.relationDefinition?.direction !== RelationDefinitionType.ONE_TO_ONE
);
const isFieldTypeFilterable = [
FieldMetadataType.BOOLEAN,
FieldMetadataType.DATE_TIME,
FieldMetadataType.DATE,
FieldMetadataType.TEXT,
FieldMetadataType.EMAILS,
FieldMetadataType.NUMBER,
FieldMetadataType.LINKS,
FieldMetadataType.FULL_NAME,
FieldMetadataType.ADDRESS,
FieldMetadataType.RELATION,
FieldMetadataType.SELECT,
FieldMetadataType.MULTI_SELECT,
FieldMetadataType.CURRENCY,
FieldMetadataType.RATING,
FieldMetadataType.ACTOR,
FieldMetadataType.PHONES,
FieldMetadataType.ARRAY,
...(isJsonFilterEnabled ? [FieldMetadataType.RAW_JSON] : []),
].includes(field.type);
const isFieldFilterable =
!isSystemField &&
isFieldActive &&
isRelationFieldHandled &&
isFieldTypeFilterable;
return isFieldFilterable;
};
};

View File

@ -1,17 +1,18 @@
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup'; import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup';
import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition'; import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters'; import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup'; import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator'; import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared'; import { isDefined } from 'twenty-shared';
import { IconLibraryPlus, IconPlus, LightButton, MenuItem } from 'twenty-ui'; import { IconLibraryPlus, IconPlus, LightButton, MenuItem } from 'twenty-ui';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
@ -41,7 +42,7 @@ export const AdvancedFilterAddFilterRuleSelect = ({
const objectMetadataId = const objectMetadataId =
currentViewWithCombinedFiltersAndSorts?.objectMetadataId; currentViewWithCombinedFiltersAndSorts?.objectMetadataId;
if (!objectMetadataId) { if (!isDefined(objectMetadataId)) {
throw new Error('Object metadata id is missing from current view'); throw new Error('Object metadata id is missing from current view');
} }
@ -49,33 +50,45 @@ export const AdvancedFilterAddFilterRuleSelect = ({
objectId: objectMetadataId, objectId: objectMetadataId,
}); });
const availableFilterDefinitions = useRecoilComponentValueV2( const availableFieldMetadataItemsForFilter = useRecoilValue(
availableFilterDefinitionsComponentState, availableFieldMetadataItemsForFilterFamilySelector({
objectMetadataItemId: objectMetadataId,
}),
); );
const getDefaultFilterDefinition = useCallback(() => { const getDefaultFieldMetadataItem = useCallback(() => {
const defaultFilterDefinition = const defaultFieldMetadataItem =
availableFilterDefinitions.find( availableFieldMetadataItemsForFilter.find(
(filterDefinition) => (fieldMetadataItem) =>
filterDefinition.fieldMetadataId === fieldMetadataItem.id ===
objectMetadataItem?.labelIdentifierFieldMetadataId, objectMetadataItem?.labelIdentifierFieldMetadataId,
) ?? availableFilterDefinitions?.[0]; ) ?? availableFieldMetadataItemsForFilter[0];
if (!defaultFilterDefinition) { if (!isDefined(defaultFieldMetadataItem)) {
throw new Error('Missing default filter definition'); throw new Error(
`Could not find default field metadata item for object ${objectMetadataId}`,
);
} }
return defaultFilterDefinition; return defaultFieldMetadataItem;
}, [availableFilterDefinitions, objectMetadataItem]); }, [
availableFieldMetadataItemsForFilter,
objectMetadataItem,
objectMetadataId,
]);
const handleAddFilter = () => { const handleAddFilter = () => {
closeDropdown(); closeDropdown();
const defaultFilterDefinition = getDefaultFilterDefinition(); const defaultFieldMetadataItem = getDefaultFieldMetadataItem();
const defaultFilterDefinition = formatFieldMetadataItemAsFilterDefinition({
field: defaultFieldMetadataItem,
});
upsertCombinedViewFilter({ upsertCombinedViewFilter({
id: v4(), id: v4(),
fieldMetadataId: defaultFilterDefinition.fieldMetadataId, fieldMetadataId: defaultFieldMetadataItem.id,
operand: getRecordFilterOperandsForRecordFilterDefinition( operand: getRecordFilterOperandsForRecordFilterDefinition(
defaultFilterDefinition, defaultFilterDefinition,
)[0], )[0],
@ -104,11 +117,15 @@ export const AdvancedFilterAddFilterRuleSelect = ({
upsertCombinedViewFilterGroup(newViewFilterGroup); upsertCombinedViewFilterGroup(newViewFilterGroup);
const defaultFilterDefinition = getDefaultFilterDefinition(); const defaultFieldMetadataItem = getDefaultFieldMetadataItem();
const defaultFilterDefinition = formatFieldMetadataItemAsFilterDefinition({
field: defaultFieldMetadataItem,
});
upsertCombinedViewFilter({ upsertCombinedViewFilter({
id: v4(), id: v4(),
fieldMetadataId: defaultFilterDefinition.fieldMetadataId, fieldMetadataId: defaultFieldMetadataItem.id,
operand: getRecordFilterOperandsForRecordFilterDefinition( operand: getRecordFilterOperandsForRecordFilterDefinition(
defaultFilterDefinition, defaultFilterDefinition,
)[0], )[0],

View File

@ -1,6 +1,5 @@
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useFilterDefinitionsFromFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterDefinitionsFromFilterableFieldMetadataItems';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters'; import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
export const useCurrentViewFilter = ({ export const useCurrentViewFilter = ({
@ -8,9 +7,8 @@ export const useCurrentViewFilter = ({
}: { }: {
viewFilterId?: string; viewFilterId?: string;
}) => { }) => {
const availableFilterDefinitions = useRecoilComponentValueV2( const { filterDefinitions } =
availableFilterDefinitionsComponentState, useFilterDefinitionsFromFilterableFieldMetadataItems();
);
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView(); const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
@ -22,10 +20,7 @@ export const useCurrentViewFilter = ({
return undefined; return undefined;
} }
const [filter] = mapViewFiltersToFilters( const [filter] = mapViewFiltersToFilters([viewFilter], filterDefinitions);
[viewFilter],
availableFilterDefinitions,
);
return filter; return filter;
}; };

View File

@ -1,15 +1,17 @@
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup'; import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup';
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition'; import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters'; import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator'; import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared';
import { import {
IconFilter, IconFilter,
MenuItemLeftContent, MenuItemLeftContent,
@ -66,8 +68,10 @@ export const AdvancedFilterButton = () => {
objectId: objectMetadataId ?? null, objectId: objectMetadataId ?? null,
}); });
const availableFilterDefinitions = useRecoilComponentValueV2( const availableFieldMetadataItemsForFilter = useRecoilValue(
availableFilterDefinitionsComponentState, availableFieldMetadataItemsForFilterFamilySelector({
objectMetadataItemId: objectMetadataItem.id,
}),
); );
const handleClick = () => { const handleClick = () => {
@ -88,24 +92,27 @@ export const AdvancedFilterButton = () => {
upsertCombinedViewFilterGroup(newViewFilterGroup); upsertCombinedViewFilterGroup(newViewFilterGroup);
const defaultFilterDefinition = const defaultFieldMetadataItem =
availableFilterDefinitions.find( availableFieldMetadataItemsForFilter.find(
(filterDefinition) => (fieldMetadataItem) =>
filterDefinition.fieldMetadataId === fieldMetadataItem.id ===
objectMetadataItem?.labelIdentifierFieldMetadataId, objectMetadataItem?.labelIdentifierFieldMetadataId,
) ?? availableFilterDefinitions?.[0]; ) ?? availableFieldMetadataItemsForFilter[0];
if (!defaultFilterDefinition) { if (!isDefined(defaultFieldMetadataItem)) {
throw new Error('Missing default filter definition'); throw new Error('Missing default filter definition');
} }
const filterDefinition = formatFieldMetadataItemAsFilterDefinition({
field: defaultFieldMetadataItem,
});
upsertCombinedViewFilter({ upsertCombinedViewFilter({
id: v4(), id: v4(),
fieldMetadataId: defaultFilterDefinition.fieldMetadataId, fieldMetadataId: defaultFieldMetadataItem.id,
operand: getRecordFilterOperandsForRecordFilterDefinition( operand:
defaultFilterDefinition, getRecordFilterOperandsForRecordFilterDefinition(filterDefinition)[0],
)[0], definition: filterDefinition,
definition: defaultFilterDefinition,
value: '', value: '',
displayValue: '', displayValue: '',
viewFilterGroupId: newViewFilterGroup.id, viewFilterGroupId: newViewFilterGroup.id,

View File

@ -1,8 +1,7 @@
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { MultipleFiltersDropdownButton } from './MultipleFiltersDropdownButton'; import { MultipleFiltersDropdownButton } from './MultipleFiltersDropdownButton';
import { SingleEntityObjectFilterDropdownButton } from './SingleEntityObjectFilterDropdownButton'; import { SingleEntityObjectFilterDropdownButton } from './SingleEntityObjectFilterDropdownButton';
@ -15,16 +14,13 @@ export const ObjectFilterDropdownButton = ({
filterDropdownId, filterDropdownId,
hotkeyScope, hotkeyScope,
}: ObjectFilterDropdownButtonProps) => { }: ObjectFilterDropdownButtonProps) => {
const availableFilterDefinitions = useRecoilComponentValueV2( const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems();
availableFilterDefinitionsComponentState,
filterDropdownId,
);
const hasOnlyOneEntityFilter = const hasOnlyOneEntityFilter =
availableFilterDefinitions.length === 1 && filterableFieldMetadataItems.length === 1 &&
availableFilterDefinitions[0].type === 'RELATION'; filterableFieldMetadataItems[0].type === 'RELATION';
if (!availableFilterDefinitions.length) { if (!filterableFieldMetadataItems.length) {
return <></>; return <></>;
} }

View File

@ -19,14 +19,15 @@ import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectab
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { isDefined } from 'twenty-shared'; import { isDefined } from 'twenty-shared';
import { FeatureFlagKey } from '~/generated/graphql'; import { FeatureFlagKey } from '~/generated/graphql';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState'; import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope'; import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
import { useLingui } from '@lingui/react/macro'; import { useLingui } from '@lingui/react/macro';
export const StyledInput = styled.input` export const StyledInput = styled.input`
@ -80,9 +81,7 @@ export const ObjectFilterDropdownFilterSelect = ({
advancedFilterViewFilterId, advancedFilterViewFilterId,
); );
const availableFilterDefinitions = useRecoilComponentValueV2( const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems();
availableFilterDefinitionsComponentState,
);
const visibleTableColumns = useRecoilComponentValueV2( const visibleTableColumns = useRecoilComponentValueV2(
visibleTableColumnsComponentSelector, visibleTableColumnsComponentSelector,
@ -99,29 +98,29 @@ export const ObjectFilterDropdownFilterSelect = ({
(column) => column.fieldMetadataId, (column) => column.fieldMetadataId,
); );
const filteredSearchInputFilterDefinitions = const filteredSearchInputFieldMetadataItems =
availableFilterDefinitions.filter((item) => filterableFieldMetadataItems.filter((fieldMetadataItem) =>
item.label fieldMetadataItem.label
.toLocaleLowerCase() .toLocaleLowerCase()
.includes(objectFilterDropdownSearchInput.toLocaleLowerCase()), .includes(objectFilterDropdownSearchInput.toLocaleLowerCase()),
); );
const visibleColumnsFilterDefinitions = filteredSearchInputFilterDefinitions const visibleColumnsFieldMetadataItems = filteredSearchInputFieldMetadataItems
.sort((a, b) => { .sort((a, b) => {
return ( return visibleColumnsIds.indexOf(a.id) - visibleColumnsIds.indexOf(b.id);
visibleColumnsIds.indexOf(a.fieldMetadataId) -
visibleColumnsIds.indexOf(b.fieldMetadataId)
);
}) })
.filter((item) => visibleColumnsIds.includes(item.fieldMetadataId)); .filter((fieldMetadataItem) =>
visibleColumnsIds.includes(fieldMetadataItem.id),
);
const hiddenColumnsFilterDefinitions = filteredSearchInputFilterDefinitions const hiddenColumnsFieldMetadataItems = filteredSearchInputFieldMetadataItems
.sort((a, b) => a.label.localeCompare(b.label)) .sort((a, b) => a.label.localeCompare(b.label))
.filter((item) => hiddenColumnIds.includes(item.fieldMetadataId)); .filter((fieldMetadataItem) =>
hiddenColumnIds.includes(fieldMetadataItem.id),
);
const selectableListItemIds = availableFilterDefinitions.map( const selectableFieldMetadataItemIds = filterableFieldMetadataItems.map(
(item) => item.fieldMetadataId, (fieldMetadataItem) => fieldMetadataItem.id,
); );
const { selectFilterDefinitionUsedInDropdown } = const { selectFilterDefinitionUsedInDropdown } =
@ -134,16 +133,20 @@ export const ObjectFilterDropdownFilterSelect = ({
const { resetSelectedItem } = useSelectableList(OBJECT_FILTER_DROPDOWN_ID); const { resetSelectedItem } = useSelectableList(OBJECT_FILTER_DROPDOWN_ID);
const handleEnter = (fieldMetadataItemId: string) => { const handleEnter = (fieldMetadataItemId: string) => {
const selectedFilterDefinition = availableFilterDefinitions.find( const selectedFieldMetadataItem = filterableFieldMetadataItems.find(
(item) => item.fieldMetadataId === fieldMetadataItemId, (fieldMetadataItem) => fieldMetadataItem.id === fieldMetadataItemId,
); );
if (!isDefined(selectedFilterDefinition)) { if (!isDefined(selectedFieldMetadataItem)) {
return; return;
} }
resetSelectedItem(); resetSelectedItem();
const selectedFilterDefinition = formatFieldMetadataItemAsFilterDefinition({
field: selectedFieldMetadataItem,
});
selectFilterDefinitionUsedInDropdown({ selectFilterDefinitionUsedInDropdown({
filterDefinition: selectedFilterDefinition, filterDefinition: selectedFilterDefinition,
}); });
@ -156,8 +159,8 @@ export const ObjectFilterDropdownFilterSelect = ({
}; };
const shoudShowSeparator = const shoudShowSeparator =
visibleColumnsFilterDefinitions.length > 0 && visibleColumnsFieldMetadataItems.length > 0 &&
hiddenColumnsFilterDefinitions.length > 0; hiddenColumnsFieldMetadataItems.length > 0;
const { currentViewId, currentViewWithCombinedFiltersAndSorts } = const { currentViewId, currentViewWithCombinedFiltersAndSorts } =
useGetCurrentView(); useGetCurrentView();
@ -186,32 +189,32 @@ export const ObjectFilterDropdownFilterSelect = ({
/> />
<SelectableList <SelectableList
hotkeyScope={FiltersHotkeyScope.ObjectFilterDropdownButton} hotkeyScope={FiltersHotkeyScope.ObjectFilterDropdownButton}
selectableItemIdArray={selectableListItemIds} selectableItemIdArray={selectableFieldMetadataItemIds}
selectableListId={OBJECT_FILTER_DROPDOWN_ID} selectableListId={OBJECT_FILTER_DROPDOWN_ID}
onEnter={handleEnter} onEnter={handleEnter}
> >
<DropdownMenuItemsContainer> <DropdownMenuItemsContainer>
{visibleColumnsFilterDefinitions.map( {visibleColumnsFieldMetadataItems.map(
(visibleFilterDefinition, index) => ( (visibleFieldMetadataItem, index) => (
<SelectableItem <SelectableItem
itemId={visibleFilterDefinition.fieldMetadataId} itemId={visibleFieldMetadataItem.id}
key={`visible-select-filter-${index}`} key={`visible-select-filter-${index}`}
> >
<ObjectFilterDropdownFilterSelectMenuItem <ObjectFilterDropdownFilterSelectMenuItem
filterDefinition={visibleFilterDefinition} fieldMetadataItemToSelect={visibleFieldMetadataItem}
/> />
</SelectableItem> </SelectableItem>
), ),
)} )}
{shoudShowSeparator && <DropdownMenuSeparator />} {shoudShowSeparator && <DropdownMenuSeparator />}
{hiddenColumnsFilterDefinitions.map( {hiddenColumnsFieldMetadataItems.map(
(hiddenFilterDefinition, index) => ( (hiddenFieldMetadataItem, index) => (
<SelectableItem <SelectableItem
itemId={hiddenFilterDefinition.fieldMetadataId} itemId={hiddenFieldMetadataItem.id}
key={`hidden-select-filter-${index}`} key={`hidden-select-filter-${index}`}
> >
<ObjectFilterDropdownFilterSelectMenuItem <ObjectFilterDropdownFilterSelectMenuItem
filterDefinition={hiddenFilterDefinition} fieldMetadataItemToSelect={hiddenFieldMetadataItem}
/> />
</SelectableItem> </SelectableItem>
), ),

View File

@ -1,7 +1,5 @@
import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown'; import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown';
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
import { useSelectFilterDefinitionUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterDefinitionUsedInDropdown';
import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState'; import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
@ -9,8 +7,12 @@ import { objectFilterDropdownFirstLevelFilterDefinitionComponentState } from '@/
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState'; import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
import { CompositeFilterableFieldType } from '@/object-record/record-filter/types/CompositeFilterableFieldType'; import { CompositeFilterableFieldType } from '@/object-record/record-filter/types/CompositeFilterableFieldType';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition'; import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition'; import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
@ -23,15 +25,12 @@ import { useRecoilValue } from 'recoil';
import { MenuItemSelect, useIcons } from 'twenty-ui'; import { MenuItemSelect, useIcons } from 'twenty-ui';
export type ObjectFilterDropdownFilterSelectMenuItemProps = { export type ObjectFilterDropdownFilterSelectMenuItemProps = {
filterDefinition: RecordFilterDefinition; fieldMetadataItemToSelect: FieldMetadataItem;
}; };
export const ObjectFilterDropdownFilterSelectMenuItem = ({ export const ObjectFilterDropdownFilterSelectMenuItem = ({
filterDefinition, fieldMetadataItemToSelect,
}: ObjectFilterDropdownFilterSelectMenuItemProps) => { }: ObjectFilterDropdownFilterSelectMenuItemProps) => {
const { selectFilterDefinitionUsedInDropdown } =
useSelectFilterDefinitionUsedInDropdown();
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
fieldMetadataItemIdUsedInDropdownComponentState, fieldMetadataItemIdUsedInDropdownComponentState,
); );
@ -58,16 +57,24 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
OBJECT_FILTER_DROPDOWN_ID, OBJECT_FILTER_DROPDOWN_ID,
); );
const filterDefinitionToSelect = formatFieldMetadataItemAsFilterDefinition({
field: fieldMetadataItemToSelect,
});
const isSelectedItem = useRecoilValue( const isSelectedItem = useRecoilValue(
isSelectedItemIdSelector(filterDefinition.fieldMetadataId), isSelectedItemIdSelector(fieldMetadataItemToSelect.id),
); );
const isACompositeField = isCompositeField(filterDefinition.type); const isACompositeField = isCompositeField(fieldMetadataItemToSelect.type);
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2( const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
selectedOperandInDropdownComponentState, selectedOperandInDropdownComponentState,
); );
const setFilterDefinitionUsedInDropdown = useSetRecoilComponentStateV2(
filterDefinitionUsedInDropdownComponentState,
);
const advancedFilterViewFilterId = useRecoilComponentValueV2( const advancedFilterViewFilterId = useRecoilComponentValueV2(
advancedFilterViewFilterIdComponentState, advancedFilterViewFilterIdComponentState,
); );
@ -83,13 +90,10 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
) => { ) => {
closeAdvancedFilterDropdown(); closeAdvancedFilterDropdown();
selectFilterDefinitionUsedInDropdown({
filterDefinition: availableFilterDefinition,
});
setFieldMetadataItemIdUsedInDropdown( setFieldMetadataItemIdUsedInDropdown(
availableFilterDefinition.fieldMetadataId, availableFilterDefinition.fieldMetadataId,
); );
setFilterDefinitionUsedInDropdown(availableFilterDefinition);
if ( if (
availableFilterDefinition.type === 'RELATION' || availableFilterDefinition.type === 'RELATION' ||
@ -115,12 +119,14 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
if (isACompositeField) { if (isACompositeField) {
// TODO: create isCompositeFilterableFieldType type guard // TODO: create isCompositeFilterableFieldType type guard
setObjectFilterDropdownSubMenuFieldType( setObjectFilterDropdownSubMenuFieldType(
filterDefinition.type as CompositeFilterableFieldType, filterDefinitionToSelect.type as CompositeFilterableFieldType,
);
setObjectFilterDropdownFirstLevelFilterDefinition(
filterDefinitionToSelect,
); );
setObjectFilterDropdownFirstLevelFilterDefinition(filterDefinition);
setObjectFilterDropdownIsSelectingCompositeField(true); setObjectFilterDropdownIsSelectingCompositeField(true);
} else { } else {
handleSelectFilterDefinition(filterDefinition); handleSelectFilterDefinition(filterDefinitionToSelect);
} }
}; };
@ -129,8 +135,8 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
selected={false} selected={false}
hovered={isSelectedItem} hovered={isSelectedItem}
onClick={handleClick} onClick={handleClick}
LeftIcon={getIcon(filterDefinition.iconName)} LeftIcon={getIcon(filterDefinitionToSelect.iconName)}
text={filterDefinition.label} text={filterDefinitionToSelect.label}
hasSubMenu={isACompositeField} hasSubMenu={isACompositeField}
/> />
); );

View File

@ -1,5 +1,4 @@
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import React from 'react';
import { IconChevronDown } from 'twenty-ui'; import { IconChevronDown } from 'twenty-ui';
import { ObjectFilterDropdownRecordRemoveFilterMenuItem } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordRemoveFilterMenuItem'; import { ObjectFilterDropdownRecordRemoveFilterMenuItem } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordRemoveFilterMenuItem';
@ -8,15 +7,10 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton'; import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { SingleEntityObjectFilterDropdownButtonEffect } from '@/object-record/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButtonEffect';
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { useLingui } from '@lingui/react/macro'; import { useLingui } from '@lingui/react/macro';
import { getRecordFilterOperandsForRecordFilterDefinition } from '../../record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition';
import { GenericEntityFilterChip } from './GenericEntityFilterChip'; import { GenericEntityFilterChip } from './GenericEntityFilterChip';
import { ObjectFilterDropdownRecordSelect } from './ObjectFilterDropdownRecordSelect'; import { ObjectFilterDropdownRecordSelect } from './ObjectFilterDropdownRecordSelect';
import { ObjectFilterDropdownSearchInput } from './ObjectFilterDropdownSearchInput'; import { ObjectFilterDropdownSearchInput } from './ObjectFilterDropdownSearchInput';
@ -32,68 +26,37 @@ export const SingleEntityObjectFilterDropdownButton = ({
selectedFilterComponentState, selectedFilterComponentState,
); );
const setFilterDefinitionUsedInDropdown = useSetRecoilComponentStateV2(
filterDefinitionUsedInDropdownComponentState,
);
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
fieldMetadataItemIdUsedInDropdownComponentState,
);
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
selectedOperandInDropdownComponentState,
);
const availableFilterDefinitions = useRecoilComponentValueV2(
availableFilterDefinitionsComponentState,
);
const availableFilterDefinition = availableFilterDefinitions[0];
React.useEffect(() => {
setFieldMetadataItemIdUsedInDropdown(
availableFilterDefinition.fieldMetadataId,
);
setFilterDefinitionUsedInDropdown(availableFilterDefinition);
const defaultOperand = getRecordFilterOperandsForRecordFilterDefinition(
availableFilterDefinition,
)[0];
setSelectedOperandInDropdown(defaultOperand);
}, [
availableFilterDefinition,
setFilterDefinitionUsedInDropdown,
setSelectedOperandInDropdown,
setFieldMetadataItemIdUsedInDropdown,
]);
const theme = useTheme(); const theme = useTheme();
const { t } = useLingui(); const { t } = useLingui();
return ( return (
<Dropdown <>
dropdownId={SINGLE_ENTITY_FILTER_DROPDOWN_ID} <SingleEntityObjectFilterDropdownButtonEffect />
dropdownHotkeyScope={hotkeyScope} <Dropdown
dropdownOffset={{ x: 0, y: -28 }} dropdownId={SINGLE_ENTITY_FILTER_DROPDOWN_ID}
clickableComponent={ dropdownHotkeyScope={hotkeyScope}
<StyledHeaderDropdownButton> dropdownOffset={{ x: 0, y: -28 }}
{selectedFilter ? ( clickableComponent={
<GenericEntityFilterChip filter={selectedFilter} /> <StyledHeaderDropdownButton>
) : ( {selectedFilter ? (
t`Filter` <GenericEntityFilterChip filter={selectedFilter} />
)} ) : (
<IconChevronDown size={theme.icon.size.md} /> t`Filter`
</StyledHeaderDropdownButton> )}
} <IconChevronDown size={theme.icon.size.md} />
dropdownComponents={ </StyledHeaderDropdownButton>
<> }
<ObjectFilterDropdownSearchInput /> dropdownComponents={
<DropdownMenuSeparator /> <>
<ObjectFilterDropdownRecordRemoveFilterMenuItem /> <ObjectFilterDropdownSearchInput />
<ObjectFilterDropdownRecordSelect <DropdownMenuSeparator />
viewComponentId={SINGLE_ENTITY_FILTER_DROPDOWN_ID} <ObjectFilterDropdownRecordRemoveFilterMenuItem />
/> <ObjectFilterDropdownRecordSelect
</> viewComponentId={SINGLE_ENTITY_FILTER_DROPDOWN_ID}
} />
/> </>
}
/>
</>
); );
}; };

View File

@ -0,0 +1,47 @@
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useEffect } from 'react';
export const SingleEntityObjectFilterDropdownButtonEffect = () => {
const setFilterDefinitionUsedInDropdown = useSetRecoilComponentStateV2(
filterDefinitionUsedInDropdownComponentState,
);
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
fieldMetadataItemIdUsedInDropdownComponentState,
);
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
selectedOperandInDropdownComponentState,
);
const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems();
const firstFieldMetadataItem = filterableFieldMetadataItems[0];
const firstFieldDefinition = formatFieldMetadataItemAsFilterDefinition({
field: firstFieldMetadataItem,
});
useEffect(() => {
setFieldMetadataItemIdUsedInDropdown(firstFieldDefinition.fieldMetadataId);
setFilterDefinitionUsedInDropdown(firstFieldDefinition);
const defaultOperand =
getRecordFilterOperandsForRecordFilterDefinition(firstFieldDefinition)[0];
setSelectedOperandInDropdown(defaultOperand);
}, [
firstFieldDefinition,
setFilterDefinitionUsedInDropdown,
setSelectedOperandInDropdown,
setFieldMetadataItemIdUsedInDropdown,
]);
return null;
};

View File

@ -4,7 +4,6 @@ import { TaskGroups } from '@/activities/tasks/components/TaskGroups';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition'; import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { MultipleFiltersDropdownButton } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownButton'; import { MultipleFiltersDropdownButton } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownButton';
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext'; import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
@ -12,7 +11,6 @@ import { RecordIndexContextProvider } from '@/object-record/record-index/context
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState'; import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { within } from '@storybook/test'; import { within } from '@storybook/test';
import { import {
@ -35,10 +33,6 @@ const meta: Meta<typeof MultipleFiltersDropdownButton> = {
(item) => item.nameSingular === CoreObjectNameSingular.Company, (item) => item.nameSingular === CoreObjectNameSingular.Company,
)!; )!;
const instanceId = 'entity-tasks-filter-scope'; const instanceId = 'entity-tasks-filter-scope';
const setAvailableFilterDefinitions = useSetRecoilComponentStateV2(
availableFilterDefinitionsComponentState,
instanceId,
);
const setTableColumns = useSetRecoilComponentStateV2( const setTableColumns = useSetRecoilComponentStateV2(
tableColumnsComponentState, tableColumnsComponentState,
@ -54,17 +48,8 @@ const meta: Meta<typeof MultipleFiltersDropdownButton> = {
}), }),
); );
const filterDefinitions = companyObjectMetadataItem.fields.map(
(fieldMetadataItem) =>
formatFieldMetadataItemAsFilterDefinition({
field: fieldMetadataItem,
}),
);
setTableColumns(columns); setTableColumns(columns);
setAvailableFilterDefinitions(filterDefinitions);
return ( return (
<RecordIndexContextProvider <RecordIndexContextProvider
value={{ value={{

View File

@ -1,6 +1,5 @@
import { advancedFilterViewFilterGroupIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState'; import { advancedFilterViewFilterGroupIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState';
import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState'; import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState'; import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
@ -27,11 +26,6 @@ export const useSelectFilterDefinitionUsedInDropdown = (
componentInstanceId, componentInstanceId,
); );
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
fieldMetadataItemIdUsedInDropdownComponentState,
componentInstanceId,
);
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2( const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
selectedOperandInDropdownComponentState, selectedOperandInDropdownComponentState,
componentInstanceId, componentInstanceId,
@ -60,7 +54,6 @@ export const useSelectFilterDefinitionUsedInDropdown = (
filterDefinition, filterDefinition,
}: SelectFilterParams) => { }: SelectFilterParams) => {
setFilterDefinitionUsedInDropdown(filterDefinition); setFilterDefinitionUsedInDropdown(filterDefinition);
setFieldMetadataItemIdUsedInDropdown(filterDefinition.fieldMetadataId);
if ( if (
filterDefinition.type === 'RELATION' || filterDefinition.type === 'RELATION' ||

View File

@ -1,12 +0,0 @@
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const availableFilterDefinitionsComponentState = createComponentStateV2<
RecordFilterDefinition[]
>({
key: 'availableFilterDefinitionsComponentState',
defaultValue: [],
componentInstanceContext: ObjectFilterDropdownComponentInstanceContext,
});

View File

@ -0,0 +1,23 @@
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useRecoilValue } from 'recoil';
export const useFilterDefinitionsFromFilterableFieldMetadataItems = () => {
const { objectMetadataItem } = useRecordIndexContextOrThrow();
const availableFieldMetadataItemsForFilter = useRecoilValue(
availableFieldMetadataItemsForFilterFamilySelector({
objectMetadataItemId: objectMetadataItem.id,
}),
);
const filterDefinitions = availableFieldMetadataItemsForFilter.map(
(fieldMetadataItem) =>
formatFieldMetadataItemAsFilterDefinition({
field: fieldMetadataItem,
}),
);
return { filterDefinitions };
};

View File

@ -0,0 +1,15 @@
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useRecoilValue } from 'recoil';
export const useFilterableFieldMetadataItems = () => {
const { objectMetadataItem } = useRecordIndexContextOrThrow();
const filterableFieldMetadataItems = useRecoilValue(
availableFieldMetadataItemsForFilterFamilySelector({
objectMetadataItemId: objectMetadataItem.id,
}),
);
return { filterableFieldMetadataItems };
};

View File

@ -26,6 +26,7 @@ import { RecordIndexActionMenu } from '@/action-menu/components/RecordIndexActio
import { ContextStoreCurrentViewTypeEffect } from '@/context-store/components/ContextStoreCurrentViewTypeEffect'; import { ContextStoreCurrentViewTypeEffect } from '@/context-store/components/ContextStoreCurrentViewTypeEffect';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType'; import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { useFilterDefinitionsFromFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterDefinitionsFromFilterableFieldMetadataItems';
import { useSetRecordGroup } from '@/object-record/record-group/hooks/useSetRecordGroup'; import { useSetRecordGroup } from '@/object-record/record-group/hooks/useSetRecordGroup';
import { RecordIndexFiltersToContextStoreEffect } from '@/object-record/record-index/components/RecordIndexFiltersToContextStoreEffect'; import { RecordIndexFiltersToContextStoreEffect } from '@/object-record/record-index/components/RecordIndexFiltersToContextStoreEffect';
import { RecordIndexTableContainerEffect } from '@/object-record/record-index/components/RecordIndexTableContainerEffect'; import { RecordIndexTableContainerEffect } from '@/object-record/record-index/components/RecordIndexTableContainerEffect';
@ -76,7 +77,7 @@ export const RecordIndexContainer = () => {
const setRecordGroup = useSetRecordGroup(recordIndexId); const setRecordGroup = useSetRecordGroup(recordIndexId);
const { columnDefinitions, filterDefinitions, sortDefinitions } = const { columnDefinitions, sortDefinitions } =
useColumnDefinitionsFromFieldMetadata(objectMetadataItem); useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
const setRecordIndexViewFilterGroups = useSetRecoilState( const setRecordIndexViewFilterGroups = useSetRecoilState(
@ -179,6 +180,9 @@ export const RecordIndexContainer = () => {
contextStoreTargetedRecordsRuleComponentState, contextStoreTargetedRecordsRuleComponentState,
); );
const { filterDefinitions } =
useFilterDefinitionsFromFilterableFieldMetadataItems();
const isCommandMenuV2Enabled = useIsFeatureEnabled( const isCommandMenuV2Enabled = useIsFeatureEnabled(
FeatureFlagKey.IsCommandMenuV2Enabled, FeatureFlagKey.IsCommandMenuV2Enabled,
); );

View File

@ -23,13 +23,12 @@ export const RecordIndexViewBarEffect = ({
objectNameSingular, objectNameSingular,
}); });
const { columnDefinitions, filterDefinitions, sortDefinitions } = const { columnDefinitions, sortDefinitions } =
useColumnDefinitionsFromFieldMetadata(objectMetadataItem); useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
const { const {
setViewObjectMetadataId, setViewObjectMetadataId,
setAvailableSortDefinitions, setAvailableSortDefinitions,
setAvailableFilterDefinitions,
setAvailableFieldDefinitions, setAvailableFieldDefinitions,
} = useInitViewBar(viewBarId); } = useInitViewBar(viewBarId);
@ -39,15 +38,12 @@ export const RecordIndexViewBarEffect = ({
} }
setViewObjectMetadataId?.(objectMetadataItem.id); setViewObjectMetadataId?.(objectMetadataItem.id);
setAvailableSortDefinitions?.(sortDefinitions); setAvailableSortDefinitions?.(sortDefinitions);
setAvailableFilterDefinitions?.(filterDefinitions);
setAvailableFieldDefinitions?.(columnDefinitions); setAvailableFieldDefinitions?.(columnDefinitions);
}, [ }, [
setViewObjectMetadataId, setViewObjectMetadataId,
objectMetadataItem, objectMetadataItem,
setAvailableSortDefinitions, setAvailableSortDefinitions,
sortDefinitions, sortDefinitions,
setAvailableFilterDefinitions,
filterDefinitions,
setAvailableFieldDefinitions, setAvailableFieldDefinitions,
columnDefinitions, columnDefinitions,
]); ]);

View File

@ -4,19 +4,19 @@ import { v4 } from 'uuid';
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata'; import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useSelectFilterDefinitionUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterDefinitionUsedInDropdown'; import { useSelectFilterDefinitionUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterDefinitionUsedInDropdown';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter'; import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition'; import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters'; import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; import { useRecoilCallback, useRecoilValue } from 'recoil';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared'; import { isDefined } from 'twenty-shared';
type UseHandleToggleColumnFilterProps = { type UseHandleToggleColumnFilterProps = {
@ -49,8 +49,10 @@ export const useHandleToggleColumnFilter = ({
}; };
}, []); }, []);
const availableFilterDefinitions = useRecoilComponentValueV2( const availableFieldMetadataItemsForFilter = useRecoilValue(
availableFilterDefinitionsComponentState, availableFieldMetadataItemsForFilterFamilySelector({
objectMetadataItemId: objectMetadataItem.id,
}),
); );
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView(); const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
@ -80,10 +82,19 @@ export const useHandleToggleColumnFilter = ({
); );
if (!existingViewFilter) { if (!existingViewFilter) {
const filterDefinition = availableFilterDefinitions.find( const fieldMetadataItem = availableFieldMetadataItemsForFilter.find(
(fd) => fd.fieldMetadataId === fieldMetadataId, (fieldMetadataItemToFind) =>
fieldMetadataItemToFind.id === fieldMetadataId,
); );
if (!isDefined(fieldMetadataItem)) {
throw new Error('Field metadata item not found');
}
const filterDefinition = formatFieldMetadataItemAsFilterDefinition({
field: fieldMetadataItem,
});
if (!isDefined(filterDefinition)) { if (!isDefined(filterDefinition)) {
throw new Error('Filter definition not found'); throw new Error('Filter definition not found');
} }
@ -118,7 +129,7 @@ export const useHandleToggleColumnFilter = ({
upsertCombinedViewFilter, upsertCombinedViewFilter,
selectFilterDefinitionUsedInDropdown, selectFilterDefinitionUsedInDropdown,
currentViewWithCombinedFiltersAndSorts, currentViewWithCombinedFiltersAndSorts,
availableFilterDefinitions, availableFieldMetadataItemsForFilter,
upsertRecordFilter, upsertRecordFilter,
setFieldMetadataItemIdUsedInDropdown, setFieldMetadataItemIdUsedInDropdown,
], ],

View File

@ -5,7 +5,6 @@ import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObje
import { useSetRecordIndexEntityCount } from '@/object-record/record-index/hooks/useSetRecordIndexEntityCount'; import { useSetRecordIndexEntityCount } from '@/object-record/record-index/hooks/useSetRecordIndexEntityCount';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS } from '@/sign-in-background-mock/constants/SignInBackgroundMockColumnDefinitions'; import { SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS } from '@/sign-in-background-mock/constants/SignInBackgroundMockColumnDefinitions';
import { SIGN_IN_BACKGROUND_MOCK_FILTER_DEFINITIONS } from '@/sign-in-background-mock/constants/SignInBackgroundMockFilterDefinitions';
import { SIGN_IN_BACKGROUND_MOCK_SORT_DEFINITIONS } from '@/sign-in-background-mock/constants/SignInBackgroundMockSortDefinitions'; import { SIGN_IN_BACKGROUND_MOCK_SORT_DEFINITIONS } from '@/sign-in-background-mock/constants/SignInBackgroundMockSortDefinitions';
import { SIGN_IN_BACKGROUND_MOCK_VIEW_FIELDS } from '@/sign-in-background-mock/constants/SignInBackgroundMockViewFields'; import { SIGN_IN_BACKGROUND_MOCK_VIEW_FIELDS } from '@/sign-in-background-mock/constants/SignInBackgroundMockViewFields';
import { useInitViewBar } from '@/views/hooks/useInitViewBar'; import { useInitViewBar } from '@/views/hooks/useInitViewBar';
@ -37,7 +36,6 @@ export const SignInBackgroundMockContainerEffect = ({
const { const {
setAvailableSortDefinitions, setAvailableSortDefinitions,
setAvailableFilterDefinitions,
setAvailableFieldDefinitions, setAvailableFieldDefinitions,
setViewObjectMetadataId, setViewObjectMetadataId,
} = useInitViewBar(viewId); } = useInitViewBar(viewId);
@ -48,7 +46,6 @@ export const SignInBackgroundMockContainerEffect = ({
setViewObjectMetadataId?.(objectMetadataItem.id); setViewObjectMetadataId?.(objectMetadataItem.id);
setAvailableSortDefinitions?.(SIGN_IN_BACKGROUND_MOCK_SORT_DEFINITIONS); setAvailableSortDefinitions?.(SIGN_IN_BACKGROUND_MOCK_SORT_DEFINITIONS);
setAvailableFilterDefinitions?.(SIGN_IN_BACKGROUND_MOCK_FILTER_DEFINITIONS);
setAvailableFieldDefinitions?.(SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS); setAvailableFieldDefinitions?.(SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS);
setAvailableTableColumns(SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS); setAvailableTableColumns(SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS);
@ -62,7 +59,6 @@ export const SignInBackgroundMockContainerEffect = ({
}, [ }, [
setViewObjectMetadataId, setViewObjectMetadataId,
setAvailableSortDefinitions, setAvailableSortDefinitions,
setAvailableFilterDefinitions,
setAvailableFieldDefinitions, setAvailableFieldDefinitions,
objectMetadataItem, objectMetadataItem,
setAvailableTableColumns, setAvailableTableColumns,

View File

@ -1,23 +1,16 @@
import { useCallback, useEffect } from 'react'; import { useCallback } from 'react';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { EditableFilterChip } from '@/views/components/EditableFilterChip'; import { EditableFilterChip } from '@/views/components/EditableFilterChip';
import { ObjectFilterOperandSelectAndInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterOperandSelectAndInput'; import { ObjectFilterOperandSelectAndInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterOperandSelectAndInput';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter'; import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand'; import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { EditableFilterDropdownButtonEffect } from '@/views/components/EditableFilterDropdownButtonEffect';
import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters'; import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { isDefined } from 'twenty-shared';
type EditableFilterDropdownButtonProps = { type EditableFilterDropdownButtonProps = {
viewFilterDropdownId: string; viewFilterDropdownId: string;
@ -30,57 +23,10 @@ export const EditableFilterDropdownButton = ({
viewFilter, viewFilter,
hotkeyScope, hotkeyScope,
}: EditableFilterDropdownButtonProps) => { }: EditableFilterDropdownButtonProps) => {
const setFilterDefinitionUsedInDropdown = useSetRecoilComponentStateV2(
filterDefinitionUsedInDropdownComponentState,
viewFilterDropdownId,
);
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
fieldMetadataItemIdUsedInDropdownComponentState,
);
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
selectedOperandInDropdownComponentState,
viewFilterDropdownId,
);
const setSelectedFilter = useSetRecoilComponentStateV2(
selectedFilterComponentState,
viewFilterDropdownId,
);
// TODO: verify this instance id works
const availableFilterDefinitions = useRecoilComponentValueV2(
availableFilterDefinitionsComponentState,
viewFilterDropdownId,
);
const { closeDropdown } = useDropdown(viewFilterDropdownId); const { closeDropdown } = useDropdown(viewFilterDropdownId);
const { deleteCombinedViewFilter } = useDeleteCombinedViewFilters(); const { deleteCombinedViewFilter } = useDeleteCombinedViewFilters();
useEffect(() => {
const filterDefinition = availableFilterDefinitions.find(
(filterDefinition) =>
filterDefinition.fieldMetadataId === viewFilter.fieldMetadataId,
);
if (isDefined(filterDefinition)) {
setFilterDefinitionUsedInDropdown(filterDefinition);
setFieldMetadataItemIdUsedInDropdown(filterDefinition.fieldMetadataId);
setSelectedOperandInDropdown(viewFilter.operand);
setSelectedFilter(viewFilter);
}
}, [
availableFilterDefinitions,
setFilterDefinitionUsedInDropdown,
setFieldMetadataItemIdUsedInDropdown,
viewFilter,
setSelectedOperandInDropdown,
setSelectedFilter,
viewFilterDropdownId,
]);
const { removeRecordFilter } = useRemoveRecordFilter(); const { removeRecordFilter } = useRemoveRecordFilter();
const handleRemove = () => { const handleRemove = () => {
@ -108,20 +54,26 @@ export const EditableFilterDropdownButton = ({
}, [viewFilter, deleteCombinedViewFilter, removeRecordFilter]); }, [viewFilter, deleteCombinedViewFilter, removeRecordFilter]);
return ( return (
<Dropdown <>
dropdownId={viewFilterDropdownId} <EditableFilterDropdownButtonEffect
clickableComponent={ viewFilterDropdownId={viewFilterDropdownId}
<EditableFilterChip viewFilter={viewFilter} onRemove={handleRemove} /> viewFilter={viewFilter}
} />
dropdownComponents={ <Dropdown
<ObjectFilterOperandSelectAndInput dropdownId={viewFilterDropdownId}
filterDropdownId={viewFilterDropdownId} clickableComponent={
/> <EditableFilterChip viewFilter={viewFilter} onRemove={handleRemove} />
} }
dropdownHotkeyScope={hotkeyScope} dropdownComponents={
dropdownOffset={{ y: 8, x: 0 }} <ObjectFilterOperandSelectAndInput
dropdownPlacement="bottom-start" filterDropdownId={viewFilterDropdownId}
onClickOutside={handleDropdownClickOutside} />
/> }
dropdownHotkeyScope={hotkeyScope}
dropdownOffset={{ y: 8, x: 0 }}
dropdownPlacement="bottom-start"
onClickOutside={handleDropdownClickOutside}
/>
</>
); );
}; };

View File

@ -0,0 +1,74 @@
import { useEffect } from 'react';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { isDefined } from 'twenty-shared';
type EditableFilterDropdownButtonEffectProps = {
viewFilterDropdownId: string;
viewFilter: RecordFilter;
};
export const EditableFilterDropdownButtonEffect = ({
viewFilterDropdownId,
viewFilter,
}: EditableFilterDropdownButtonEffectProps) => {
const setFilterDefinitionUsedInDropdown = useSetRecoilComponentStateV2(
filterDefinitionUsedInDropdownComponentState,
viewFilterDropdownId,
);
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
fieldMetadataItemIdUsedInDropdownComponentState,
);
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
selectedOperandInDropdownComponentState,
viewFilterDropdownId,
);
const setSelectedFilter = useSetRecoilComponentStateV2(
selectedFilterComponentState,
viewFilterDropdownId,
);
const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems();
useEffect(() => {
const fieldMetadataItem = filterableFieldMetadataItems.find(
(fieldMetadataItem) =>
fieldMetadataItem.id === viewFilter.fieldMetadataId,
);
if (!isDefined(fieldMetadataItem)) {
return;
}
const filterDefinition = formatFieldMetadataItemAsFilterDefinition({
field: fieldMetadataItem,
});
if (isDefined(filterDefinition)) {
setFilterDefinitionUsedInDropdown(filterDefinition);
setFieldMetadataItemIdUsedInDropdown(filterDefinition.fieldMetadataId);
setSelectedOperandInDropdown(viewFilter.operand);
setSelectedFilter(viewFilter);
}
}, [
filterableFieldMetadataItems,
setFilterDefinitionUsedInDropdown,
setFieldMetadataItemIdUsedInDropdown,
viewFilter,
setSelectedOperandInDropdown,
setSelectedFilter,
viewFilterDropdownId,
]);
return null;
};

View File

@ -4,7 +4,6 @@ import { useContext, useEffect, useState } from 'react';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewEventContext } from '@/views/events/contexts/ViewEventContext'; import { ViewEventContext } from '@/views/events/contexts/ViewEventContext';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { isPersistingViewFieldsComponentState } from '@/views/states/isPersistingViewFieldsComponentState'; import { isPersistingViewFieldsComponentState } from '@/views/states/isPersistingViewFieldsComponentState';
import { View } from '@/views/types/View'; import { View } from '@/views/types/View';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
@ -23,11 +22,6 @@ export const ViewBarEffect = ({ viewBarId }: ViewBarEffectProps) => {
View | undefined View | undefined
>(undefined); >(undefined);
const availableFilterDefinitions = useRecoilComponentValueV2(
availableFilterDefinitionsComponentState,
viewBarId,
);
const isPersistingViewFields = useRecoilComponentValueV2( const isPersistingViewFields = useRecoilComponentValueV2(
isPersistingViewFieldsComponentState, isPersistingViewFieldsComponentState,
viewBarId, viewBarId,
@ -52,7 +46,6 @@ export const ViewBarEffect = ({ viewBarId }: ViewBarEffectProps) => {
} }
} }
}, [ }, [
availableFilterDefinitions,
currentViewSnapshot, currentViewSnapshot,
currentViewWithCombinedFiltersAndSorts, currentViewWithCombinedFiltersAndSorts,
isPersistingViewFields, isPersistingViewFields,

View File

@ -8,7 +8,6 @@ import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/ob
import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState'; import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState';
import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState'; import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema'; import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
import { simpleRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/simpleRelationFilterValueSchema'; import { simpleRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/simpleRelationFilterValueSchema';
import { isDefined } from 'twenty-shared'; import { isDefined } from 'twenty-shared';
@ -22,10 +21,6 @@ export const ViewBarFilterEffect = ({
}: ViewBarFilterEffectProps) => { }: ViewBarFilterEffectProps) => {
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView(); const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
const availableFilterDefinitions = useRecoilComponentValueV2(
availableFilterDefinitionsComponentState,
);
const filterDefinitionUsedInDropdown = useRecoilComponentValueV2( const filterDefinitionUsedInDropdown = useRecoilComponentValueV2(
filterDefinitionUsedInDropdownComponentState, filterDefinitionUsedInDropdownComponentState,
filterDropdownId, filterDropdownId,
@ -42,18 +37,6 @@ export const ViewBarFilterEffect = ({
filterDropdownId, filterDropdownId,
); );
// TODO: verify this instance id works
const setAvailableFilterDefinitions = useSetRecoilComponentStateV2(
availableFilterDefinitionsComponentState,
filterDropdownId,
);
useEffect(() => {
if (isDefined(availableFilterDefinitions)) {
setAvailableFilterDefinitions(availableFilterDefinitions);
}
}, [availableFilterDefinitions, setAvailableFilterDefinitions]);
useEffect(() => { useEffect(() => {
if (filterDefinitionUsedInDropdown?.type === 'RELATION') { if (filterDefinitionUsedInDropdown?.type === 'RELATION') {
const viewFilterUsedInDropdown = const viewFilterUsedInDropdown =

View File

@ -1,9 +1,10 @@
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState'; import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
import { View } from '@/views/types/View'; import { View } from '@/views/types/View';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters'; import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
@ -21,29 +22,31 @@ export const ViewBarRecordFilterEffect = () => {
currentRecordFiltersComponentState, currentRecordFiltersComponentState,
); );
const availableFilterDefinitions = useRecoilComponentValueV2( const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems();
availableFilterDefinitionsComponentState,
);
useEffect(() => { useEffect(() => {
if (isDataPrefetched) { if (isDataPrefetched) {
const currentView = views.find((view) => view.id === currentViewId); const currentView = views.find((view) => view.id === currentViewId);
const filterDefinitions = filterableFieldMetadataItems.map(
(fieldMetadataItem) =>
formatFieldMetadataItemAsFilterDefinition({
field: fieldMetadataItem,
}),
);
if (isDefined(currentView)) { if (isDefined(currentView)) {
setCurrentRecordFilters( setCurrentRecordFilters(
mapViewFiltersToFilters( mapViewFiltersToFilters(currentView.viewFilters, filterDefinitions),
currentView.viewFilters,
availableFilterDefinitions,
),
); );
} }
} }
}, [ }, [
isDataPrefetched, isDataPrefetched,
views, views,
availableFilterDefinitions,
currentViewId, currentViewId,
setCurrentRecordFilters, setCurrentRecordFilters,
filterableFieldMetadataItems,
]); ]);
return null; return null;

View File

@ -1,30 +1,42 @@
import { act, renderHook } from '@testing-library/react'; import { act, renderHook } from '@testing-library/react';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition'; import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState'; import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
import { ViewFilter } from '@/views/types/ViewFilter'; import { ViewFilter } from '@/views/types/ViewFilter';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { isDefined } from 'twenty-shared';
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { useApplyCurrentViewFiltersToCurrentRecordFilters } from '../useApplyCurrentViewFiltersToCurrentRecordFilters'; import { useApplyCurrentViewFiltersToCurrentRecordFilters } from '../useApplyCurrentViewFiltersToCurrentRecordFilters';
jest.mock('@/prefetch/hooks/usePrefetchedData'); jest.mock('@/prefetch/hooks/usePrefetchedData');
describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => { describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
const mockFilterDefinition: RecordFilterDefinition = { const mockObjectMetadataItem = generatedMockObjectMetadataItems.find(
fieldMetadataId: 'field-1', (item) => item.nameSingular === 'company',
label: 'Test Field', );
type: 'TEXT',
iconName: 'IconText', if (!isDefined(mockObjectMetadataItem)) {
}; throw new Error(
'Missing mock object metadata item with name singular "company"',
);
}
const mockFieldMetadataItem = mockObjectMetadataItem.fields[0];
const mockFilterDefinition: RecordFilterDefinition =
formatFieldMetadataItemAsFilterDefinition({
field: mockFieldMetadataItem,
});
const mockViewFilter: ViewFilter = { const mockViewFilter: ViewFilter = {
__typename: 'ViewFilter', __typename: 'ViewFilter',
id: 'filter-1', id: 'filter-1',
fieldMetadataId: 'field-1', fieldMetadataId: mockFieldMetadataItem.id,
operand: ViewFilterOperand.Contains, operand: ViewFilterOperand.Contains,
value: 'test', value: 'test',
displayValue: 'test', displayValue: 'test',
@ -36,17 +48,15 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
const mockView = { const mockView = {
id: 'view-1', id: 'view-1',
name: 'Test View', name: 'Test View',
objectMetadataId: 'object-1', objectMetadataId: mockObjectMetadataItem.id,
viewFilters: [mockViewFilter], viewFilters: [mockViewFilter],
}; };
beforeEach(() => { it('should apply filters from current view', () => {
(usePrefetchedData as jest.Mock).mockReturnValue({ (usePrefetchedData as jest.Mock).mockReturnValue({
records: [mockView], records: [mockView],
}); });
});
it('should apply filters from current view', () => {
const { result } = renderHook( const { result } = renderHook(
() => { () => {
const { applyCurrentViewFiltersToCurrentRecordFilters } = const { applyCurrentViewFiltersToCurrentRecordFilters } =
@ -70,12 +80,6 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
}), }),
mockView.id, mockView.id,
); );
snapshot.set(
availableFilterDefinitionsComponentState.atomFamily({
instanceId: 'instanceId',
}),
[mockFilterDefinition],
);
}, },
}), }),
}, },
@ -127,12 +131,6 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
}), }),
mockView.id, mockView.id,
); );
snapshot.set(
availableFilterDefinitionsComponentState.atomFamily({
instanceId: 'instanceId',
}),
[mockFilterDefinition],
);
}, },
}), }),
}, },
@ -178,12 +176,6 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
}), }),
mockView.id, mockView.id,
); );
snapshot.set(
availableFilterDefinitionsComponentState.atomFamily({
instanceId: 'instanceId',
}),
[mockFilterDefinition],
);
}, },
}), }),
}, },

View File

@ -1,26 +1,38 @@
import { act, renderHook } from '@testing-library/react'; import { act, renderHook } from '@testing-library/react';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition'; import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { ViewFilter } from '@/views/types/ViewFilter'; import { ViewFilter } from '@/views/types/ViewFilter';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { isDefined } from 'twenty-shared';
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { useApplyViewFiltersToCurrentRecordFilters } from '../useApplyViewFiltersToCurrentRecordFilters'; import { useApplyViewFiltersToCurrentRecordFilters } from '../useApplyViewFiltersToCurrentRecordFilters';
describe('useApplyViewFiltersToCurrentRecordFilters', () => { describe('useApplyViewFiltersToCurrentRecordFilters', () => {
const mockAvailableFilterDefinition: RecordFilterDefinition = { const mockObjectMetadataItem = generatedMockObjectMetadataItems.find(
fieldMetadataId: 'field-1', (item) => item.nameSingular === 'company',
label: 'Test Field', );
type: 'TEXT',
iconName: 'IconText', if (!isDefined(mockObjectMetadataItem)) {
}; throw new Error(
'Missing mock object metadata item with name singular "company"',
);
}
const mockFieldMetadataItem = mockObjectMetadataItem.fields[0];
const mockAvailableFilterDefinition: RecordFilterDefinition =
formatFieldMetadataItemAsFilterDefinition({
field: mockFieldMetadataItem,
});
const mockViewFilter: ViewFilter = { const mockViewFilter: ViewFilter = {
__typename: 'ViewFilter', __typename: 'ViewFilter',
id: 'filter-1', id: 'filter-1',
fieldMetadataId: 'field-1', fieldMetadataId: mockFieldMetadataItem.id,
operand: ViewFilterOperand.Contains, operand: ViewFilterOperand.Contains,
value: 'test', value: 'test',
displayValue: 'test', displayValue: 'test',
@ -42,16 +54,7 @@ describe('useApplyViewFiltersToCurrentRecordFilters', () => {
return { applyViewFiltersToCurrentRecordFilters, currentFilters }; return { applyViewFiltersToCurrentRecordFilters, currentFilters };
}, },
{ {
wrapper: getJestMetadataAndApolloMocksWrapper({ wrapper: getJestMetadataAndApolloMocksWrapper({}),
onInitializeRecoilSnapshot: (snapshot) => {
snapshot.set(
availableFilterDefinitionsComponentState.atomFamily({
instanceId: 'instanceId',
}),
[mockAvailableFilterDefinition],
);
},
}),
}, },
); );
@ -86,16 +89,7 @@ describe('useApplyViewFiltersToCurrentRecordFilters', () => {
return { applyViewFiltersToCurrentRecordFilters, currentFilters }; return { applyViewFiltersToCurrentRecordFilters, currentFilters };
}, },
{ {
wrapper: getJestMetadataAndApolloMocksWrapper({ wrapper: getJestMetadataAndApolloMocksWrapper({}),
onInitializeRecoilSnapshot: (snapshot) => {
snapshot.set(
availableFilterDefinitionsComponentState.atomFamily({
instanceId: 'instanceId',
}),
[mockAvailableFilterDefinition],
);
},
}),
}, },
); );

View File

@ -1,9 +1,9 @@
import { useFilterDefinitionsFromFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterDefinitionsFromFilterableFieldMetadataItems';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState'; import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
import { View } from '@/views/types/View'; import { View } from '@/views/types/View';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters'; import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
@ -19,19 +19,15 @@ export const useApplyCurrentViewFiltersToCurrentRecordFilters = () => {
currentRecordFiltersComponentState, currentRecordFiltersComponentState,
); );
const availableFilterDefinitions = useRecoilComponentValueV2( const { filterDefinitions } =
availableFilterDefinitionsComponentState, useFilterDefinitionsFromFilterableFieldMetadataItems();
);
const applyCurrentViewFiltersToCurrentRecordFilters = () => { const applyCurrentViewFiltersToCurrentRecordFilters = () => {
const currentView = views.find((view) => view.id === currentViewId); const currentView = views.find((view) => view.id === currentViewId);
if (isDefined(currentView)) { if (isDefined(currentView)) {
setCurrentRecordFilters( setCurrentRecordFilters(
mapViewFiltersToFilters( mapViewFiltersToFilters(currentView.viewFilters, filterDefinitions),
currentView.viewFilters,
availableFilterDefinitions,
),
); );
} }
}; };

View File

@ -1,7 +1,6 @@
import { useFilterDefinitionsFromFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterDefinitionsFromFilterableFieldMetadataItems';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { ViewFilter } from '@/views/types/ViewFilter'; import { ViewFilter } from '@/views/types/ViewFilter';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters'; import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
@ -10,16 +9,15 @@ export const useApplyViewFiltersToCurrentRecordFilters = () => {
currentRecordFiltersComponentState, currentRecordFiltersComponentState,
); );
const availableFilterDefinitions = useRecoilComponentValueV2( const { filterDefinitions } =
availableFilterDefinitionsComponentState, useFilterDefinitionsFromFilterableFieldMetadataItems();
);
const applyViewFiltersToCurrentRecordFilters = ( const applyViewFiltersToCurrentRecordFilters = (
viewFilters: ViewFilter[], viewFilters: ViewFilter[],
) => { ) => {
const recordFiltersToApply = mapViewFiltersToFilters( const recordFiltersToApply = mapViewFiltersToFilters(
viewFilters, viewFilters,
availableFilterDefinitions, filterDefinitions,
); );
setCurrentRecordFilters(recordFiltersToApply); setCurrentRecordFilters(recordFiltersToApply);

View File

@ -1,6 +1,5 @@
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { availableFieldDefinitionsComponentState } from '@/views/states/availableFieldDefinitionsComponentState'; import { availableFieldDefinitionsComponentState } from '@/views/states/availableFieldDefinitionsComponentState';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { availableSortDefinitionsComponentState } from '@/views/states/availableSortDefinitionsComponentState'; import { availableSortDefinitionsComponentState } from '@/views/states/availableSortDefinitionsComponentState';
import { viewObjectMetadataIdComponentState } from '@/views/states/viewObjectMetadataIdComponentState'; import { viewObjectMetadataIdComponentState } from '@/views/states/viewObjectMetadataIdComponentState';
@ -15,11 +14,6 @@ export const useInitViewBar = (viewBarInstanceId?: string) => {
viewBarInstanceId, viewBarInstanceId,
); );
const setAvailableFilterDefinitions = useSetRecoilComponentStateV2(
availableFilterDefinitionsComponentState,
viewBarInstanceId,
);
const setViewObjectMetadataId = useSetRecoilComponentStateV2( const setViewObjectMetadataId = useSetRecoilComponentStateV2(
viewObjectMetadataIdComponentState, viewObjectMetadataIdComponentState,
viewBarInstanceId, viewBarInstanceId,
@ -28,7 +22,6 @@ export const useInitViewBar = (viewBarInstanceId?: string) => {
return { return {
setAvailableFieldDefinitions, setAvailableFieldDefinitions,
setAvailableSortDefinitions, setAvailableSortDefinitions,
setAvailableFilterDefinitions,
setViewObjectMetadataId, setViewObjectMetadataId,
}; };
}; };

View File

@ -1,11 +0,0 @@
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const availableFilterDefinitionsComponentState = createComponentStateV2<
RecordFilterDefinition[]
>({
key: 'availableFilterDefinitionsComponentState',
defaultValue: [],
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -0,0 +1,22 @@
import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState';
import { isDefined } from 'twenty-shared';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
export const checkIfFeatureFlagIsEnabledOnWorkspace = (
featureKey: FeatureFlagKey | null | undefined,
workspace: CurrentWorkspace | null | undefined,
) => {
if (
!isDefined(featureKey) ||
!isDefined(workspace) ||
!isDefined(workspace.featureFlags)
) {
return false;
}
const featureFlag = workspace.featureFlags.find(
(flag) => flag.key === featureKey,
);
return featureFlag?.value === true;
};

View File

@ -3,9 +3,11 @@ import { ReactNode } from 'react';
import { MutableSnapshot, RecoilRoot } from 'recoil'; import { MutableSnapshot, RecoilRoot } from 'recoil';
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext'; import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope'; import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter'; import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
export const getJestMetadataAndApolloMocksWrapper = ({ export const getJestMetadataAndApolloMocksWrapper = ({
apolloMocks, apolloMocks,
@ -20,17 +22,31 @@ export const getJestMetadataAndApolloMocksWrapper = ({
<RecoilRoot initializeState={onInitializeRecoilSnapshot}> <RecoilRoot initializeState={onInitializeRecoilSnapshot}>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager"> <SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
<MockedProvider mocks={apolloMocks} addTypename={false}> <MockedProvider mocks={apolloMocks} addTypename={false}>
<RecordFiltersComponentInstanceContext.Provider <RecordIndexContextProvider
value={{ instanceId: 'instanceId' }} value={{
indexIdentifierUrl: () => 'indexIdentifierUrl',
onIndexRecordsLoaded: () => {},
objectNamePlural: 'objectNamePlural',
objectNameSingular: 'objectNameSingular',
objectMetadataItem:
generatedMockObjectMetadataItems.find(
(item) => item.nameSingular === 'company',
) ?? generatedMockObjectMetadataItems[0],
recordIndexId: 'recordIndexId',
}}
> >
<ViewComponentInstanceContext.Provider <RecordFiltersComponentInstanceContext.Provider
value={{ instanceId: 'instanceId' }} value={{ instanceId: 'instanceId' }}
> >
<JestObjectMetadataItemSetter> <ViewComponentInstanceContext.Provider
{children} value={{ instanceId: 'instanceId' }}
</JestObjectMetadataItemSetter> >
</ViewComponentInstanceContext.Provider> <JestObjectMetadataItemSetter>
</RecordFiltersComponentInstanceContext.Provider> {children}
</JestObjectMetadataItemSetter>
</ViewComponentInstanceContext.Provider>
</RecordFiltersComponentInstanceContext.Provider>
</RecordIndexContextProvider>
</MockedProvider> </MockedProvider>
</SnackBarProviderScope> </SnackBarProviderScope>
</RecoilRoot> </RecoilRoot>