Advanced filters bug bash (#11178)

This commit is contained in:
Lucas Bordeau
2025-03-26 11:06:53 +01:00
committed by GitHub
parent 7d6573e766
commit 8cc9f77417
15 changed files with 190 additions and 137 deletions

View File

@ -13,8 +13,8 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { MenuItem } from 'twenty-ui';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { MenuItem } from 'twenty-ui';
const StyledContainer = styled.div` const StyledContainer = styled.div`
flex: 1; flex: 1;
@ -80,6 +80,7 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
const operandsForFilterType = isDefined(filterType) const operandsForFilterType = isDefined(filterType)
? getRecordFilterOperands({ ? getRecordFilterOperands({
filterType, filterType,
subFieldName: filter?.subFieldName,
}) })
: []; : [];

View File

@ -12,6 +12,7 @@ import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-recor
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
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 { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel'; import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
import { getFilterableFieldTypeLabel } from '@/object-record/object-filter-dropdown/utils/getFilterableFieldTypeLabel'; import { getFilterableFieldTypeLabel } from '@/object-record/object-filter-dropdown/utils/getFilterableFieldTypeLabel';
import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs'; import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs';
@ -37,6 +38,10 @@ export const AdvancedFilterSubFieldSelectMenu = ({
objectFilterDropdownFilterIsSelectedComponentState, objectFilterDropdownFilterIsSelectedComponentState,
); );
const [, setSubFieldNameUsedInDropdown] = useRecoilComponentStateV2(
subFieldNameUsedInDropdownComponentState,
);
const [, setObjectFilterDropdownIsSelectingCompositeField] = const [, setObjectFilterDropdownIsSelectingCompositeField] =
useRecoilComponentStateV2( useRecoilComponentStateV2(
objectFilterDropdownIsSelectingCompositeFieldComponentState, objectFilterDropdownIsSelectingCompositeFieldComponentState,
@ -81,6 +86,7 @@ export const AdvancedFilterSubFieldSelectMenu = ({
setObjectFilterDropdownSubMenuFieldType(null); setObjectFilterDropdownSubMenuFieldType(null);
setObjectFilterDropdownIsSelectingCompositeField(false); setObjectFilterDropdownIsSelectingCompositeField(false);
setObjectFilterDropdownFilterIsSelected(false); setObjectFilterDropdownFilterIsSelected(false);
setSubFieldNameUsedInDropdown(null);
}; };
if (!isDefined(objectFilterDropdownSubMenuFieldType)) { if (!isDefined(objectFilterDropdownSubMenuFieldType)) {

View File

@ -6,7 +6,6 @@ import { configurableViewFilterOperands } from '@/object-record/object-filter-dr
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { SelectControl } from '@/ui/input/components/SelectControl'; import { SelectControl } from '@/ui/input/components/SelectControl';
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 { 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';
@ -79,14 +78,12 @@ export const AdvancedFilterValueInputDropdownButton = ({
setSelectedFilter(filter); setSelectedFilter(filter);
}} }}
dropdownComponents={ dropdownComponents={
<DropdownMenuItemsContainer> <ObjectFilterDropdownFilterInput recordFilterId={filter.id} />
<ObjectFilterDropdownFilterInput />
</DropdownMenuItemsContainer>
} }
dropdownHotkeyScope={{ scope: dropdownId }} dropdownHotkeyScope={{ scope: dropdownId }}
dropdownOffset={{ y: 8, x: 0 }} dropdownOffset={{ y: 8, x: 0 }}
dropdownPlacement="bottom-start" dropdownPlacement="bottom-start"
dropdownMenuWidth={280} dropdownMenuWidth={200}
/> />
)} )}
</StyledValueDropdownContainer> </StyledValueDropdownContainer>

View File

@ -72,6 +72,7 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
const firstOperand = getRecordFilterOperands({ const firstOperand = getRecordFilterOperands({
filterType, filterType,
subFieldName,
})[0]; })[0];
setSelectedOperandInDropdown(firstOperand); setSelectedOperandInDropdown(firstOperand);
@ -97,9 +98,7 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
subFieldName, subFieldName,
}); });
if (isDefined(subFieldName)) { setSubFieldNameUsedInDropdown(subFieldName);
setSubFieldNameUsedInDropdown(subFieldName);
}
setObjectFilterDropdownSearchInput(''); setObjectFilterDropdownSearchInput('');
}; };

