Refactored object filter dropdown input state management (#11838)

This PR refactors the generic module object dropdown filter input.

We have multiple components for each filter type and each one was using
selectedFilterState and the applyRecordFilter hook to read and set its
value.

The main issue was that each component was forced to pass every property
of a RecordFilter to applyRecordFilter to only modify the value
property, thus creating a lot of unnecessary dependencies and tight
coupling between every component and hook that used the record filters.

Now we have each component only reading from a new
objectFilterDropdownCurrentRecordFilterComponentState, scoped to a
ObjectFilterDropdownComponentInstanceContext, thus whether we're in a
view bar dropdown, an editable filter chip dropdown or an advanced
filter dropdown, we know where to read the filter value from.

This component state might even be simplified by only storing the record
filter id, thus avoiding having to synchronize this state with its
counterpart in currentRecordFilterComponentState, but we should try
after the main refactor effort, as those two states aren't in the same
instance context.

We implement a new applyObjectFilterDropdownFilterValue hook to handle
the value setting from an object filter dropdown input component.
There's also a new useApplyObjectFilterDropdownOperand for doing the
same but for operand.

Another important thing that had to be done to keep a synchronous code
path was to set the states of each advanced filter row at the advanced
filter dropdown onOpen event, using useSetAdvancedFilterDropdownStates.

Finally we remove : useApplyRecordFilter, useSelectFilterUsedInDropdown
and selectedFilterComponentState which were making all of this zone
difficult to work with.

Closes https://github.com/twentyhq/core-team-issues/issues/718
Closes https://github.com/twentyhq/core-team-issues/issues/720
This commit is contained in:
Lucas Bordeau
2025-05-02 17:28:45 +02:00
committed by GitHub
parent aa2b27dcbe
commit 5946b37712
35 changed files with 444 additions and 1015 deletions

View File

@ -1,122 +0,0 @@
import { v4 } from 'uuid';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { getRelativeDateDisplayValue } from '@/object-record/object-filter-dropdown/utils/getRelativeDateDisplayValue';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { DateTimePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { computeVariableDateViewFilterValue } from '@/views/view-filter-value/utils/computeVariableDateViewFilterValue';
import {
resolveDateViewFilterValue,
VariableDateViewFilterValueDirection,
VariableDateViewFilterValueUnit,
} from '@/views/view-filter-value/utils/resolveDateViewFilterValue';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { FieldMetadataType } from '~/generated-metadata/graphql';
export const AdvancedFilterDropdownDateInput = () => {
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
);
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
);
const { applyRecordFilter } = useApplyRecordFilter();
const initialFilterValue = selectedFilter
? resolveDateViewFilterValue(selectedFilter)
: null;
const [internalDate, setInternalDate] = useState<Date | null>(
initialFilterValue instanceof Date ? initialFilterValue : null,
);
const isDateTimeInput =
fieldMetadataItemUsedInDropdown?.type === FieldMetadataType.DATE_TIME;
const handleAbsoluteDateChange = (newDate: Date | null) => {
setInternalDate(newDate);
if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) return;
const newDisplayValue = isDefined(newDate)
? newDate.toLocaleDateString()
: '';
applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
value: newDate?.toISOString() ?? '',
operand: selectedOperandInDropdown,
displayValue: newDisplayValue,
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup,
type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type),
label: fieldMetadataItemUsedInDropdown.label,
});
};
const handleRelativeDateChange = (
relativeDate: {
direction: VariableDateViewFilterValueDirection;
amount?: number;
unit: VariableDateViewFilterValueUnit;
} | null,
) => {
if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) return;
const value = relativeDate
? computeVariableDateViewFilterValue(
relativeDate.direction,
relativeDate.amount,
relativeDate.unit,
)
: '';
applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
value,
operand: selectedOperandInDropdown,
displayValue: getRelativeDateDisplayValue(relativeDate),
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup,
type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type),
label: fieldMetadataItemUsedInDropdown.label,
});
};
const isRelativeOperand =
selectedOperandInDropdown === ViewFilterOperand.IsRelative;
const resolvedValue = selectedFilter
? resolveDateViewFilterValue(selectedFilter)
: null;
const relativeDate =
resolvedValue && !(resolvedValue instanceof Date)
? resolvedValue
: undefined;
return (
<DateTimePicker
relativeDate={relativeDate}
highlightedDateRange={relativeDate}
isRelative={isRelativeOperand}
date={internalDate}
onChange={handleAbsoluteDateChange}
onRelativeDateChange={handleRelativeDateChange}
isDateTimeInput={isDateTimeInput}
/>
);
};

View File

