Fixed sub-field filter dropdown content and icons (#12516)
This PR improves the sub-field selection UX in advanced filters. - Now using the icon of the field instead of the field type - Now using the label of the field instead of the field type - Removed now useless constant ICON_NAME_BY_ANY_SUB_FIELD - Now selects a default value (any or default sub-field for type) when clicking on the field, instead of waiting for the user to select the sub-field Fixes https://github.com/twentyhq/core-team-issues/issues/1005
This commit is contained in:
@ -106,17 +106,17 @@ export const AdvancedFilterFieldSelectMenu = ({
|
|||||||
selectedFieldMetadataItem.type,
|
selectedFieldMetadataItem.type,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
selectFieldUsedInAdvancedFilterDropdown({
|
||||||
|
fieldMetadataItemId: selectedFieldMetadataItem.id,
|
||||||
|
recordFilterId,
|
||||||
|
});
|
||||||
|
|
||||||
if (isCompositeFieldType(filterType)) {
|
if (isCompositeFieldType(filterType)) {
|
||||||
setObjectFilterDropdownSubMenuFieldType(filterType);
|
setObjectFilterDropdownSubMenuFieldType(filterType);
|
||||||
|
|
||||||
setFieldMetadataItemIdUsedInDropdown(selectedFieldMetadataItem.id);
|
setFieldMetadataItemIdUsedInDropdown(selectedFieldMetadataItem.id);
|
||||||
setObjectFilterDropdownIsSelectingCompositeField(true);
|
setObjectFilterDropdownIsSelectingCompositeField(true);
|
||||||
} else {
|
} else {
|
||||||
selectFieldUsedInAdvancedFilterDropdown({
|
|
||||||
fieldMetadataItemId: selectedFieldMetadataItem.id,
|
|
||||||
recordFilterId,
|
|
||||||
});
|
|
||||||
|
|
||||||
closeAdvancedFilterFieldSelectDropdown();
|
closeAdvancedFilterFieldSelectDropdown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,12 +10,9 @@ import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-recor
|
|||||||
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
||||||
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
|
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
|
||||||
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
|
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
|
||||||
import { getFilterableFieldTypeLabel } from '@/object-record/object-filter-dropdown/utils/getFilterableFieldTypeLabel';
|
|
||||||
import { DEFAULT_ANY_SUB_FIELD_ICON_NAME } from '@/object-record/record-filter/constants/DefaultAnySubFieldIconName';
|
|
||||||
import { ICON_NAME_BY_ANY_SUB_FIELD } from '@/object-record/record-filter/constants/IconNameByAnySubField';
|
|
||||||
import { ICON_NAME_BY_SUB_FIELD } from '@/object-record/record-filter/constants/IconNameBySubField';
|
import { ICON_NAME_BY_SUB_FIELD } from '@/object-record/record-filter/constants/IconNameBySubField';
|
||||||
import { areCompositeTypeSubFieldsFilterable } from '@/object-record/record-filter/utils/areCompositeTypeSubFieldsFilterable';
|
import { areCompositeTypeSubFieldsFilterable } from '@/object-record/record-filter/utils/areCompositeTypeSubFieldsFilterable';
|
||||||
import { isCompositeTypeFilterableByAnySubField } from '@/object-record/record-filter/utils/isCompositeTypeFilterableByAnySubField';
|
import { isCompositeTypeNonFilterableByAnySubField } from '@/object-record/record-filter/utils/isCompositeTypeNonFilterableByAnySubField';
|
||||||
import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs';
|
import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs';
|
||||||
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
@ -103,7 +100,7 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
|||||||
|
|
||||||
const compositeFieldTypeIsFilterableByAnySubField =
|
const compositeFieldTypeIsFilterableByAnySubField =
|
||||||
isDefined(fieldMetadataItemUsedInDropdown) &&
|
isDefined(fieldMetadataItemUsedInDropdown) &&
|
||||||
isCompositeTypeFilterableByAnySubField(
|
!isCompositeTypeNonFilterableByAnySubField(
|
||||||
fieldMetadataItemUsedInDropdown.type,
|
fieldMetadataItemUsedInDropdown.type,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -112,16 +109,6 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
|||||||
...subFieldNames.map((subFieldName) => subFieldName),
|
...subFieldNames.map((subFieldName) => subFieldName),
|
||||||
];
|
];
|
||||||
|
|
||||||
const iconNameForAnySubField = isDefined(fieldMetadataItemUsedInDropdown)
|
|
||||||
? ICON_NAME_BY_ANY_SUB_FIELD[fieldMetadataItemUsedInDropdown?.type]
|
|
||||||
: (null ?? null);
|
|
||||||
|
|
||||||
const anySubFieldIcon = getIcon(
|
|
||||||
iconNameForAnySubField ??
|
|
||||||
fieldMetadataItemUsedInDropdown?.icon ??
|
|
||||||
DEFAULT_ANY_SUB_FIELD_ICON_NAME,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownContent>
|
<DropdownContent>
|
||||||
<DropdownMenuHeader
|
<DropdownMenuHeader
|
||||||
@ -132,7 +119,7 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)}
|
{fieldMetadataItemUsedInDropdown?.label}
|
||||||
</DropdownMenuHeader>
|
</DropdownMenuHeader>
|
||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
@ -155,7 +142,7 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleSelectFilter(fieldMetadataItemUsedInDropdown);
|
handleSelectFilter(fieldMetadataItemUsedInDropdown);
|
||||||
}}
|
}}
|
||||||
LeftIcon={anySubFieldIcon}
|
LeftIcon={getIcon(fieldMetadataItemUsedInDropdown.icon)}
|
||||||
text={`Any ${fieldMetadataItemUsedInDropdown.label} field`}
|
text={`Any ${fieldMetadataItemUsedInDropdown.label} field`}
|
||||||
/>
|
/>
|
||||||
</SelectableListItem>
|
</SelectableListItem>
|
||||||
|
|||||||
@ -6,10 +6,13 @@ import { objectFilterDropdownSearchInputComponentState } from '@/object-record/o
|
|||||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||||
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
|
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
|
||||||
|
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
|
||||||
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
|
import { getDefaultSubFieldNameForCompositeFilterableFieldType } from '@/object-record/record-filter/utils/getDefaultSubFieldNameForCompositeFilterableFieldType';
|
||||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||||
|
import { isCompositeTypeNonFilterableByAnySubField } from '@/object-record/record-filter/utils/isCompositeTypeNonFilterableByAnySubField';
|
||||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
||||||
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
||||||
|
|
||||||
@ -81,7 +84,13 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
|||||||
const firstOperand = getRecordFilterOperands({
|
const firstOperand = getRecordFilterOperands({
|
||||||
filterType,
|
filterType,
|
||||||
subFieldName,
|
subFieldName,
|
||||||
})[0];
|
})?.[0];
|
||||||
|
|
||||||
|
if (!isDefined(firstOperand)) {
|
||||||
|
throw new Error(
|
||||||
|
`No valid operand found for filter type: ${filterType} and subFieldName: ${subFieldName}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
setSelectedOperandInDropdown(firstOperand);
|
setSelectedOperandInDropdown(firstOperand);
|
||||||
|
|
||||||
@ -94,6 +103,27 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
|||||||
(recordFilter) => recordFilter.id === recordFilterId,
|
(recordFilter) => recordFilter.id === recordFilterId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isCompositeFilterOnAnySubField =
|
||||||
|
isCompositeFieldType(filterType) && !isDefined(subFieldName);
|
||||||
|
const compositeFilterNonFilterableByAnySubField =
|
||||||
|
isCompositeTypeNonFilterableByAnySubField(filterType);
|
||||||
|
|
||||||
|
let subFieldNameForNonFilterableWithAny:
|
||||||
|
| CompositeFieldSubFieldName
|
||||||
|
| undefined
|
||||||
|
| null = subFieldName;
|
||||||
|
|
||||||
|
if (
|
||||||
|
isCompositeFilterOnAnySubField &&
|
||||||
|
compositeFilterNonFilterableByAnySubField
|
||||||
|
) {
|
||||||
|
subFieldNameForNonFilterableWithAny =
|
||||||
|
getDefaultSubFieldNameForCompositeFilterableFieldType(filterType);
|
||||||
|
}
|
||||||
|
|
||||||
|
const subFieldNameToUse =
|
||||||
|
subFieldName ?? subFieldNameForNonFilterableWithAny;
|
||||||
|
|
||||||
const newAdvancedFilter = {
|
const newAdvancedFilter = {
|
||||||
id: recordFilterId,
|
id: recordFilterId,
|
||||||
fieldMetadataId: fieldMetadataItem.id,
|
fieldMetadataId: fieldMetadataItem.id,
|
||||||
@ -105,10 +135,10 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
|||||||
existingRecordFilter?.positionInRecordFilterGroup,
|
existingRecordFilter?.positionInRecordFilterGroup,
|
||||||
type: filterType,
|
type: filterType,
|
||||||
label: fieldMetadataItem.label,
|
label: fieldMetadataItem.label,
|
||||||
subFieldName,
|
subFieldName: subFieldNameToUse,
|
||||||
} satisfies RecordFilter;
|
} satisfies RecordFilter;
|
||||||
|
|
||||||
setSubFieldNameUsedInDropdown(subFieldName);
|
setSubFieldNameUsedInDropdown(subFieldNameToUse);
|
||||||
|
|
||||||
setObjectFilterDropdownSearchInput('');
|
setObjectFilterDropdownSearchInput('');
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
|
||||||
|
|
||||||
export const ICON_NAME_BY_ANY_SUB_FIELD: Partial<
|
|
||||||
Record<FieldMetadataType, string>
|
|
||||||
> = {
|
|
||||||
[FieldMetadataType.LINKS]: 'IconLink',
|
|
||||||
[FieldMetadataType.EMAILS]: 'IconMail',
|
|
||||||
[FieldMetadataType.PHONES]: 'IconPhone',
|
|
||||||
[FieldMetadataType.ADDRESS]: 'IconMap',
|
|
||||||
[FieldMetadataType.ACTOR]: 'IconCreativeCommonsSa',
|
|
||||||
[FieldMetadataType.FULL_NAME]: 'IconUser',
|
|
||||||
[FieldMetadataType.POSITION]: 'IconBriefcase',
|
|
||||||
};
|
|
||||||
@ -8,8 +8,8 @@ const COMPOSITE_TYPES_NON_FILTERABLE_WITH_ANY = [
|
|||||||
type CompositeTypeNonFilterableWithAny =
|
type CompositeTypeNonFilterableWithAny =
|
||||||
(typeof COMPOSITE_TYPES_NON_FILTERABLE_WITH_ANY)[number];
|
(typeof COMPOSITE_TYPES_NON_FILTERABLE_WITH_ANY)[number];
|
||||||
|
|
||||||
export const isCompositeTypeFilterableByAnySubField = (
|
export const isCompositeTypeNonFilterableByAnySubField = (
|
||||||
fieldType: FieldType,
|
fieldType: FieldType,
|
||||||
): fieldType is CompositeTypeNonFilterableWithAny => {
|
): fieldType is CompositeTypeNonFilterableWithAny => {
|
||||||
return !COMPOSITE_TYPES_NON_FILTERABLE_WITH_ANY.includes(fieldType as any);
|
return COMPOSITE_TYPES_NON_FILTERABLE_WITH_ANY.includes(fieldType as any);
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user