View File

@ -16,8 +16,8 @@ import {
VariableDateViewFilterValueUnit, VariableDateViewFilterValueUnit,
} from '@/views/view-filter-value/utils/resolveDateViewFilterValue'; } from '@/views/view-filter-value/utils/resolveDateViewFilterValue';
import { useState } from 'react'; import { useState } from 'react';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { FieldMetadataType } from '~/generated-metadata/graphql';
export const ObjectFilterDropdownDateInput = () => { export const ObjectFilterDropdownDateInput = () => {
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
@ -114,7 +114,6 @@ export const ObjectFilterDropdownDateInput = () => {
date={internalDate} date={internalDate}
onChange={handleAbsoluteDateChange} onChange={handleAbsoluteDateChange}
onRelativeDateChange={handleRelativeDateChange} onRelativeDateChange={handleRelativeDateChange}
onClose={handleAbsoluteDateChange}
isDateTimeInput={isDateTimeInput} isDateTimeInput={isDateTimeInput}
/> />
); );

View File

@ -23,10 +23,12 @@ import { isDefined } from 'twenty-shared/utils';
type ObjectFilterDropdownFilterInputProps = { type ObjectFilterDropdownFilterInputProps = {
filterDropdownId?: string; filterDropdownId?: string;
recordFilterId?: string;
}; };
export const ObjectFilterDropdownFilterInput = ({ export const ObjectFilterDropdownFilterInput = ({
filterDropdownId, filterDropdownId,
recordFilterId,
}: ObjectFilterDropdownFilterInputProps) => { }: ObjectFilterDropdownFilterInputProps) => {
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector, fieldMetadataItemUsedInDropdownComponentSelector,
@ -74,8 +76,9 @@ export const ObjectFilterDropdownFilterInput = ({
<> <>
{isConfigurable && selectedOperandInDropdown && ( {isConfigurable && selectedOperandInDropdown && (
<> <>
{TEXT_FILTER_TYPES.includes(filterType) && {TEXT_FILTER_TYPES.includes(filterType) && (
!isActorSourceCompositeFilter && <ObjectFilterDropdownTextInput />} <ObjectFilterDropdownTextInput />
)}
{NUMBER_FILTER_TYPES.includes(filterType) && ( {NUMBER_FILTER_TYPES.includes(filterType) && (
<ObjectFilterDropdownNumberInput /> <ObjectFilterDropdownNumberInput />
)} )}
@ -87,15 +90,21 @@ export const ObjectFilterDropdownFilterInput = ({
<> <>
<ObjectFilterDropdownSearchInput /> <ObjectFilterDropdownSearchInput />
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<ObjectFilterDropdownRecordSelect /> <ObjectFilterDropdownRecordSelect
</> recordFilterId={recordFilterId}
)} />
{filterType === 'ACTOR' && isActorSourceCompositeFilter && (
<>
<DropdownMenuSeparator />
<ObjectFilterDropdownSourceSelect />
</> </>
)} )}
{filterType === 'ACTOR' &&
(isActorSourceCompositeFilter ? (
<>
<ObjectFilterDropdownSourceSelect />
</>
) : (
<>
<ObjectFilterDropdownTextInput />
</>
))}
{['SELECT', 'MULTI_SELECT'].includes(filterType) && ( {['SELECT', 'MULTI_SELECT'].includes(filterType) && (
<> <>
<ObjectFilterDropdownSearchInput /> <ObjectFilterDropdownSearchInput />

View File

@ -22,8 +22,8 @@ import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/
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 { useState } from 'react'; import { useState } from 'react';
import { IconApps, IconChevronLeft, MenuItem, useIcons } from 'twenty-ui';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { IconApps, IconChevronLeft, MenuItem, useIcons } from 'twenty-ui';
export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
const [searchText] = useState(''); const [searchText] = useState('');
@ -126,6 +126,7 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
setObjectFilterDropdownSubMenuFieldType(null); setObjectFilterDropdownSubMenuFieldType(null);
setObjectFilterDropdownIsSelectingCompositeField(false); setObjectFilterDropdownIsSelectingCompositeField(false);
setObjectFilterDropdownFilterIsSelected(false); setObjectFilterDropdownFilterIsSelected(false);
setSubFieldNameUsedInDropdown(null);
}; };
if (!isDefined(objectFilterDropdownSubMenuFieldType)) { if (!isDefined(objectFilterDropdownSubMenuFieldType)) {

View File

@ -7,6 +7,7 @@ import { selectedFilterComponentState } from '@/object-record/object-filter-drop
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput'; import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const ObjectFilterDropdownNumberInput = () => { export const ObjectFilterDropdownNumberInput = () => {
@ -44,31 +45,33 @@ export const ObjectFilterDropdownNumberInput = () => {
return ( return (
fieldMetadataItemUsedInDropdown && fieldMetadataItemUsedInDropdown &&
selectedOperandInDropdown && ( selectedOperandInDropdown && (
<DropdownMenuInput <DropdownMenuItemsContainer>
ref={handleInputRef} <DropdownMenuInput
value={inputValue} ref={handleInputRef}
autoFocus value={inputValue}
type="number" autoFocus
placeholder={fieldMetadataItemUsedInDropdown.label} type="number"
onChange={(event: ChangeEvent<HTMLInputElement>) => { placeholder={fieldMetadataItemUsedInDropdown.label}
const newValue = event.target.value; onChange={(event: ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
setInputValue(newValue); setInputValue(newValue);
applyRecordFilter({ applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(), id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown?.id ?? '', fieldMetadataId: fieldMetadataItemUsedInDropdown?.id ?? '',
value: newValue, value: newValue,
operand: selectedOperandInDropdown, operand: selectedOperandInDropdown,
displayValue: newValue, displayValue: newValue,
type: getFilterTypeFromFieldType( type: getFilterTypeFromFieldType(
fieldMetadataItemUsedInDropdown.type, fieldMetadataItemUsedInDropdown.type,
), ),
label: fieldMetadataItemUsedInDropdown.label, label: fieldMetadataItemUsedInDropdown.label,
recordFilterGroupId: selectedFilter?.recordFilterGroupId, recordFilterGroupId: selectedFilter?.recordFilterGroupId,
}); });
}} }}
/> />
</DropdownMenuItemsContainer>
) )
); );
}; };

View File

@ -14,9 +14,9 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { isDefined } from 'twenty-shared/utils';
import { MenuItem } from 'twenty-ui'; import { MenuItem } from 'twenty-ui';
import { getOperandLabel } from '../utils/getOperandLabel'; import { getOperandLabel } from '../utils/getOperandLabel';
import { isDefined } from 'twenty-shared/utils';
const StyledDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)` const StyledDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
background-color: ${({ theme }) => theme.background.primary}; background-color: ${({ theme }) => theme.background.primary};
@ -66,7 +66,7 @@ export const ObjectFilterDropdownOperandSelect = () => {
if (isValuelessOperand && isDefined(fieldMetadataItemUsedInDropdown)) { if (isValuelessOperand && isDefined(fieldMetadataItemUsedInDropdown)) {
applyRecordFilter({ applyRecordFilter({
id: v4(), id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown.id, fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
displayValue: '', displayValue: '',
operand: newOperand, operand: newOperand,

View File

@ -5,12 +5,17 @@ import { RATING_VALUES } from '@/object-record/record-field/meta-types/constants
import { FieldRatingValue } from '@/object-record/record-field/types/FieldMetadata'; import { FieldRatingValue } from '@/object-record/record-field/types/FieldMetadata';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { RatingInput } from '@/ui/field/input/components/RatingInput'; import { RatingInput } from '@/ui/field/input/components/RatingInput';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import styled from '@emotion/styled';
const StyledRatingInputContainer = styled.div`
padding: ${({ theme }) => theme.spacing(2)};
`;
const convertFieldRatingValueToNumber = ( const convertFieldRatingValueToNumber = (
rating: Exclude<FieldRatingValue, null>, rating: Exclude<FieldRatingValue, null>,
): string => { ): string => {
@ -51,7 +56,7 @@ export const ObjectFilterDropdownRatingInput = () => {
return ( return (
fieldMetadataItemUsedInDropdown && fieldMetadataItemUsedInDropdown &&
selectedOperandInDropdown && ( selectedOperandInDropdown && (
<DropdownMenuItemsContainer> <StyledRatingInputContainer>
<RatingInput <RatingInput
value={selectedFilter?.value as FieldRatingValue} value={selectedFilter?.value as FieldRatingValue}
onChange={(newValue: FieldRatingValue) => { onChange={(newValue: FieldRatingValue) => {
@ -73,7 +78,7 @@ export const ObjectFilterDropdownRatingInput = () => {
}); });
}} }}
/> />
</DropdownMenuItemsContainer> </StyledRatingInputContainer>
) )
); );
}; };

View File

@ -7,24 +7,23 @@ import { ObjectFilterDropdownRecordPinnedItems } from '@/object-record/object-fi
import { CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID } from '@/object-record/object-filter-dropdown/constants/CurrentWorkspaceMemberSelectableItemId'; import { CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID } from '@/object-record/object-filter-dropdown/constants/CurrentWorkspaceMemberSelectableItemId';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState';
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 { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters';
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown'; import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect'; import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
import { SelectableItem } from '@/object-record/select/types/SelectableItem'; import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
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 { RelationFilterValue } from '@/views/view-filter-value/types/RelationFilterValue'; import { RelationFilterValue } from '@/views/view-filter-value/types/RelationFilterValue';
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/utils';
import { IconUserCircle } from 'twenty-ui'; import { IconUserCircle } from 'twenty-ui';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { isDefined } from 'twenty-shared/utils';
export const EMPTY_FILTER_VALUE: string = JSON.stringify({ export const EMPTY_FILTER_VALUE: string = JSON.stringify({
isCurrentWorkspaceMemberSelected: false, isCurrentWorkspaceMemberSelected: false,
@ -35,10 +34,12 @@ export const MAX_RECORDS_TO_DISPLAY = 3;
type ObjectFilterDropdownRecordSelectProps = { type ObjectFilterDropdownRecordSelectProps = {
viewComponentId?: string; viewComponentId?: string;
recordFilterId?: string;
}; };
export const ObjectFilterDropdownRecordSelect = ({ export const ObjectFilterDropdownRecordSelect = ({
viewComponentId, viewComponentId,
recordFilterId,
}: ObjectFilterDropdownRecordSelectProps) => { }: ObjectFilterDropdownRecordSelectProps) => {
const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2( const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector, fieldMetadataItemUsedInDropdownComponentSelector,
@ -56,12 +57,8 @@ export const ObjectFilterDropdownRecordSelect = ({
objectFilterDropdownSearchInputComponentState, objectFilterDropdownSearchInputComponentState,
); );
const objectFilterDropdownSelectedRecordIds = useRecoilComponentValueV2( const currentRecordFilters = useRecoilComponentValueV2(
objectFilterDropdownSelectedRecordIdsComponentState, currentRecordFiltersComponentState,
);
const setObjectFilterDropdownSelectedRecordIds = useSetRecoilComponentStateV2(
objectFilterDropdownSelectedRecordIdsComponentState,
); );
const { applyRecordFilter } = useApplyRecordFilter(viewComponentId); const { applyRecordFilter } = useApplyRecordFilter(viewComponentId);
@ -97,10 +94,34 @@ export const ObjectFilterDropdownRecordSelect = ({
throw new Error('objectNameSingular is not defined'); throw new Error('objectNameSingular is not defined');
} }
const firstSimpleRecordFilterForFieldMetadataItemUsedInDropdown =
currentRecordFilters.find(
(filter) =>
filter.fieldMetadataId === fieldMetadataItemUsedInFilterDropdown?.id &&
!isDefined(filter.recordFilterGroupId),
);
const recordFilterPassedInProps = currentRecordFilters.find(
(filter) => filter.id === recordFilterId,
);
const recordFilterUsedInDropdown = isDefined(recordFilterId)
? recordFilterPassedInProps
: firstSimpleRecordFilterForFieldMetadataItemUsedInDropdown;
const { selectedRecordIds } = jsonRelationFilterValueSchema
.catch({
isCurrentWorkspaceMemberSelected: false,
selectedRecordIds: simpleRelationFilterValueSchema.parse(
recordFilterUsedInDropdown?.value,
),
})
.parse(recordFilterUsedInDropdown?.value);
const { loading, filteredSelectedRecords, recordsToSelect, selectedRecords } = const { loading, filteredSelectedRecords, recordsToSelect, selectedRecords } =
useRecordsForSelect({ useRecordsForSelect({
searchFilterText: objectFilterDropdownSearchInput, searchFilterText: objectFilterDropdownSearchInput,
selectedIds: objectFilterDropdownSelectedRecordIds, selectedIds: selectedRecordIds,
objectNameSingular, objectNameSingular,
limit: 10, limit: 10,
}); });
@ -123,10 +144,6 @@ export const ObjectFilterDropdownRecordSelect = ({
.includes(objectFilterDropdownSearchInput.toLowerCase()), .includes(objectFilterDropdownSearchInput.toLowerCase()),
); );
const currentRecordFilters = useRecoilComponentValueV2(
currentRecordFiltersComponentState,
);
const handleMultipleRecordSelectChange = ( const handleMultipleRecordSelectChange = (
itemToSelect: SelectableItem, itemToSelect: SelectableItem,
isNewSelectedValue: boolean, isNewSelectedValue: boolean,
@ -139,17 +156,16 @@ export const ObjectFilterDropdownRecordSelect = ({
itemToSelect.id === CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID; itemToSelect.id === CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID;
const selectedRecordIdsWithAddedRecord = [ const selectedRecordIdsWithAddedRecord = [
...objectFilterDropdownSelectedRecordIds, ...selectedRecordIds,
itemToSelect.id, itemToSelect.id,
]; ];
const selectedRecordIdsWithRemovedRecord = const selectedRecordIdsWithRemovedRecord = selectedRecordIds.filter(
objectFilterDropdownSelectedRecordIds.filter( (id) => id !== itemToSelect.id,
(id) => id !== itemToSelect.id, );
);
const newSelectedRecordIds = isItemCurrentWorkspaceMember const newSelectedRecordIds = isItemCurrentWorkspaceMember
? objectFilterDropdownSelectedRecordIds ? selectedRecordIds
: isNewSelectedValue : isNewSelectedValue
? selectedRecordIdsWithAddedRecord ? selectedRecordIdsWithAddedRecord
: selectedRecordIdsWithRemovedRecord; : selectedRecordIdsWithRemovedRecord;
@ -194,28 +210,44 @@ export const ObjectFilterDropdownRecordSelect = ({
} satisfies RelationFilterValue) } satisfies RelationFilterValue)
: ''; : '';
const recordFilterInDropdown = currentRecordFilters.find( const duplicateFilterInCurrentRecordFilters =
(recordFilter) => findDuplicateRecordFilterInNonAdvancedRecordFilters({
recordFilter.fieldMetadataId === recordFilters: currentRecordFilters,
fieldMetadataItemUsedInFilterDropdown.id, fieldMetadataItemId: fieldMetadataItemUsedInFilterDropdown.id,
});
const filterIsAlreadyInCurrentRecordFilters = isDefined(
duplicateFilterInCurrentRecordFilters,
); );
setObjectFilterDropdownSelectedRecordIds(newSelectedRecordIds); if (filterIsAlreadyInCurrentRecordFilters && !isDefined(recordFilterId)) {
applyRecordFilter({
const filterId = recordFilterInDropdown?.id ?? v4(); id: duplicateFilterInCurrentRecordFilters.id,
type: getFilterTypeFromFieldType(
applyRecordFilter({ fieldMetadataItemUsedInFilterDropdown.type,
id: selectedFilter?.id ? selectedFilter.id : filterId, ),
type: getFilterTypeFromFieldType( label: fieldMetadataItemUsedInFilterDropdown.label,
fieldMetadataItemUsedInFilterDropdown.type, operand: selectedOperandInDropdown,
), displayValue: filterDisplayValue,
label: fieldMetadataItemUsedInFilterDropdown.label, fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id,
operand: selectedOperandInDropdown, value: newFilterValue,
displayValue: filterDisplayValue, recordFilterGroupId:
fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id, duplicateFilterInCurrentRecordFilters.recordFilterGroupId,
value: newFilterValue, });
recordFilterGroupId: selectedFilter?.recordFilterGroupId, } else {
}); applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(),
type: getFilterTypeFromFieldType(
fieldMetadataItemUsedInFilterDropdown.type,
),
label: fieldMetadataItemUsedInFilterDropdown.label,
operand: selectedOperandInDropdown,
displayValue: filterDisplayValue,
fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id,
value: newFilterValue,
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
});
}
} }
}; };

View File

@ -1,8 +1,4 @@
import { useState } from 'react';
import { v4 } from 'uuid';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useEmptyRecordFilter } from '@/object-record/object-filter-dropdown/hooks/useEmptyRecordFilter';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState'; import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState';
@ -10,8 +6,8 @@ import { selectedFilterComponentState } from '@/object-record/object-filter-drop
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { getActorSourceMultiSelectOptions } from '@/object-record/object-filter-dropdown/utils/getActorSourceMultiSelectOptions'; import { getActorSourceMultiSelectOptions } from '@/object-record/object-filter-dropdown/utils/getActorSourceMultiSelectOptions';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters';
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown'; import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
import { SelectableItem } from '@/object-record/select/types/SelectableItem'; import { SelectableItem } from '@/object-record/select/types/SelectableItem';
@ -19,6 +15,7 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
export const EMPTY_FILTER_VALUE = '[]'; export const EMPTY_FILTER_VALUE = '[]';
export const MAX_ITEMS_TO_DISPLAY = 3; export const MAX_ITEMS_TO_DISPLAY = 3;
@ -56,9 +53,6 @@ export const ObjectFilterDropdownSourceSelect = ({
const { applyRecordFilter } = useApplyRecordFilter(viewComponentId); const { applyRecordFilter } = useApplyRecordFilter(viewComponentId);
// TODO: this should be removed as it is not consistent across re-renders
const [fieldId] = useState(v4());
const sourceTypes = getActorSourceMultiSelectOptions( const sourceTypes = getActorSourceMultiSelectOptions(
objectFilterDropdownSelectedRecordIds, objectFilterDropdownSelectedRecordIds,
); );
@ -67,10 +61,6 @@ export const ObjectFilterDropdownSourceSelect = ({
objectFilterDropdownSelectedRecordIds.includes(option.id), objectFilterDropdownSelectedRecordIds.includes(option.id),
); );
const { emptyRecordFilter } = useEmptyRecordFilter();
const { removeRecordFilter } = useRemoveRecordFilter();
const currentRecordFilters = useRecoilComponentValueV2( const currentRecordFilters = useRecoilComponentValueV2(
currentRecordFiltersComponentState, currentRecordFiltersComponentState,
); );
@ -91,13 +81,6 @@ export const ObjectFilterDropdownSourceSelect = ({
); );
} }
if (newSelectedItemIds.length === 0) {
emptyRecordFilter();
removeRecordFilter(fieldMetadataItemUsedInFilterDropdown.id);
return;
}
setObjectFilterDropdownSelectedRecordIds(newSelectedItemIds); setObjectFilterDropdownSelectedRecordIds(newSelectedItemIds);
const selectedItemNames = sourceTypes const selectedItemNames = sourceTypes
@ -118,13 +101,20 @@ export const ObjectFilterDropdownSourceSelect = ({
? JSON.stringify(newSelectedItemIds) ? JSON.stringify(newSelectedItemIds)
: EMPTY_FILTER_VALUE; : EMPTY_FILTER_VALUE;
const recordFilter = currentRecordFilters.find( const duplicateFilterInCurrentRecordFilters =
(recordFilter) => findDuplicateRecordFilterInNonAdvancedRecordFilters({
recordFilter.fieldMetadataId === recordFilters: currentRecordFilters,
fieldMetadataItemUsedInFilterDropdown.id, fieldMetadataItemId: fieldMetadataItemUsedInFilterDropdown.id,
subFieldName: 'source',
});
const filterIsAlreadyInCurrentRecordFilters = isDefined(
duplicateFilterInCurrentRecordFilters,
); );
const filterId = recordFilter?.id ?? fieldId; const filterId = filterIsAlreadyInCurrentRecordFilters
? duplicateFilterInCurrentRecordFilters?.id
: v4();
applyRecordFilter({ applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : filterId, id: selectedFilter?.id ? selectedFilter.id : filterId,
@ -137,6 +127,7 @@ export const ObjectFilterDropdownSourceSelect = ({
fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id, fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id,
value: newFilterValue, value: newFilterValue,
recordFilterGroupId: selectedFilter?.recordFilterGroupId, recordFilterGroupId: selectedFilter?.recordFilterGroupId,
subFieldName: 'source',
}); });
} }
}; };

View File

@ -7,6 +7,7 @@ import { selectedFilterComponentState } from '@/object-record/object-filter-drop
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput'; import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const ObjectFilterDropdownTextInput = () => { export const ObjectFilterDropdownTextInput = () => {
@ -44,31 +45,33 @@ export const ObjectFilterDropdownTextInput = () => {
return ( return (
fieldMetadataItemUsedInDropdown && fieldMetadataItemUsedInDropdown &&
selectedOperandInDropdown && ( selectedOperandInDropdown && (
<DropdownMenuInput <DropdownMenuItemsContainer>
ref={handleInputRef} <DropdownMenuInput
value={inputValue} ref={handleInputRef}
autoFocus value={inputValue}
type="text" autoFocus
placeholder={fieldMetadataItemUsedInDropdown.label} type="text"
onChange={(event: ChangeEvent<HTMLInputElement>) => { placeholder={fieldMetadataItemUsedInDropdown.label}
const newValue = event.target.value; onChange={(event: ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
setInputValue(newValue); setInputValue(newValue);
applyRecordFilter({ applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(), id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown?.id ?? '', fieldMetadataId: fieldMetadataItemUsedInDropdown?.id ?? '',
value: newValue, value: newValue,
operand: selectedOperandInDropdown, operand: selectedOperandInDropdown,
displayValue: newValue, displayValue: newValue,
type: getFilterTypeFromFieldType( type: getFilterTypeFromFieldType(
fieldMetadataItemUsedInDropdown.type, fieldMetadataItemUsedInDropdown.type,
), ),
label: fieldMetadataItemUsedInDropdown.label, label: fieldMetadataItemUsedInDropdown.label,
recordFilterGroupId: selectedFilter?.recordFilterGroupId, recordFilterGroupId: selectedFilter?.recordFilterGroupId,
}); });
}} }}
/> />
</DropdownMenuItemsContainer>
) )
); );
}; };

View File

@ -6,7 +6,6 @@ export const TEXT_FILTER_TYPES = [
'LINK', 'LINK',
'LINKS', 'LINKS',
'ADDRESS', 'ADDRESS',
'ACTOR',
'ARRAY', 'ARRAY',
'RAW_JSON', 'RAW_JSON',
]; ];

View File

@ -671,6 +671,10 @@ export const computeFilterRecordGqlOperationFilter = ({
case 'ACTOR': { case 'ACTOR': {
switch (filter.operand) { switch (filter.operand) {
case RecordFilterOperand.Is: { case RecordFilterOperand.Is: {
if (filter.value === '[]') {
return;
}
const parsedRecordIds = JSON.parse(filter.value) as string[]; const parsedRecordIds = JSON.parse(filter.value) as string[];
return { return {
@ -682,6 +686,10 @@ export const computeFilterRecordGqlOperationFilter = ({
}; };
} }
case RecordFilterOperand.IsNot: { case RecordFilterOperand.IsNot: {
if (filter.value === '[]') {
return;
}
const parsedRecordIds = JSON.parse(filter.value) as string[]; const parsedRecordIds = JSON.parse(filter.value) as string[];
if (parsedRecordIds.length === 0) return; if (parsedRecordIds.length === 0) return;