@ -5,9 +5,9 @@ import { ObjectFilterDropdownSearchInput } from '@/object-record/object-filter-d
import { ObjectFilterDropdownSourceSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSourceSelect';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { AdvancedFilterDropdownDateInput } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownDateInput';
import { ObjectFilterDropdownBooleanSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect';
import { ObjectFilterDropdownCurrencySelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownCurrencySelect';
import { ObjectFilterDropdownDateInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput';
import { ObjectFilterDropdownTextInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput';
import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes';
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
@ -41,7 +41,7 @@ export const AdvancedFilterDropdownFilterInput = ({
<>
{filterType === 'RATING' && <ObjectFilterDropdownRatingInput />}
{DATE_FILTER_TYPES.includes(filterType) && (
<AdvancedFilterDropdownDateInput />
<ObjectFilterDropdownDateInput />
)}
{filterType === 'RELATION' && (
<>

View File

@ -1,13 +1,9 @@
import { useState } from 'react';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { v4 } from 'uuid';
export const AdvancedFilterDropdownNumberInput = () => {
const selectedOperandInDropdown = useRecoilComponentValueV2(
@ -18,34 +14,14 @@ export const AdvancedFilterDropdownNumberInput = () => {
fieldMetadataItemUsedInDropdownComponentSelector,
);
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
);
const { objectFilterDropdownFilterValue } =
useObjectFilterDropdownFilterValue();
const { applyRecordFilter } = useApplyRecordFilter();
const [inputValue, setInputValue] = useState(
() => selectedFilter?.value || '',
);
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const handleChange = (newValue: string) => {
if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) {
return;
}
setInputValue(newValue);
applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown?.id ?? '',
value: newValue,
operand: selectedOperandInDropdown,
displayValue: newValue,
type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type),
label: fieldMetadataItemUsedInDropdown.label,
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup,
});
applyObjectFilterDropdownFilterValue(newValue);
};
if (!selectedOperandInDropdown || !fieldMetadataItemUsedInDropdown) {
@ -54,7 +30,7 @@ export const AdvancedFilterDropdownNumberInput = () => {
return (
<TextInputV2
value={inputValue}
value={objectFilterDropdownFilterValue}
onChange={handleChange}
placeholder="Enter value"
fullWidth

View File

@ -1,4 +1,4 @@
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
@ -9,21 +9,11 @@ type AdvancedFilterDropdownTextInputProps = {
export const AdvancedFilterDropdownTextInput = ({
recordFilter,
}: AdvancedFilterDropdownTextInputProps) => {
const { applyRecordFilter } = useApplyRecordFilter();
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const handleChange = (newValue: string) => {
applyRecordFilter({
id: recordFilter.id,
fieldMetadataId: recordFilter.fieldMetadataId,
value: newValue,
operand: recordFilter.operand,
displayValue: newValue,
type: recordFilter.type,
label: recordFilter.label,
recordFilterGroupId: recordFilter.recordFilterGroupId,
positionInRecordFilterGroup: recordFilter.positionInRecordFilterGroup,
subFieldName: recordFilter.subFieldName,
});
applyObjectFilterDropdownFilterValue(newValue);
};
return (

View File

@ -4,6 +4,8 @@ import { AdvancedFilterValueInputDropdownButtonClickableSelect } from '@/object-
import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset';
import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes';
import { TEXT_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/TextFilterTypes';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { configurableViewFilterOperands } from '@/object-record/object-filter-dropdown/utils/configurableViewFilterOperands';
import { isExpectedSubFieldName } from '@/object-record/object-filter-dropdown/utils/isExpectedSubFieldName';
@ -44,6 +46,15 @@ export const AdvancedFilterValueInput = ({
objectFilterDropdownSearchInputComponentState,
);
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
fieldMetadataItemIdUsedInDropdownComponentState,
);
const setObjectFilterDropdownCurrentRecordFilter =
useSetRecoilComponentStateV2(
objectFilterDropdownCurrentRecordFilterComponentState,
);
const operandHasNoInput =
recordFilter && !configurableViewFilterOperands.has(recordFilter.operand);
@ -55,6 +66,11 @@ export const AdvancedFilterValueInput = ({
setObjectFilterDropdownSearchInput('');
};
const handleFilterValueDropdownOpen = () => {
setObjectFilterDropdownCurrentRecordFilter(recordFilter);
setFieldMetadataItemIdUsedInDropdown(recordFilter.fieldMetadataId);
};
const filterType = recordFilter.type;
const dropdownContentOffset =
@ -98,6 +114,7 @@ export const AdvancedFilterValueInput = ({
dropdownPlacement="bottom-start"
dropdownWidth={280}
onClose={handleFilterValueDropdownClose}
onOpen={handleFilterValueDropdownOpen}
/>
)}
</StyledValueDropdownContainer>

View File

@ -1,12 +1,14 @@
import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
@ -40,14 +42,19 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
const setHotkeyScope = useSetHotkeyScope();
const { applyRecordFilter } = useApplyRecordFilter();
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
const setSubFieldNameUsedInDropdown = useSetRecoilComponentStateV2(
subFieldNameUsedInDropdownComponentState,
);
const setObjectFilterDropdownCurrentRecordFilter =
useSetRecoilComponentStateV2(
objectFilterDropdownCurrentRecordFilterComponentState,
);
const { upsertRecordFilter } = useUpsertRecordFilter();
const selectFieldUsedInAdvancedFilterDropdown = ({
fieldMetadataItemId,
recordFilterId,
@ -86,7 +93,7 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
(recordFilter) => recordFilter.id === recordFilterId,
);
applyRecordFilter({
const newAdvancedFilter = {
id: recordFilterId,
fieldMetadataId: fieldMetadataItem.id,
displayValue,
@ -97,12 +104,15 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
existingRecordFilter?.positionInRecordFilterGroup,
type: filterType,
label: fieldMetadataItem.label,
subFieldName: subFieldName ?? null,
});
subFieldName,
} satisfies RecordFilter;
setSubFieldNameUsedInDropdown(subFieldName);
setObjectFilterDropdownSearchInput('');
setObjectFilterDropdownCurrentRecordFilter(newAdvancedFilter);
upsertRecordFilter(newAdvancedFilter);
};
return {

View File

@ -0,0 +1,98 @@
import { rootLevelRecordFilterGroupComponentSelector } from '@/object-record/advanced-filter/states/rootLevelRecordFilterGroupComponentSelector';
import { getAdvancedFilterObjectFilterDropdownComponentInstanceId } from '@/object-record/advanced-filter/utils/getAdvancedFilterObjectFilterDropdownComponentInstanceId';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useRecoilCallback } from 'recoil';
export const useSetAdvancedFilterDropdownStates = () => {
const rootLevelRecordFilterGroup = useRecoilComponentValueV2(
rootLevelRecordFilterGroupComponentSelector,
);
const currentRecordFilters = useRecoilComponentValueV2(
currentRecordFiltersComponentState,
);
const currentRecordFilterGroups = useRecoilComponentValueV2(
currentRecordFilterGroupsComponentState,
);
const setAdvancedFilterDropdownStates = useRecoilCallback(
({ set }) =>
() => {
const rootLevelRecordFilters = currentRecordFilters.filter(
(recordFilter) =>
recordFilter.recordFilterGroupId === rootLevelRecordFilterGroup?.id,
);
for (const rootLevelRecordFilter of rootLevelRecordFilters) {
set(
objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({
instanceId:
getAdvancedFilterObjectFilterDropdownComponentInstanceId(
rootLevelRecordFilter.id,
),
}),
rootLevelRecordFilter,
);
set(
fieldMetadataItemIdUsedInDropdownComponentState.atomFamily({
instanceId:
getAdvancedFilterObjectFilterDropdownComponentInstanceId(
rootLevelRecordFilter.id,
),
}),
rootLevelRecordFilter.fieldMetadataId,
);
}
const childRecordFilterGroups = currentRecordFilterGroups.filter(
(currentRecordGroupToFilter) =>
currentRecordGroupToFilter.parentRecordFilterGroupId ===
rootLevelRecordFilterGroup?.id,
);
for (const childRecordFilterGroup of childRecordFilterGroups) {
const recordFiltersInThisGroup = currentRecordFilters.filter(
(recordFilter) =>
recordFilter.recordFilterGroupId === childRecordFilterGroup.id,
);
for (const recordFilterInThisGroup of recordFiltersInThisGroup) {
set(
objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({
instanceId:
getAdvancedFilterObjectFilterDropdownComponentInstanceId(
recordFilterInThisGroup.id,
),
}),
recordFilterInThisGroup,
);
set(
fieldMetadataItemIdUsedInDropdownComponentState.atomFamily({
instanceId:
getAdvancedFilterObjectFilterDropdownComponentInstanceId(
recordFilterInThisGroup.id,
),
}),
recordFilterInThisGroup.fieldMetadataId,
);
}
}
},
[
currentRecordFilterGroups,
currentRecordFilters,
rootLevelRecordFilterGroup,
],
);
return {
setAdvancedFilterDropdownStates,
};
};

View File

@ -1,6 +1,6 @@
import { getAdvancedFilterObjectFilterDropdownComponentInstanceId } from '@/object-record/advanced-filter/utils/getAdvancedFilterObjectFilterDropdownComponentInstanceId';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { useRecoilCallback } from 'recoil';
@ -31,7 +31,7 @@ export const useSetRecordFilterUsedInAdvancedFilterDropdownRow = () => {
);
set(
selectedFilterComponentState.atomFamily({
objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({
instanceId:
advancedFilterRowObjectFilterDropdownComponentInstanceId,
}),

View File

@ -1,20 +1,13 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useEffect, useState } from 'react';
import { v4 } from 'uuid';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
import { BooleanDisplay } from '@/ui/field/display/components/BooleanDisplay';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isDefined } from 'twenty-shared/utils';
import { IconCheck } from 'twenty-ui/display';
const StyledBooleanSelectContainer = styled.div<{ selected?: boolean }>`
@ -40,52 +33,20 @@ export const ObjectFilterDropdownBooleanSelect = () => {
const theme = useTheme();
const options = [true, false];
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
const { objectFilterDropdownFilterValue } =
useObjectFilterDropdownFilterValue();
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
);
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
);
const { applyRecordFilter } = useApplyRecordFilter();
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const { closeDropdown } = useDropdown();
const [selectedValue, setSelectedValue] = useState<boolean | undefined>(
selectedFilter?.value === 'true',
);
const handleOptionSelect = (newValue: boolean) => {
applyObjectFilterDropdownFilterValue(
newValue.toString(),
newValue ? 'True' : 'False',
);
useEffect(() => {
setSelectedValue(selectedFilter?.value === 'true');
}, [selectedFilter?.value]);
const handleOptionSelect = (value: boolean) => {
if (
!isDefined(fieldMetadataItemUsedInDropdown) ||
!isDefined(selectedOperandInDropdown)
) {
return;
}
applyRecordFilter({
id: selectedFilter?.id ?? v4(),
operand: selectedOperandInDropdown,
displayValue: value ? 'True' : 'False',
fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
value: value.toString(),
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup,
type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type),
label: fieldMetadataItemUsedInDropdown.label,
subFieldName: selectedFilter?.subFieldName,
});
setSelectedValue(value);
closeDropdown();
};
@ -100,10 +61,10 @@ export const ObjectFilterDropdownBooleanSelect = () => {
<StyledBooleanSelectContainer
key={String(option)}
onClick={() => handleOptionSelect(option)}
selected={selectedValue === option}
selected={objectFilterDropdownFilterValue === option.toString()}
>
<BooleanDisplay value={option} />
{selectedFilter?.value === option.toString() && (
{objectFilterDropdownFilterValue === option.toString() && (
<StyledIconCheckContainer>
<IconCheck color={theme.grayScale.gray50} size={16} />
</StyledIconCheckContainer>

View File

@ -1,12 +1,7 @@
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { turnCurrencyIntoSelectableItem } from '@/object-record/object-filter-dropdown/utils/turnCurrencyIntoSelectableItem';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters';
import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip';
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { CURRENCIES } from '@/settings/data-model/constants/Currencies';
@ -14,70 +9,55 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useLingui } from '@lingui/react/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { ChangeEvent, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
import { v4 } from 'uuid';
export const EMPTY_FILTER_VALUE = '[]';
export const MAX_ITEMS_TO_DISPLAY = 3;
type ObjectFilterDropdownCurrencySelectProps = {
viewComponentId?: string;
dropdownWidth?: number;
};
export const ObjectFilterDropdownCurrencySelect = ({
viewComponentId,
dropdownWidth,
}: ObjectFilterDropdownCurrencySelectProps) => {
const [searchText, setSearchText] = useState('');
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2(
objectFilterDropdownCurrentRecordFilterComponentState,
);
const setObjectFilterDropdownSelectedRecordIds = useSetRecoilComponentStateV2(
objectFilterDropdownSelectedRecordIdsComponentState,
selectedFilter?.id,
);
const objectFilterDropdownSelectedRecordIds = useRecoilComponentValueV2(
objectFilterDropdownSelectedRecordIdsComponentState,
selectedFilter?.id,
);
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
);
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
const { applyRecordFilter } = useApplyRecordFilter(viewComponentId);
const currenciesAsSelectableItems = CURRENCIES.map(
turnCurrencyIntoSelectableItem,
);
const selectedCurrencies = isNonEmptyString(
objectFilterDropdownCurrentRecordFilter?.value,
)
? (JSON.parse(objectFilterDropdownCurrentRecordFilter.value) as string[]) // TODO: replace by a safe parse
: [];
const filteredSelectableItems = currenciesAsSelectableItems.filter(
(selectableItem) =>
selectableItem.name.toLowerCase().includes(searchText.toLowerCase()) &&
!objectFilterDropdownSelectedRecordIds.includes(selectableItem.id),
!selectedCurrencies.includes(selectableItem.id),
);
const filteredSelectedItems = currenciesAsSelectableItems.filter(
(selectableItem) =>
selectableItem.name.toLowerCase().includes(searchText.toLowerCase()) &&
objectFilterDropdownSelectedRecordIds.includes(selectableItem.id),
);
const currentRecordFilters = useRecoilComponentValueV2(
currentRecordFiltersComponentState,
selectedCurrencies.includes(selectableItem.id),
);
const handleMultipleItemSelectChange = (
@ -85,10 +65,8 @@ export const ObjectFilterDropdownCurrencySelect = ({
newSelectedValue: boolean,
) => {
const newSelectedItemIds = newSelectedValue
? [...objectFilterDropdownSelectedRecordIds, itemToSelect.id]
: objectFilterDropdownSelectedRecordIds.filter(
(id) => id !== itemToSelect.id,
);
? [...selectedCurrencies, itemToSelect.id]
: selectedCurrencies.filter((id) => id !== itemToSelect.id);
if (!isDefined(fieldMetadataItemUsedInFilterDropdown)) {
throw new Error(
@ -96,8 +74,6 @@ export const ObjectFilterDropdownCurrencySelect = ({
);
}
setObjectFilterDropdownSelectedRecordIds(newSelectedItemIds);
const selectedItemNames = currenciesAsSelectableItems
.filter((option) => newSelectedItemIds.includes(option.id))
.map((option) => option.name);
@ -107,46 +83,12 @@ export const ObjectFilterDropdownCurrencySelect = ({
? `${selectedItemNames.length} currencies`
: selectedItemNames.join(', ');
if (
isDefined(fieldMetadataItemUsedInFilterDropdown) &&
isDefined(selectedOperandInDropdown)
) {
const newFilterValue =
newSelectedItemIds.length > 0
? JSON.stringify(newSelectedItemIds)
: EMPTY_FILTER_VALUE;
const newFilterValue =
newSelectedItemIds.length > 0
? JSON.stringify(newSelectedItemIds)
: EMPTY_FILTER_VALUE;
const duplicateFilterInCurrentRecordFilters =
findDuplicateRecordFilterInNonAdvancedRecordFilters({
recordFilters: currentRecordFilters,
fieldMetadataItemId: fieldMetadataItemUsedInFilterDropdown.id,
subFieldName: 'currencyCode',
});
const filterIsAlreadyInCurrentRecordFilters = isDefined(
duplicateFilterInCurrentRecordFilters,
);
const filterId = filterIsAlreadyInCurrentRecordFilters
? duplicateFilterInCurrentRecordFilters?.id
: v4();
applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : filterId,
type: getFilterTypeFromFieldType(
fieldMetadataItemUsedInFilterDropdown.type,
),
label: fieldMetadataItemUsedInFilterDropdown.label,
operand: selectedOperandInDropdown || ViewFilterOperand.Is,
displayValue: filterDisplayValue,
fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id,
value: newFilterValue,
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
subFieldName: 'currencyCode',
positionInRecordFilterGroup:
selectedFilter?.positionInRecordFilterGroup,
});
}
applyObjectFilterDropdownFilterValue(newFilterValue, filterDisplayValue);
};
const showNoResult =

View File

@ -1,11 +1,8 @@
import { v4 } from 'uuid';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { getRelativeDateDisplayValue } from '@/object-record/object-filter-dropdown/utils/getRelativeDateDisplayValue';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { DateTimePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
@ -28,15 +25,17 @@ export const ObjectFilterDropdownDateInput = () => {
selectedOperandInDropdownComponentState,
);
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2(
objectFilterDropdownCurrentRecordFilterComponentState,
);
const { applyRecordFilter } = useApplyRecordFilter();
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const initialFilterValue = selectedFilter
? resolveDateViewFilterValue(selectedFilter)
const initialFilterValue = isDefined(objectFilterDropdownCurrentRecordFilter)
? resolveDateViewFilterValue(objectFilterDropdownCurrentRecordFilter)
: null;
const [internalDate, setInternalDate] = useState<Date | null>(
initialFilterValue instanceof Date ? initialFilterValue : null,
);
@ -47,24 +46,14 @@ export const ObjectFilterDropdownDateInput = () => {
const handleAbsoluteDateChange = (newDate: Date | null) => {
setInternalDate(newDate);
if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) return;
const newFilterValue = newDate?.toISOString() ?? '';
const newDisplayValue = isDefined(newDate)
? isDateTimeInput
? newDate.toLocaleString()
: newDate.toLocaleDateString()
: '';
applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
value: newDate?.toISOString() ?? '',
operand: selectedOperandInDropdown,
displayValue: isDefined(newDate)
? isDateTimeInput
? newDate.toLocaleString()
: newDate.toLocaleDateString()
: '',
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup,
type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type),
label: fieldMetadataItemUsedInDropdown.label,
subFieldName: selectedFilter?.subFieldName,
});
applyObjectFilterDropdownFilterValue(newFilterValue, newDisplayValue);
};
const handleRelativeDateChange = (
@ -74,9 +63,7 @@ export const ObjectFilterDropdownDateInput = () => {
unit: VariableDateViewFilterValueUnit;
} | null,
) => {
if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) return;
const value = relativeDate
const newFilterValue = relativeDate
? computeVariableDateViewFilterValue(
relativeDate.direction,
relativeDate.amount,
@ -84,24 +71,18 @@ export const ObjectFilterDropdownDateInput = () => {
)
: '';
applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
value,
operand: selectedOperandInDropdown,
displayValue: getRelativeDateDisplayValue(relativeDate),
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup,
type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type),
label: fieldMetadataItemUsedInDropdown.label,
});
const newDisplayValue = relativeDate
? getRelativeDateDisplayValue(relativeDate)
: '';
applyObjectFilterDropdownFilterValue(newFilterValue, newDisplayValue);
};
const isRelativeOperand =
selectedOperandInDropdown === ViewFilterOperand.IsRelative;
const resolvedValue = selectedFilter
? resolveDateViewFilterValue(selectedFilter)
const resolvedValue = objectFilterDropdownCurrentRecordFilter
? resolveDateViewFilterValue(objectFilterDropdownCurrentRecordFilter)
: null;
const relativeDate =

View File

@ -9,7 +9,6 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { FILTER_FIELD_LIST_ID } from '@/object-record/object-filter-dropdown/constants/FilterFieldListId';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters';
@ -68,10 +67,6 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
currentRecordFiltersComponentState,
);
const setSelectedFilter = useSetRecoilComponentStateV2(
selectedFilterComponentState,
);
const setObjectFilterDropdownCurrentRecordFilter =
useSetRecoilComponentStateV2(
objectFilterDropdownCurrentRecordFilterComponentState,
@ -103,8 +98,6 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
);
if (filterIsAlreadyInCurrentRecordFilters) {
setSelectedFilter(duplicateFilterInCurrentRecordFilters);
setObjectFilterDropdownCurrentRecordFilter(
duplicateFilterInCurrentRecordFilters,
);

View File

@ -1,41 +1,25 @@
import { ChangeEvent, useCallback, useState } from 'react';
import { v4 } from 'uuid';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
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';
export const ObjectFilterDropdownNumberInput = () => {
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
);
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
);
const { objectFilterDropdownFilterValue } =
useObjectFilterDropdownFilterValue();
const subFieldNameUsedInDropdown = useRecoilComponentValueV2(
subFieldNameUsedInDropdownComponentState,
);
const { applyRecordFilter } = useApplyRecordFilter();
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const [hasFocused, setHasFocused] = useState(false);
const [inputValue, setInputValue] = useState(
() => selectedFilter?.value || '',
);
const handleInputRef = useCallback(
(node: HTMLInputElement | null) => {
if (Boolean(node) && !hasFocused) {
@ -47,39 +31,22 @@ export const ObjectFilterDropdownNumberInput = () => {
[hasFocused],
);
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
applyObjectFilterDropdownFilterValue(newValue);
};
return (
fieldMetadataItemUsedInDropdown &&
selectedOperandInDropdown && (
<DropdownMenuItemsContainer>
<DropdownMenuInput
ref={handleInputRef}
value={inputValue}
autoFocus
type="number"
placeholder={fieldMetadataItemUsedInDropdown.label}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
setInputValue(newValue);
applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown?.id ?? '',
value: newValue,
operand: selectedOperandInDropdown,
displayValue: newValue,
type: getFilterTypeFromFieldType(
fieldMetadataItemUsedInDropdown.type,
),
label: fieldMetadataItemUsedInDropdown.label,
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
positionInRecordFilterGroup:
selectedFilter?.positionInRecordFilterGroup,
subFieldName: subFieldNameUsedInDropdown,
});
}}
/>
</DropdownMenuItemsContainer>
)
<DropdownMenuItemsContainer>
<DropdownMenuInput
ref={handleInputRef}
value={objectFilterDropdownFilterValue}
autoFocus
type="number"
placeholder={fieldMetadataItemUsedInDropdown?.label}
onChange={handleInputChange}
/>
</DropdownMenuItemsContainer>
);
};

View File

@ -1,22 +1,16 @@
import { v4 } from 'uuid';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import styled from '@emotion/styled';
import { isDefined } from 'twenty-shared/utils';
import { getOperandLabel } from '../utils/getOperandLabel';
import { MenuItem } from 'twenty-ui/navigation';
import { getOperandLabel } from '../utils/getOperandLabel';
const StyledDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
background-color: ${({ theme }) => theme.background.primary};
@ -28,19 +22,12 @@ export const ObjectFilterDropdownOperandSelect = () => {
fieldMetadataItemUsedInDropdownComponentSelector,
);
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
selectedOperandInDropdownComponentState,
);
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
);
const subFieldNameUsedInDropdown = useRecoilComponentValueV2(
subFieldNameUsedInDropdownComponentState,
);
const { applyRecordFilter } = useApplyRecordFilter();
const { applyObjectFilterDropdownOperand } =
useApplyObjectFilterDropdownOperand();
const { closeDropdown } = useDropdown();
@ -54,56 +41,7 @@ export const ObjectFilterDropdownOperandSelect = () => {
: [];
const handleOperandChange = (newOperand: ViewFilterOperand) => {
const isValuelessOperand = [
ViewFilterOperand.IsEmpty,
ViewFilterOperand.IsNotEmpty,
ViewFilterOperand.IsInPast,
ViewFilterOperand.IsInFuture,
ViewFilterOperand.IsToday,
].includes(newOperand);
setSelectedOperandInDropdown(newOperand);
if (isValuelessOperand && isDefined(fieldMetadataItemUsedInDropdown)) {
applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
displayValue: '',
operand: newOperand,
value: '',
type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type),
label: fieldMetadataItemUsedInDropdown.label,
subFieldName: subFieldNameUsedInDropdown,
});
return;
}
if (
isDefined(fieldMetadataItemUsedInDropdown) &&
isDefined(selectedFilter)
) {
const filterType = getFilterTypeFromFieldType(
fieldMetadataItemUsedInDropdown.type,
);
const { value, displayValue } = getInitialFilterValue(
filterType,
newOperand,
selectedFilter.value,
selectedFilter.displayValue,
);
applyRecordFilter({
id: selectedFilter.id ? selectedFilter.id : v4(),
fieldMetadataId: selectedFilter.fieldMetadataId,
displayValue,
operand: newOperand,
value,
type: filterType,
label: fieldMetadataItemUsedInDropdown.label,
subFieldName: subFieldNameUsedInDropdown,
});
}
applyObjectFilterDropdownOperand(newOperand);
};
return (

View File

@ -1,6 +1,5 @@
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Key } from 'ts-key-enum';
import { v4 } from 'uuid';
import { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem';
import { useOptionsForSelect } from '@/object-record/object-filter-dropdown/hooks/useOptionsForSelect';
@ -10,19 +9,17 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isNonEmptyString } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { MenuItem, MenuItemMultiSelect } from 'twenty-ui/navigation';
@ -38,27 +35,30 @@ export const ObjectFilterDropdownOptionSelect = () => {
fieldMetadataItemUsedInDropdownComponentSelector,
);
const objectFilterDropdownSelectedOptionValues = useRecoilComponentValueV2(
objectFilterDropdownSelectedOptionValuesComponentState,
);
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
);
const objectFilterDropdownSearchInput = useRecoilComponentValueV2(
objectFilterDropdownSearchInputComponentState,
);
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
);
const componentInstanceId = useAvailableComponentInstanceIdOrThrow(
ObjectFilterDropdownComponentInstanceContext,
);
const { applyRecordFilter } = useApplyRecordFilter();
const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2(
objectFilterDropdownCurrentRecordFilterComponentState,
);
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const selectedOptions = useMemo(
() =>
isNonEmptyString(objectFilterDropdownCurrentRecordFilter?.value)
? (JSON.parse(
objectFilterDropdownCurrentRecordFilter.value,
) as string[]) // TODO: replace by a safe parse
: [],
[objectFilterDropdownCurrentRecordFilter?.value],
);
const { closeDropdown } = useDropdown();
@ -80,9 +80,7 @@ export const ObjectFilterDropdownOptionSelect = () => {
useEffect(() => {
if (isDefined(selectOptions)) {
const options = selectOptions.map((option) => {
const isSelected =
objectFilterDropdownSelectedOptionValues?.includes(option.value) ??
false;
const isSelected = selectedOptions?.includes(option.value) ?? false;
return {
...option,
@ -92,7 +90,7 @@ export const ObjectFilterDropdownOptionSelect = () => {
setSelectableOptions(options);
}
}, [objectFilterDropdownSelectedOptionValues, selectOptions]);
}, [selectedOptions, selectOptions]);
useScopedHotkeys(
[Key.Escape],
@ -127,29 +125,13 @@ export const ObjectFilterDropdownOptionSelect = () => {
? `${selectedOptions.length} options`
: selectedOptions.map((option) => option.label).join(', ');
if (
isDefined(fieldMetadataItemUsedInDropdown) &&
isDefined(selectedOperandInDropdown)
) {
const newFilterValue =
selectedOptions.length > 0
? JSON.stringify(selectedOptions.map((option) => option.value))
: EMPTY_FILTER_VALUE;
const newFilterValue =
selectedOptions.length > 0
? JSON.stringify(selectedOptions.map((option) => option.value))
: EMPTY_FILTER_VALUE;
applyObjectFilterDropdownFilterValue(newFilterValue, filterDisplayValue);
applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : v4(),
type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type),
label: fieldMetadataItemUsedInDropdown.label,
operand: selectedOperandInDropdown,
displayValue: filterDisplayValue,
fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
value: newFilterValue,
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
positionInRecordFilterGroup:
selectedFilter?.positionInRecordFilterGroup,
subFieldName: selectedFilter?.subFieldName,
});
}
resetSelectedItem();
};

View File

@ -1,15 +1,9 @@
import { v4 } from 'uuid';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { RATING_VALUES } from '@/object-record/record-field/meta-types/constants/RatingValues';
import { FieldRatingValue } from '@/object-record/record-field/types/FieldMetadata';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { RatingInput } from '@/ui/field/input/components/RatingInput';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
import styled from '@emotion/styled';
const StyledRatingInputContainer = styled.div`
@ -35,53 +29,37 @@ export const convertLessThanRatingToArrayOfRatingValues = (
};
export const convertRatingToRatingValue = (rating: number) => {
return `RATING_${rating}`;
return `RATING_${rating}` as FieldRatingValue;
};
export const ObjectFilterDropdownRatingInput = () => {
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
);
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
const { objectFilterDropdownFilterValue } =
useObjectFilterDropdownFilterValue();
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
);
const handleInputChange = (newRatingValue: FieldRatingValue) => {
if (!newRatingValue) {
return;
}
const { applyRecordFilter } = useApplyRecordFilter();
const ratingValueConverted =
convertFieldRatingValueToNumber(newRatingValue);
applyObjectFilterDropdownFilterValue(ratingValueConverted);
};
const currentFilterValueConvertedToRatingValue = convertRatingToRatingValue(
Number(objectFilterDropdownFilterValue),
);
return (
fieldMetadataItemUsedInDropdown &&
selectedOperandInDropdown && (
<StyledRatingInputContainer>
<RatingInput
value={selectedFilter?.value as FieldRatingValue}
onChange={(newValue: FieldRatingValue) => {
if (!newValue) {
return;
}
applyRecordFilter?.({
id: selectedFilter?.id ? selectedFilter.id : v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
value: convertFieldRatingValueToNumber(newValue),
operand: selectedOperandInDropdown,
displayValue: convertFieldRatingValueToNumber(newValue),
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
positionInRecordFilterGroup:
selectedFilter?.positionInRecordFilterGroup,
type: getFilterTypeFromFieldType(
fieldMetadataItemUsedInDropdown.type,
),
label: fieldMetadataItemUsedInDropdown.label,
subFieldName: selectedFilter?.subFieldName,
});
}}
/>
</StyledRatingInputContainer>
)
<StyledRatingInputContainer>
<RatingInput
value={currentFilterValueConvertedToRatingValue}
onChange={handleInputChange}
/>
</StyledRatingInputContainer>
);
};

View File

@ -1,17 +1,13 @@
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import {
getFilterTypeFromFieldType,
getRelationObjectMetadataNameSingular,
} from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { getRelationObjectMetadataNameSingular } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { ObjectFilterDropdownRecordPinnedItems } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordPinnedItems';
import { CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID } from '@/object-record/object-filter-dropdown/constants/CurrentWorkspaceMemberSelectableItemId';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
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 { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
@ -23,7 +19,6 @@ import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validat
import { simpleRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/simpleRelationFilterValueSchema';
import { isDefined } from 'twenty-shared/utils';
import { IconUserCircle } from 'twenty-ui/display';
import { v4 } from 'uuid';
export const EMPTY_FILTER_VALUE: string = JSON.stringify({
isCurrentWorkspaceMemberSelected: false,
@ -33,26 +28,26 @@ export const EMPTY_FILTER_VALUE: string = JSON.stringify({
export const MAX_RECORDS_TO_DISPLAY = 3;
type ObjectFilterDropdownRecordSelectProps = {
viewComponentId?: string;
recordFilterId?: string;
};
export const ObjectFilterDropdownRecordSelect = ({
viewComponentId,
recordFilterId,
}: ObjectFilterDropdownRecordSelectProps) => {
const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
const { objectFilterDropdownFilterValue } =
useObjectFilterDropdownFilterValue();
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
);
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
);
const objectFilterDropdownSearchInput = useRecoilComponentValueV2(
objectFilterDropdownSearchInputComponentState,
);
@ -61,16 +56,14 @@ export const ObjectFilterDropdownRecordSelect = ({
currentRecordFiltersComponentState,
);
const { applyRecordFilter } = useApplyRecordFilter(viewComponentId);
const { isCurrentWorkspaceMemberSelected } = jsonRelationFilterValueSchema
.catch({
isCurrentWorkspaceMemberSelected: false,
selectedRecordIds: simpleRelationFilterValueSchema.parse(
selectedFilter?.value,
objectFilterDropdownFilterValue,
),
})
.parse(selectedFilter?.value);
.parse(objectFilterDropdownFilterValue);
if (!isDefined(fieldMetadataItemUsedInFilterDropdown)) {
throw new Error('fieldMetadataItemUsedInFilterDropdown is not defined');
@ -210,50 +203,7 @@ export const ObjectFilterDropdownRecordSelect = ({
} satisfies RelationFilterValue)
: '';
const duplicateFilterInCurrentRecordFilters =
findDuplicateRecordFilterInNonAdvancedRecordFilters({
recordFilters: currentRecordFilters,
fieldMetadataItemId: fieldMetadataItemUsedInFilterDropdown.id,
});
const filterIsAlreadyInCurrentRecordFilters = isDefined(
duplicateFilterInCurrentRecordFilters,
);
if (filterIsAlreadyInCurrentRecordFilters && !isDefined(recordFilterId)) {
applyRecordFilter({
id: duplicateFilterInCurrentRecordFilters.id,
type: getFilterTypeFromFieldType(
fieldMetadataItemUsedInFilterDropdown.type,
),
label: fieldMetadataItemUsedInFilterDropdown.label,
operand: selectedOperandInDropdown,
displayValue: filterDisplayValue,
fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id,
value: newFilterValue,
recordFilterGroupId:
duplicateFilterInCurrentRecordFilters.recordFilterGroupId,
positionInRecordFilterGroup:
duplicateFilterInCurrentRecordFilters.positionInRecordFilterGroup,
subFieldName: duplicateFilterInCurrentRecordFilters.subFieldName,
});
} 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,
positionInRecordFilterGroup:
selectedFilter?.positionInRecordFilterGroup,
subFieldName: selectedFilter?.subFieldName,
});
}
applyObjectFilterDropdownFilterValue(newFilterValue, filterDisplayValue);
}
};

View File

@ -1,68 +1,44 @@
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
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 { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { getActorSourceMultiSelectOptions } from '@/object-record/object-filter-dropdown/utils/getActorSourceMultiSelectOptions';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
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 { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { isNonEmptyString } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
export const EMPTY_FILTER_VALUE = '[]';
export const MAX_ITEMS_TO_DISPLAY = 3;
type ObjectFilterDropdownSourceSelectProps = {
viewComponentId?: string;
};
export const ObjectFilterDropdownSourceSelect = ({
viewComponentId,
}: ObjectFilterDropdownSourceSelectProps) => {
export const ObjectFilterDropdownSourceSelect = () => {
const objectFilterDropdownSearchInput = useRecoilComponentValueV2(
objectFilterDropdownSearchInputComponentState,
);
const setObjectFilterDropdownSelectedRecordIds = useSetRecoilComponentStateV2(
objectFilterDropdownSelectedRecordIdsComponentState,
);
const objectFilterDropdownSelectedRecordIds = useRecoilComponentValueV2(
objectFilterDropdownSelectedRecordIdsComponentState,
);
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
);
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
);
const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
const { applyRecordFilter } = useApplyRecordFilter(viewComponentId);
const sourceTypes = getActorSourceMultiSelectOptions(
objectFilterDropdownSelectedRecordIds,
const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2(
objectFilterDropdownCurrentRecordFilterComponentState,
);
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const selectedSources = isNonEmptyString(
objectFilterDropdownCurrentRecordFilter?.value,
)
? (JSON.parse(objectFilterDropdownCurrentRecordFilter.value) as string[]) // TODO: replace by a safe parse
: [];
const sourceTypes = getActorSourceMultiSelectOptions(selectedSources);
const filteredSelectedItems = sourceTypes.filter((option) =>
objectFilterDropdownSelectedRecordIds.includes(option.id),
);
const currentRecordFilters = useRecoilComponentValueV2(
currentRecordFiltersComponentState,
selectedSources.includes(option.id),
);
const handleMultipleItemSelectChange = (
@ -70,10 +46,8 @@ export const ObjectFilterDropdownSourceSelect = ({
newSelectedValue: boolean,
) => {
const newSelectedItemIds = newSelectedValue
? [...objectFilterDropdownSelectedRecordIds, itemToSelect.id]
: objectFilterDropdownSelectedRecordIds.filter(
(id) => id !== itemToSelect.id,
);
? [...selectedSources, itemToSelect.id]
: selectedSources.filter((id) => id !== itemToSelect.id);
if (!isDefined(fieldMetadataItemUsedInFilterDropdown)) {
throw new Error(
@ -81,8 +55,6 @@ export const ObjectFilterDropdownSourceSelect = ({
);
}
setObjectFilterDropdownSelectedRecordIds(newSelectedItemIds);
const selectedItemNames = sourceTypes
.filter((option) => newSelectedItemIds.includes(option.id))
.map((option) => option.name);
@ -92,46 +64,12 @@ export const ObjectFilterDropdownSourceSelect = ({
? `${selectedItemNames.length} source types`
: selectedItemNames.join(', ');
if (
isDefined(fieldMetadataItemUsedInFilterDropdown) &&
isDefined(selectedOperandInDropdown)
) {
const newFilterValue =
newSelectedItemIds.length > 0
? JSON.stringify(newSelectedItemIds)
: EMPTY_FILTER_VALUE;
const newFilterValue =
newSelectedItemIds.length > 0
? JSON.stringify(newSelectedItemIds)
: EMPTY_FILTER_VALUE;
const duplicateFilterInCurrentRecordFilters =
findDuplicateRecordFilterInNonAdvancedRecordFilters({
recordFilters: currentRecordFilters,
fieldMetadataItemId: fieldMetadataItemUsedInFilterDropdown.id,
subFieldName: 'source',
});
const filterIsAlreadyInCurrentRecordFilters = isDefined(
duplicateFilterInCurrentRecordFilters,
);
const filterId = filterIsAlreadyInCurrentRecordFilters
? duplicateFilterInCurrentRecordFilters?.id
: v4();
applyRecordFilter({
id: selectedFilter?.id ? selectedFilter.id : filterId,
type: getFilterTypeFromFieldType(
fieldMetadataItemUsedInFilterDropdown.type,
),
label: fieldMetadataItemUsedInFilterDropdown.label,
operand: selectedOperandInDropdown || ViewFilterOperand.Is,
displayValue: filterDisplayValue,
fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id,
value: newFilterValue,
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
subFieldName: 'source',
positionInRecordFilterGroup:
selectedFilter?.positionInRecordFilterGroup,
});
}
applyObjectFilterDropdownFilterValue(newFilterValue, filterDisplayValue);
};
return (

View File

@ -9,7 +9,6 @@ import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-rec
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
@ -81,10 +80,6 @@ export const ObjectFilterDropdownSubFieldSelect = () => {
currentRecordFiltersComponentState,
);
const setSelectedFilter = useSetRecoilComponentStateV2(
selectedFilterComponentState,
);
const setObjectFilterDropdownCurrentRecordFilter =
useSetRecoilComponentStateV2(
objectFilterDropdownCurrentRecordFilterComponentState,
@ -125,8 +120,6 @@ export const ObjectFilterDropdownSubFieldSelect = () => {
);
if (filterIsAlreadyInCurrentRecordFilters) {
setSelectedFilter(duplicateFilterInCurrentRecordFilters);
setObjectFilterDropdownCurrentRecordFilter(
duplicateFilterInCurrentRecordFilters,
);

View File

@ -31,6 +31,12 @@ export const ObjectFilterDropdownTextInput = () => {
[hasFocused],
);
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
applyObjectFilterDropdownFilterValue(newValue);
};
return (
<DropdownMenuItemsContainer>
<DropdownMenuInput
@ -39,11 +45,7 @@ export const ObjectFilterDropdownTextInput = () => {
autoFocus
type="text"
placeholder={fieldMetadataItemUsedInDropdown?.label}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
applyObjectFilterDropdownFilterValue(newValue);
}}
onChange={handleInputChange}
/>
</DropdownMenuItemsContainer>
);

View File

@ -1,80 +0,0 @@
import { ChangeEvent, useCallback, useState } from 'react';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { v4 } from 'uuid';
export const ObjectFilterDropdownTextSearchInput = () => {
const [hasFocused, setHasFocused] = useState(false);
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
);
const objectFilterDropdownSearchInput = useRecoilComponentValueV2(
objectFilterDropdownSearchInputComponentState,
);
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
);
const setObjectFilterDropdownSearchInput = useSetRecoilComponentStateV2(
objectFilterDropdownSearchInputComponentState,
);
const { applyRecordFilter } = useApplyRecordFilter();
const handleInputRef = useCallback(
(node: HTMLInputElement | null) => {
if (Boolean(node) && !hasFocused) {
node?.focus();
node?.select();
setHasFocused(true);
}
},
[hasFocused],
);
return (
fieldMetadataItemUsedInDropdown &&
selectedOperandInDropdown && (
<DropdownMenuSearchInput
ref={handleInputRef}
autoFocus
type="text"
placeholder={fieldMetadataItemUsedInDropdown.label}
value={selectedFilter?.value ?? objectFilterDropdownSearchInput}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setObjectFilterDropdownSearchInput(event.target.value);
applyRecordFilter({
id: selectedFilter?.id ?? v4(),
fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
value: event.target.value,
operand: selectedOperandInDropdown,
displayValue: event.target.value,
recordFilterGroupId: selectedFilter?.recordFilterGroupId,
type: getFilterTypeFromFieldType(
fieldMetadataItemUsedInDropdown.type,
),
label: fieldMetadataItemUsedInDropdown.label,
positionInRecordFilterGroup:
selectedFilter?.positionInRecordFilterGroup,
subFieldName: selectedFilter?.subFieldName,
});
}}
/>
)
);
};

View File

@ -25,7 +25,10 @@ export const useApplyObjectFilterDropdownFilterValue = () => {
const { upsertObjectFilterDropdownCurrentFilter } =
useUpsertObjectFilterDropdownCurrentFilter();
const applyObjectFilterDropdownFilterValue = (newFilterValue: string) => {
const applyObjectFilterDropdownFilterValue = (
newFilterValue: string,
newDisplayValue?: string,
) => {
if (objectFilterDropdownFilterNotYetCreated) {
if (!isDefined(fieldMetadataItemUsedInDropdown)) {
throw new Error(
@ -41,7 +44,7 @@ export const useApplyObjectFilterDropdownFilterValue = () => {
const newCurrentRecordFilter = {
...newRecordFilterFromObjectFilterDropdownStates,
value: newFilterValue,
displayValue: newFilterValue,
displayValue: newDisplayValue ?? newFilterValue,
} satisfies RecordFilter;
upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter);
@ -49,7 +52,7 @@ export const useApplyObjectFilterDropdownFilterValue = () => {
const newCurrentRecordFilter = {
...objectFilterDropdownCurrentRecordFilter,
value: newFilterValue,
displayValue: newFilterValue,
displayValue: newDisplayValue ?? newFilterValue,
} satisfies RecordFilter;
upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter);

View File

@ -0,0 +1,79 @@
import { useUpsertObjectFilterDropdownCurrentFilter } from '@/object-record/object-filter-dropdown/hooks/useUpsertObjectFilterDropdownCurrentFilter';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useCreateEmptyRecordFilterFromFieldMetadataItem } from '@/object-record/record-filter/hooks/useCreateEmptyRecordFilterFromFieldMetadataItem';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { isDefined } from 'twenty-shared/utils';
export const useApplyObjectFilterDropdownOperand = () => {
const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2(
objectFilterDropdownCurrentRecordFilterComponentState,
);
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
selectedOperandInDropdownComponentState,
);
const objectFilterDropdownFilterIsCreated = isDefined(
objectFilterDropdownCurrentRecordFilter,
);
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
const { upsertObjectFilterDropdownCurrentFilter } =
useUpsertObjectFilterDropdownCurrentFilter();
const { createEmptyRecordFilterFromFieldMetadataItem } =
useCreateEmptyRecordFilterFromFieldMetadataItem();
const applyObjectFilterDropdownOperand = (
newOperand: RecordFilterOperand,
) => {
const isValuelessOperand = [
RecordFilterOperand.IsEmpty,
RecordFilterOperand.IsNotEmpty,
RecordFilterOperand.IsInPast,
RecordFilterOperand.IsInFuture,
RecordFilterOperand.IsToday,
].includes(newOperand);
if (objectFilterDropdownFilterIsCreated) {
const newCurrentRecordFilter = {
...objectFilterDropdownCurrentRecordFilter,
operand: newOperand,
} satisfies RecordFilter;
upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter);
} else if (isValuelessOperand) {
if (!isDefined(fieldMetadataItemUsedInDropdown)) {
throw new Error(
'FieldMetadataItemUsedInDropdown is not defined, cannot create empty record filter, this should not happen',
);
}
const { newRecordFilter: emptyRecordFilter } =
createEmptyRecordFilterFromFieldMetadataItem(
fieldMetadataItemUsedInDropdown,
);
const recordFilterToCreate = {
...emptyRecordFilter,
operand: newOperand,
} satisfies RecordFilter;
upsertObjectFilterDropdownCurrentFilter(recordFilterToCreate);
}
setSelectedOperandInDropdown(newOperand);
};
return {
applyObjectFilterDropdownOperand,
};
};

View File

@ -1,6 +1,6 @@
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
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 { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useRecoilCallback } from 'recoil';
@ -17,22 +17,23 @@ export const useEmptyRecordFilter = (componentInstanceId?: string) => {
componentInstanceId,
);
const selectedFilterCallbackState = useRecoilComponentCallbackStateV2(
selectedFilterComponentState,
componentInstanceId,
);
const objectFilterDropdownCurrentRecordFilter =
useRecoilComponentCallbackStateV2(
objectFilterDropdownCurrentRecordFilterComponentState,
componentInstanceId,
);
const emptyRecordFilter = useRecoilCallback(
({ set }) =>
() => {
set(objectFilterDropdownSearchInputCallbackState, '');
set(objectFilterDropdownSelectedRecordIdsCallbackState, []);
set(selectedFilterCallbackState, undefined);
set(objectFilterDropdownCurrentRecordFilter, undefined);
},
[
objectFilterDropdownSearchInputCallbackState,
objectFilterDropdownSelectedRecordIdsCallbackState,
selectedFilterCallbackState,
objectFilterDropdownCurrentRecordFilter,
],
);

View File

@ -4,7 +4,6 @@ import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-rec
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
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 { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useRecoilCallback } from 'recoil';
@ -28,11 +27,6 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => {
componentInstanceId,
);
const selectedFilterCallbackState = useRecoilComponentCallbackStateV2(
selectedFilterComponentState,
componentInstanceId,
);
const selectedOperandInDropdownCallbackState =
useRecoilComponentCallbackStateV2(
selectedOperandInDropdownComponentState,
@ -62,7 +56,6 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => {
() => {
set(objectFilterDropdownSearchInputCallbackState, '');
set(objectFilterDropdownSelectedRecordIdsCallbackState, []);
set(selectedFilterCallbackState, undefined);
set(selectedOperandInDropdownCallbackState, null);
set(objectFilterDropdownFilterIsSelectedCallbackState, false);
set(objectFilterDropdownIsSelectingCompositeFieldCallbackState, false);
@ -72,7 +65,6 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => {
[
objectFilterDropdownSearchInputCallbackState,
objectFilterDropdownSelectedRecordIdsCallbackState,
selectedFilterCallbackState,
selectedOperandInDropdownCallbackState,
objectFilterDropdownFilterIsSelectedCallbackState,
objectFilterDropdownIsSelectingCompositeFieldCallbackState,

View File

@ -1,97 +0,0 @@
import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
import { getDefaultSubFieldNameForCompositeFilterableFieldType } from '@/object-record/record-filter/utils/getDefaultSubFieldNameForCompositeFilterableFieldType';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
type SelectFilterParams = {
fieldMetadataItemId: string;
};
export const useSelectFilterUsedInDropdown = (componentInstanceId?: string) => {
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
selectedOperandInDropdownComponentState,
componentInstanceId,
);
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
fieldMetadataItemIdUsedInDropdownComponentState,
componentInstanceId,
);
const setObjectFilterDropdownSearchInput = useSetRecoilComponentStateV2(
objectFilterDropdownSearchInputComponentState,
componentInstanceId,
);
const setHotkeyScope = useSetHotkeyScope();
const { applyRecordFilter } = useApplyRecordFilter(componentInstanceId);
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
const selectFilterUsedInDropdown = ({
fieldMetadataItemId,
}: SelectFilterParams) => {
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItemId);
const fieldMetadataItem = getFieldMetadataItemById(fieldMetadataItemId);
if (!isDefined(fieldMetadataItem)) {
return;
}
if (
fieldMetadataItem.type === 'RELATION' ||
fieldMetadataItem.type === 'SELECT'
) {
setHotkeyScope(SingleRecordPickerHotkeyScope.SingleRecordPicker);
}
const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type);
const defaultSubFieldName =
getDefaultSubFieldNameForCompositeFilterableFieldType(filterType);
const firstOperand = getRecordFilterOperands({
filterType,
subFieldName: defaultSubFieldName,
})[0];
setSelectedOperandInDropdown(firstOperand);
const { value, displayValue } = getInitialFilterValue(
filterType,
firstOperand,
);
if (value !== '') {
applyRecordFilter({
id: v4(),
fieldMetadataId: fieldMetadataItem.id,
displayValue,
operand: firstOperand,
value,
type: filterType,
label: fieldMetadataItem.label,
subFieldName: defaultSubFieldName,
});
}
setObjectFilterDropdownSearchInput('');
};
return {
selectFilterUsedInDropdown,
};
};

View File

@ -1,5 +1,4 @@
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
@ -10,10 +9,6 @@ export const useUpsertObjectFilterDropdownCurrentFilter = () => {
objectFilterDropdownCurrentRecordFilterComponentState,
);
const setSelectedFilter = useSetRecoilComponentStateV2(
selectedFilterComponentState,
);
const { upsertRecordFilter } = useUpsertRecordFilter();
const upsertObjectFilterDropdownCurrentFilter = (
@ -22,7 +17,6 @@ export const useUpsertObjectFilterDropdownCurrentFilter = () => {
upsertRecordFilter(recordFilterToUpsert);
setObjectFilterDropdownCurrentRecordFilter(recordFilterToUpsert);
setSelectedFilter(recordFilterToUpsert);
};
return {

View File

@ -1,11 +0,0 @@
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { RecordFilter } from '../../record-filter/types/RecordFilter';
export const onFilterSelectComponentState = createComponentStateV2<
((filter: RecordFilter | null) => void) | undefined
>({
key: 'onFilterSelectComponentState',
defaultValue: undefined,
componentInstanceContext: ObjectFilterDropdownComponentInstanceContext,
});

View File

@ -1,11 +0,0 @@
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { RecordFilter } from '../../record-filter/types/RecordFilter';
export const selectedFilterComponentState = createComponentStateV2<
RecordFilter | undefined | null
>({
key: 'selectedFilterComponentState',
defaultValue: undefined,
componentInstanceContext: ObjectFilterDropdownComponentInstanceContext,
});

View File

@ -1,32 +0,0 @@
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
export const useApplyRecordFilter = (componentInstanceId?: string) => {
const selectedFilterCallbackState = useRecoilComponentCallbackStateV2(
selectedFilterComponentState,
componentInstanceId,
);
const { upsertRecordFilter } = useUpsertRecordFilter();
const applyRecordFilter = useRecoilCallback(
({ set }) =>
(filter: RecordFilter | null) => {
set(selectedFilterCallbackState, filter);
if (isDefined(filter)) {
upsertRecordFilter(filter);
}
},
[selectedFilterCallbackState, upsertRecordFilter],
);
return {
applyRecordFilter,
};
};

View File

@ -1,23 +1,16 @@
import { useSelectFilterUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown';
import { useCreateEmptyRecordFilterFromFieldMetadataItem } from '@/object-record/record-filter/hooks/useCreateEmptyRecordFilterFromFieldMetadataItem';
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useOpenDropdownFromOutside } from '@/ui/layout/dropdown/hooks/useOpenDropdownFromOutside';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetEditableFilterChipDropdownStates } from '@/views/hooks/useSetEditableFilterChipDropdownStates';
import { isDefined } from 'twenty-shared/utils';
export const useOpenRecordFilterChipFromTableHeader = () => {
const { recordIndexId } = useRecordIndexContextOrThrow();
const { filterableFieldMetadataItems } =
useFilterableFieldMetadataItemsInRecordIndexContext();
const { selectFilterUsedInDropdown } =
useSelectFilterUsedInDropdown(recordIndexId);
const currentRecordFilters = useRecoilComponentValueV2(
currentRecordFiltersComponentState,
);
@ -60,8 +53,6 @@ export const useOpenRecordFilterChipFromTableHeader = () => {
upsertRecordFilter(newRecordFilter);
selectFilterUsedInDropdown({ fieldMetadataItemId });
setEditableFilterChipDropdownStates(newRecordFilter);
openDropdownFromOutside(newRecordFilter.id);
};

View File

@ -69,8 +69,10 @@ export const RelativeDatePickerHeader = (
});
}}
options={RELATIVE_DATE_DIRECTION_SELECT_OPTIONS}
fullWidth
/>
<TextInput
width={50}
value={textInputValue}
onChange={(text) => {
const amountString = text.replace(/[^0-9]|^0+/g, '');
@ -106,6 +108,7 @@ export const RelativeDatePickerHeader = (
});
}}
options={unitSelectOptions}
fullWidth
/>
</StyledContainer>
);

View File

@ -1,6 +1,7 @@
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { AdvancedFilterRootRecordFilterGroup } from '@/object-record/advanced-filter/components/AdvancedFilterRootRecordFilterGroup';
import { useSetAdvancedFilterDropdownStates } from '@/object-record/advanced-filter/hooks/useSetAdvancedFilterDropdownAllRowsStates';
import { rootLevelRecordFilterGroupComponentSelector } from '@/object-record/advanced-filter/states/rootLevelRecordFilterGroupComponentSelector';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { AdvancedFilterChip } from '@/views/components/AdvancedFilterChip';
@ -12,6 +13,13 @@ export const AdvancedFilterDropdownButton = () => {
rootLevelRecordFilterGroupComponentSelector,
);
const { setAdvancedFilterDropdownStates } =
useSetAdvancedFilterDropdownStates();
const handleOpenAdvancedFilterDropdown = () => {
setAdvancedFilterDropdownStates();
};
if (!isDefined(rootLevelRecordFilterGroup)) {
return null;
}
@ -25,6 +33,7 @@ export const AdvancedFilterDropdownButton = () => {
dropdownOffset={{ y: 8, x: 0 }}
dropdownPlacement="bottom-start"
dropdownWidth="100%"
onOpen={handleOpenAdvancedFilterDropdown}
/>
);
};

View File

@ -3,7 +3,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { VIEW_BAR_FILTER_DROPDOWN_ID } from '@/views/constants/ViewBarFilterDropdownId';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
import { isRecordFilterConsideredEmpty } from '@/object-record/record-filter/utils/isRecordFilterConsideredEmpty';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -22,17 +22,19 @@ export const ViewBarFilterDropdown = ({
const { removeRecordFilter } = useRemoveRecordFilter();
const selectedFilter = useRecoilComponentValueV2(
selectedFilterComponentState,
const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2(
objectFilterDropdownCurrentRecordFilterComponentState,
);
const handleDropdownClickOutside = () => {
const recordFilterIsEmpty =
isDefined(selectedFilter) &&
isRecordFilterConsideredEmpty(selectedFilter);
isDefined(objectFilterDropdownCurrentRecordFilter) &&
isRecordFilterConsideredEmpty(objectFilterDropdownCurrentRecordFilter);
if (recordFilterIsEmpty) {
removeRecordFilter({ recordFilterId: selectedFilter.id });
removeRecordFilter({
recordFilterId: objectFilterDropdownCurrentRecordFilter.id,
});
}
};

View File

@ -2,7 +2,6 @@ import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState';
import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
@ -42,13 +41,6 @@ export const useSetEditableFilterChipDropdownStates = () => {
recordFilter.operand,
);
set(
selectedFilterComponentState.atomFamily({
instanceId: recordFilter.id,
}),
recordFilter,
);
set(
objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({
instanceId: recordFilter.id,