Fix bug and refactored advanced filter field selection dropdown (#11103)
This PR is a first step towards isolating each filter dropdown use case, here we isolate advanced filter field selection dropdown from view bar filter field selection dropdown. ## Isolation of advanced filter field selection logic We reimplement the previously generic logic into AdvancedFilterFieldSelectMenu and AdvancedFilterSubFieldSelectMenu components. For now it contains duplicated code but, the end goal is to factorize what's common to all object filter dropdowns in small hooks where possible, at the end of the code path isolation first step, which will be done for applyFilter and selectFilter logic that will be able to be deleted after code path isolation. A new component ObjectFilterDropdownFilterSelectMenuItemV2 has been created to expose an onClick method instead of computing logic that tries to guess where it is located, which allows to verticalize what happens when we select a field in each specific use case, one layer above. Created the hook useSelectFieldUsedInAdvancedFilterDropdown which contains the logic for field selection for advanced filter field select dropdown specific use case, the first example of a good verticalized and unique place to handle field selection in the context of advanced filter. The naming useAdvancedFilterDropdown was lying and is now useAdvancedFilterFieldSelectDropdown which is now self-explanatory. useAdvancedFilterFieldSelectDropdown has been removed from the main object filter dropdown, thus isolating advanced filters field select dropdown from the generic object filter field select dropdown. ## Miscellaneous Removed states that were used in the generic filter dropdown to guess whether it was in advanced filter context or not.
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
import { AdvancedFilterFieldSelectDrodownContent } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectDrodownContent';
|
import { AdvancedFilterFieldSelectDropdownContent } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownContent';
|
||||||
import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown';
|
import { useAdvancedFilterFieldSelectDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown';
|
||||||
|
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { SelectControl } from '@/ui/input/components/SelectControl';
|
import { SelectControl } from '@/ui/input/components/SelectControl';
|
||||||
@ -11,15 +11,15 @@ const StyledContainer = styled.div`
|
|||||||
flex: 2;
|
flex: 2;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type AdvancedFilterFieldSelectDrodownButtonProps = {
|
type AdvancedFilterFieldSelectDropdownButtonProps = {
|
||||||
recordFilterId: string;
|
recordFilterId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AdvancedFilterFieldSelectDrodownButton = ({
|
export const AdvancedFilterFieldSelectDropdownButton = ({
|
||||||
recordFilterId,
|
recordFilterId,
|
||||||
}: AdvancedFilterFieldSelectDrodownButtonProps) => {
|
}: AdvancedFilterFieldSelectDropdownButtonProps) => {
|
||||||
const { advancedFilterDropdownId } =
|
const { advancedFilterFieldSelectDropdownId } =
|
||||||
useAdvancedFilterDropdown(recordFilterId);
|
useAdvancedFilterFieldSelectDropdown(recordFilterId);
|
||||||
|
|
||||||
const currentRecordFilters = useRecoilComponentValueV2(
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
currentRecordFiltersComponentState,
|
currentRecordFiltersComponentState,
|
||||||
@ -34,7 +34,7 @@ export const AdvancedFilterFieldSelectDrodownButton = ({
|
|||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={advancedFilterDropdownId}
|
dropdownId={advancedFilterFieldSelectDropdownId}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<SelectControl
|
<SelectControl
|
||||||
selectedOption={{
|
selectedOption={{
|
||||||
@ -43,8 +43,12 @@ export const AdvancedFilterFieldSelectDrodownButton = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
dropdownComponents={<AdvancedFilterFieldSelectDrodownContent />}
|
dropdownComponents={
|
||||||
dropdownHotkeyScope={{ scope: advancedFilterDropdownId }}
|
<AdvancedFilterFieldSelectDropdownContent
|
||||||
|
recordFilterId={recordFilterId}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
dropdownHotkeyScope={{ scope: advancedFilterFieldSelectDropdownId }}
|
||||||
dropdownOffset={{ y: 8, x: 0 }}
|
dropdownOffset={{ y: 8, x: 0 }}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
/>
|
/>
|
||||||
@ -1,9 +1,15 @@
|
|||||||
import { ObjectFilterDropdownFilterSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect';
|
import { AdvancedFilterFieldSelectMenu } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu';
|
||||||
import { ObjectFilterDropdownFilterSelectCompositeFieldSubMenu } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu';
|
import { AdvancedFilterSubFieldSelectMenu } from '@/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu';
|
||||||
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
||||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||||
|
|
||||||
export const AdvancedFilterFieldSelectDrodownContent = () => {
|
type AdvancedFilterFieldSelectDropdownContentProps = {
|
||||||
|
recordFilterId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AdvancedFilterFieldSelectDropdownContent = ({
|
||||||
|
recordFilterId,
|
||||||
|
}: AdvancedFilterFieldSelectDropdownContentProps) => {
|
||||||
const [objectFilterDropdownIsSelectingCompositeField] =
|
const [objectFilterDropdownIsSelectingCompositeField] =
|
||||||
useRecoilComponentStateV2(
|
useRecoilComponentStateV2(
|
||||||
objectFilterDropdownIsSelectingCompositeFieldComponentState,
|
objectFilterDropdownIsSelectingCompositeFieldComponentState,
|
||||||
@ -13,8 +19,8 @@ export const AdvancedFilterFieldSelectDrodownContent = () => {
|
|||||||
objectFilterDropdownIsSelectingCompositeField;
|
objectFilterDropdownIsSelectingCompositeField;
|
||||||
|
|
||||||
return shouldShowCompositeSelectionSubMenu ? (
|
return shouldShowCompositeSelectionSubMenu ? (
|
||||||
<ObjectFilterDropdownFilterSelectCompositeFieldSubMenu />
|
<AdvancedFilterSubFieldSelectMenu recordFilterId={recordFilterId} />
|
||||||
) : (
|
) : (
|
||||||
<ObjectFilterDropdownFilterSelect />
|
<AdvancedFilterFieldSelectMenu recordFilterId={recordFilterId} />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -0,0 +1,182 @@
|
|||||||
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
|
||||||
|
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
|
||||||
|
|
||||||
|
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||||
|
|
||||||
|
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||||
|
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||||
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
|
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
|
||||||
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
|
import { AdvancedFilterFieldSelectSearchInput } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectSearchInput';
|
||||||
|
import { useAdvancedFilterFieldSelectDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown';
|
||||||
|
import { useSelectFieldUsedInAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useSelectFieldUsedInAdvancedFilterDropdown';
|
||||||
|
import { ObjectFilterDropdownFilterSelectMenuItemV2 } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItemV2';
|
||||||
|
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||||
|
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
||||||
|
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
|
||||||
|
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
||||||
|
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
|
||||||
|
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
|
||||||
|
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||||
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
|
|
||||||
|
type AdvancedFilterFieldSelectMenuProps = {
|
||||||
|
recordFilterId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AdvancedFilterFieldSelectMenu = ({
|
||||||
|
recordFilterId,
|
||||||
|
}: AdvancedFilterFieldSelectMenuProps) => {
|
||||||
|
const { recordIndexId } = useRecordIndexContextOrThrow();
|
||||||
|
|
||||||
|
const { closeAdvancedFilterFieldSelectDropdown } =
|
||||||
|
useAdvancedFilterFieldSelectDropdown(recordFilterId);
|
||||||
|
|
||||||
|
const [objectFilterDropdownSearchInput] = useRecoilComponentStateV2(
|
||||||
|
objectFilterDropdownSearchInputComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { filterableFieldMetadataItems } =
|
||||||
|
useFilterableFieldMetadataItemsInRecordIndexContext();
|
||||||
|
|
||||||
|
const visibleTableColumns = useRecoilComponentValueV2(
|
||||||
|
visibleTableColumnsComponentSelector,
|
||||||
|
recordIndexId,
|
||||||
|
);
|
||||||
|
const visibleColumnsIds = visibleTableColumns.map(
|
||||||
|
(column) => column.fieldMetadataId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const filteredSearchInputFieldMetadataItems =
|
||||||
|
filterableFieldMetadataItems.filter((fieldMetadataItem) =>
|
||||||
|
fieldMetadataItem.label
|
||||||
|
.toLocaleLowerCase()
|
||||||
|
.includes(objectFilterDropdownSearchInput.toLocaleLowerCase()),
|
||||||
|
);
|
||||||
|
|
||||||
|
const visibleColumnsFieldMetadataItems = filteredSearchInputFieldMetadataItems
|
||||||
|
.sort((a, b) => {
|
||||||
|
return visibleColumnsIds.indexOf(a.id) - visibleColumnsIds.indexOf(b.id);
|
||||||
|
})
|
||||||
|
.filter((fieldMetadataItem) =>
|
||||||
|
visibleColumnsIds.includes(fieldMetadataItem.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
const hiddenColumnsFieldMetadataItems = filteredSearchInputFieldMetadataItems
|
||||||
|
.sort((a, b) => a.label.localeCompare(b.label))
|
||||||
|
.filter(
|
||||||
|
(fieldMetadataItem) => !visibleColumnsIds.includes(fieldMetadataItem.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectableFieldMetadataItemIds = filterableFieldMetadataItems.map(
|
||||||
|
(fieldMetadataItem) => fieldMetadataItem.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { resetSelectedItem } = useSelectableList(OBJECT_FILTER_DROPDOWN_ID);
|
||||||
|
|
||||||
|
const { selectFieldUsedInAdvancedFilterDropdown } =
|
||||||
|
useSelectFieldUsedInAdvancedFilterDropdown();
|
||||||
|
|
||||||
|
const [, setObjectFilterDropdownSubMenuFieldType] = useRecoilComponentStateV2(
|
||||||
|
objectFilterDropdownSubMenuFieldTypeComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [, setObjectFilterDropdownIsSelectingCompositeField] =
|
||||||
|
useRecoilComponentStateV2(
|
||||||
|
objectFilterDropdownIsSelectingCompositeFieldComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
|
||||||
|
fieldMetadataItemIdUsedInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleEnter = (fieldMetadataItemId: string) => {
|
||||||
|
const selectedFieldMetadataItem = filterableFieldMetadataItems.find(
|
||||||
|
(fieldMetadataItem) => fieldMetadataItem.id === fieldMetadataItemId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isDefined(selectedFieldMetadataItem)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFieldMetadataItemSelect(selectedFieldMetadataItem);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFieldMetadataItemSelect = (
|
||||||
|
selectedFieldMetadataItem: FieldMetadataItem,
|
||||||
|
) => {
|
||||||
|
resetSelectedItem();
|
||||||
|
|
||||||
|
const filterType = getFilterTypeFromFieldType(
|
||||||
|
selectedFieldMetadataItem.type,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isCompositeField(filterType)) {
|
||||||
|
setObjectFilterDropdownSubMenuFieldType(filterType);
|
||||||
|
|
||||||
|
setFieldMetadataItemIdUsedInDropdown(selectedFieldMetadataItem.id);
|
||||||
|
setObjectFilterDropdownIsSelectingCompositeField(true);
|
||||||
|
} else {
|
||||||
|
selectFieldUsedInAdvancedFilterDropdown({
|
||||||
|
fieldMetadataItemId: selectedFieldMetadataItem.id,
|
||||||
|
recordFilterId,
|
||||||
|
});
|
||||||
|
|
||||||
|
closeAdvancedFilterFieldSelectDropdown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const shouldShowSeparator =
|
||||||
|
visibleColumnsFieldMetadataItems.length > 0 &&
|
||||||
|
hiddenColumnsFieldMetadataItems.length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AdvancedFilterFieldSelectSearchInput />
|
||||||
|
<SelectableList
|
||||||
|
hotkeyScope={FiltersHotkeyScope.ObjectFilterDropdownButton}
|
||||||
|
selectableItemIdArray={selectableFieldMetadataItemIds}
|
||||||
|
selectableListId={OBJECT_FILTER_DROPDOWN_ID}
|
||||||
|
onEnter={handleEnter}
|
||||||
|
>
|
||||||
|
<DropdownMenuItemsContainer>
|
||||||
|
{visibleColumnsFieldMetadataItems.map(
|
||||||
|
(visibleFieldMetadataItem, index) => (
|
||||||
|
<SelectableItem
|
||||||
|
itemId={visibleFieldMetadataItem.id}
|
||||||
|
key={`visible-select-filter-${index}`}
|
||||||
|
>
|
||||||
|
<ObjectFilterDropdownFilterSelectMenuItemV2
|
||||||
|
fieldMetadataItemToSelect={visibleFieldMetadataItem}
|
||||||
|
onClick={handleFieldMetadataItemSelect}
|
||||||
|
/>
|
||||||
|
</SelectableItem>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
{shouldShowSeparator && <DropdownMenuSeparator />}
|
||||||
|
{hiddenColumnsFieldMetadataItems.map(
|
||||||
|
(hiddenFieldMetadataItem, index) => (
|
||||||
|
<SelectableItem
|
||||||
|
itemId={hiddenFieldMetadataItem.id}
|
||||||
|
key={`hidden-select-filter-${index}`}
|
||||||
|
>
|
||||||
|
<ObjectFilterDropdownFilterSelectMenuItemV2
|
||||||
|
fieldMetadataItemToSelect={hiddenFieldMetadataItem}
|
||||||
|
onClick={handleFieldMetadataItemSelect}
|
||||||
|
/>
|
||||||
|
</SelectableItem>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</DropdownMenuItemsContainer>
|
||||||
|
</SelectableList>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { AdvancedFilterDropdownRow } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownRow';
|
import { AdvancedFilterDropdownRow } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownRow';
|
||||||
import { AdvancedFilterFieldSelectDrodownButton } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectDrodownButton';
|
import { AdvancedFilterFieldSelectDropdownButton } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownButton';
|
||||||
import { AdvancedFilterLogicalOperatorCell } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorCell';
|
import { AdvancedFilterLogicalOperatorCell } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorCell';
|
||||||
import { AdvancedFilterRecordFilterOperandSelect } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect';
|
import { AdvancedFilterRecordFilterOperandSelect } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect';
|
||||||
import { AdvancedFilterRecordFilterOptionsDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOptionsDropdown';
|
import { AdvancedFilterRecordFilterOptionsDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOptionsDropdown';
|
||||||
@ -26,7 +26,7 @@ export const AdvancedFilterRecordFilterRow = ({
|
|||||||
index={recordFilterIndex}
|
index={recordFilterIndex}
|
||||||
recordFilterGroup={recordFilterGroup}
|
recordFilterGroup={recordFilterGroup}
|
||||||
/>
|
/>
|
||||||
<AdvancedFilterFieldSelectDrodownButton
|
<AdvancedFilterFieldSelectDropdownButton
|
||||||
recordFilterId={recordFilter.id}
|
recordFilterId={recordFilter.id}
|
||||||
/>
|
/>
|
||||||
<AdvancedFilterRecordFilterOperandSelect
|
<AdvancedFilterRecordFilterOperandSelect
|
||||||
|
|||||||
@ -0,0 +1,140 @@
|
|||||||
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
|
import { useAdvancedFilterFieldSelectDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown';
|
||||||
|
import { useSelectFieldUsedInAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useSelectFieldUsedInAdvancedFilterDropdown';
|
||||||
|
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||||
|
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||||
|
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
||||||
|
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
||||||
|
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
|
||||||
|
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
|
||||||
|
import { getFilterableFieldTypeLabel } from '@/object-record/object-filter-dropdown/utils/getFilterableFieldTypeLabel';
|
||||||
|
import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs';
|
||||||
|
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||||
|
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||||
|
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||||
|
import { IconApps, IconChevronLeft, MenuItem, useIcons } from 'twenty-ui';
|
||||||
|
|
||||||
|
type AdvancedFilterSubFieldSelectMenuProps = {
|
||||||
|
recordFilterId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AdvancedFilterSubFieldSelectMenu = ({
|
||||||
|
recordFilterId,
|
||||||
|
}: AdvancedFilterSubFieldSelectMenuProps) => {
|
||||||
|
const { getIcon } = useIcons();
|
||||||
|
|
||||||
|
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
||||||
|
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [, setObjectFilterDropdownFilterIsSelected] = useRecoilComponentStateV2(
|
||||||
|
objectFilterDropdownFilterIsSelectedComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [, setObjectFilterDropdownIsSelectingCompositeField] =
|
||||||
|
useRecoilComponentStateV2(
|
||||||
|
objectFilterDropdownIsSelectingCompositeFieldComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [
|
||||||
|
objectFilterDropdownSubMenuFieldType,
|
||||||
|
setObjectFilterDropdownSubMenuFieldType,
|
||||||
|
] = useRecoilComponentStateV2(
|
||||||
|
objectFilterDropdownSubMenuFieldTypeComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
|
||||||
|
fieldMetadataItemIdUsedInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { closeAdvancedFilterFieldSelectDropdown } =
|
||||||
|
useAdvancedFilterFieldSelectDropdown(recordFilterId);
|
||||||
|
|
||||||
|
const { selectFieldUsedInAdvancedFilterDropdown } =
|
||||||
|
useSelectFieldUsedInAdvancedFilterDropdown();
|
||||||
|
|
||||||
|
const handleSelectFilter = (
|
||||||
|
selectedFieldMetadataItem: FieldMetadataItem | null | undefined,
|
||||||
|
subFieldName?: string | null | undefined,
|
||||||
|
) => {
|
||||||
|
if (!isDefined(selectedFieldMetadataItem)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectFieldUsedInAdvancedFilterDropdown({
|
||||||
|
fieldMetadataItemId: selectedFieldMetadataItem.id,
|
||||||
|
recordFilterId,
|
||||||
|
subFieldName,
|
||||||
|
});
|
||||||
|
|
||||||
|
closeAdvancedFilterFieldSelectDropdown();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubMenuBack = () => {
|
||||||
|
setFieldMetadataItemIdUsedInDropdown(null);
|
||||||
|
setObjectFilterDropdownSubMenuFieldType(null);
|
||||||
|
setObjectFilterDropdownIsSelectingCompositeField(false);
|
||||||
|
setObjectFilterDropdownFilterIsSelected(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isDefined(objectFilterDropdownSubMenuFieldType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS[
|
||||||
|
objectFilterDropdownSubMenuFieldType
|
||||||
|
].filterableSubFields.sort((a, b) => a.localeCompare(b));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DropdownMenuHeader
|
||||||
|
StartComponent={
|
||||||
|
<DropdownMenuHeaderLeftComponent
|
||||||
|
onClick={handleSubMenuBack}
|
||||||
|
Icon={IconChevronLeft}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)}
|
||||||
|
</DropdownMenuHeader>
|
||||||
|
<DropdownMenuItemsContainer>
|
||||||
|
<MenuItem
|
||||||
|
key={`select-filter-${-1}`}
|
||||||
|
testId={`select-filter-${-1}`}
|
||||||
|
onClick={() => {
|
||||||
|
handleSelectFilter(fieldMetadataItemUsedInDropdown);
|
||||||
|
}}
|
||||||
|
LeftIcon={IconApps}
|
||||||
|
text={`Any ${getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} field`}
|
||||||
|
/>
|
||||||
|
{/* TODO: fix this with a backend field on ViewFilter for composite field filter */}
|
||||||
|
{fieldMetadataItemUsedInDropdown?.type === 'ACTOR' &&
|
||||||
|
options.map((subFieldName, index) => (
|
||||||
|
<MenuItem
|
||||||
|
key={`select-filter-${index}`}
|
||||||
|
testId={`select-filter-${index}`}
|
||||||
|
onClick={() => {
|
||||||
|
if (isDefined(fieldMetadataItemUsedInDropdown)) {
|
||||||
|
handleSelectFilter(
|
||||||
|
fieldMetadataItemUsedInDropdown,
|
||||||
|
subFieldName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
text={getCompositeSubFieldLabel(
|
||||||
|
objectFilterDropdownSubMenuFieldType,
|
||||||
|
subFieldName,
|
||||||
|
)}
|
||||||
|
LeftIcon={getIcon(fieldMetadataItemUsedInDropdown?.icon)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</DropdownMenuItemsContainer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,84 +0,0 @@
|
|||||||
import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown';
|
|
||||||
|
|
||||||
import { ObjectFilterDropdownFilterSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect';
|
|
||||||
import { ObjectFilterDropdownFilterSelectCompositeFieldSubMenu } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu';
|
|
||||||
import { advancedFilterViewFilterGroupIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState';
|
|
||||||
import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState';
|
|
||||||
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
|
||||||
import { SelectControl } from '@/ui/input/components/SelectControl';
|
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
|
||||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
|
||||||
flex: 2;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type AdvancedFilterViewFilterFieldSelectProps = {
|
|
||||||
viewFilterId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AdvancedFilterViewFilterFieldSelect = ({
|
|
||||||
viewFilterId,
|
|
||||||
}: AdvancedFilterViewFilterFieldSelectProps) => {
|
|
||||||
const { advancedFilterDropdownId } = useAdvancedFilterDropdown(viewFilterId);
|
|
||||||
|
|
||||||
const currentRecordFilters = useRecoilComponentValueV2(
|
|
||||||
currentRecordFiltersComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const recordFilter = currentRecordFilters.find(
|
|
||||||
(recordFilter) => recordFilter.id === viewFilterId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const selectedFieldLabel = recordFilter?.label ?? '';
|
|
||||||
|
|
||||||
const setAdvancedFilterViewFilterId = useSetRecoilComponentStateV2(
|
|
||||||
advancedFilterViewFilterIdComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const setAdvancedFilterViewFilterGroupId = useSetRecoilComponentStateV2(
|
|
||||||
advancedFilterViewFilterGroupIdComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [objectFilterDropdownIsSelectingCompositeField] =
|
|
||||||
useRecoilComponentStateV2(
|
|
||||||
objectFilterDropdownIsSelectingCompositeFieldComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const shouldShowCompositeSelectionSubMenu =
|
|
||||||
objectFilterDropdownIsSelectingCompositeField;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledContainer>
|
|
||||||
<Dropdown
|
|
||||||
dropdownId={advancedFilterDropdownId}
|
|
||||||
clickableComponent={
|
|
||||||
<SelectControl
|
|
||||||
selectedOption={{
|
|
||||||
label: selectedFieldLabel,
|
|
||||||
value: null,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onOpen={() => {
|
|
||||||
setAdvancedFilterViewFilterId(recordFilter?.id);
|
|
||||||
setAdvancedFilterViewFilterGroupId(recordFilter?.recordFilterGroupId);
|
|
||||||
}}
|
|
||||||
dropdownComponents={
|
|
||||||
shouldShowCompositeSelectionSubMenu ? (
|
|
||||||
<ObjectFilterDropdownFilterSelectCompositeFieldSubMenu />
|
|
||||||
) : (
|
|
||||||
<ObjectFilterDropdownFilterSelect />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
dropdownHotkeyScope={{ scope: advancedFilterDropdownId }}
|
|
||||||
dropdownOffset={{ y: 8, x: 0 }}
|
|
||||||
dropdownPlacement="bottom-start"
|
|
||||||
/>
|
|
||||||
</StyledContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
|
||||||
|
|
||||||
export const useAdvancedFilterDropdown = (viewFilterId?: string) => {
|
|
||||||
const advancedFilterDropdownId = `advanced-filter-view-filter-field-${viewFilterId}`;
|
|
||||||
|
|
||||||
const { closeDropdown: closeAdvancedFilterDropdown } = useDropdown(
|
|
||||||
advancedFilterDropdownId,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
closeAdvancedFilterDropdown,
|
|
||||||
advancedFilterDropdownId,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||||
|
|
||||||
|
export const useAdvancedFilterFieldSelectDropdown = (viewFilterId?: string) => {
|
||||||
|
const advancedFilterFieldSelectDropdownId = `advanced-filter-view-filter-field-${viewFilterId}`;
|
||||||
|
|
||||||
|
const { closeDropdown: closeAdvancedFilterFieldSelectDropdown } = useDropdown(
|
||||||
|
advancedFilterFieldSelectDropdownId,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
closeAdvancedFilterFieldSelectDropdown,
|
||||||
|
advancedFilterFieldSelectDropdownId,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
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 { 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 { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
|
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||||
|
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
||||||
|
|
||||||
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
|
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';
|
||||||
|
|
||||||
|
type SelectFilterParams = {
|
||||||
|
fieldMetadataItemId: string;
|
||||||
|
recordFilterId: string;
|
||||||
|
subFieldName?: string | null | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
||||||
|
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
|
||||||
|
selectedOperandInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
|
||||||
|
fieldMetadataItemIdUsedInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setObjectFilterDropdownSearchInput = useSetRecoilComponentStateV2(
|
||||||
|
objectFilterDropdownSearchInputComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
|
||||||
|
const { applyRecordFilter } = useApplyRecordFilter();
|
||||||
|
|
||||||
|
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
|
||||||
|
|
||||||
|
const setSubFieldNameUsedInDropdown = useSetRecoilComponentStateV2(
|
||||||
|
subFieldNameUsedInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectFieldUsedInAdvancedFilterDropdown = ({
|
||||||
|
fieldMetadataItemId,
|
||||||
|
recordFilterId,
|
||||||
|
subFieldName,
|
||||||
|
}: 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 firstOperand = getRecordFilterOperands({
|
||||||
|
filterType,
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
setSelectedOperandInDropdown(firstOperand);
|
||||||
|
|
||||||
|
const { value, displayValue } = getInitialFilterValue(
|
||||||
|
filterType,
|
||||||
|
firstOperand,
|
||||||
|
);
|
||||||
|
|
||||||
|
const existingRecordFilter = currentRecordFilters.find(
|
||||||
|
(recordFilter) => recordFilter.id === recordFilterId,
|
||||||
|
);
|
||||||
|
|
||||||
|
applyRecordFilter({
|
||||||
|
id: recordFilterId,
|
||||||
|
fieldMetadataId: fieldMetadataItem.id,
|
||||||
|
displayValue,
|
||||||
|
operand: firstOperand,
|
||||||
|
value,
|
||||||
|
recordFilterGroupId: existingRecordFilter?.recordFilterGroupId,
|
||||||
|
type: filterType,
|
||||||
|
label: fieldMetadataItem.label,
|
||||||
|
subFieldName,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isDefined(subFieldName)) {
|
||||||
|
setSubFieldNameUsedInDropdown(subFieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
setObjectFilterDropdownSearchInput('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectFieldUsedInAdvancedFilterDropdown,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -27,11 +27,11 @@ export const MultipleFiltersDropdownContent = ({
|
|||||||
const shouldShowCompositeSelectionSubMenu =
|
const shouldShowCompositeSelectionSubMenu =
|
||||||
objectFilterDropdownIsSelectingCompositeField;
|
objectFilterDropdownIsSelectingCompositeField;
|
||||||
|
|
||||||
const shoudShowFilterInput = objectFilterDropdownFilterIsSelected;
|
const shouldShowFilterInput = objectFilterDropdownFilterIsSelected;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{shoudShowFilterInput ? (
|
{shouldShowFilterInput ? (
|
||||||
<ObjectFilterOperandSelectAndInput
|
<ObjectFilterOperandSelectAndInput
|
||||||
filterDropdownId={filterDropdownId}
|
filterDropdownId={filterDropdownId}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import styled from '@emotion/styled';
|
|||||||
|
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
|
||||||
import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown';
|
|
||||||
import { AdvancedFilterButton } from '@/object-record/object-filter-dropdown/components/AdvancedFilterButton';
|
import { AdvancedFilterButton } from '@/object-record/object-filter-dropdown/components/AdvancedFilterButton';
|
||||||
import { ObjectFilterDropdownFilterSelectMenuItem } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem';
|
import { ObjectFilterDropdownFilterSelectMenuItem } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem';
|
||||||
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
|
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
|
||||||
@ -21,7 +20,6 @@ import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
|||||||
import { FeatureFlagKey } from '~/generated/graphql';
|
import { FeatureFlagKey } from '~/generated/graphql';
|
||||||
|
|
||||||
import { useSelectFilterUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown';
|
import { useSelectFilterUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown';
|
||||||
import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState';
|
|
||||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||||
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
||||||
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
|
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
|
||||||
@ -65,17 +63,9 @@ export const ObjectFilterDropdownFilterSelect = ({
|
|||||||
}: ObjectFilterDropdownFilterSelectProps) => {
|
}: ObjectFilterDropdownFilterSelectProps) => {
|
||||||
const { recordIndexId } = useRecordIndexContextOrThrow();
|
const { recordIndexId } = useRecordIndexContextOrThrow();
|
||||||
|
|
||||||
const advancedFilterViewFilterId = useRecoilComponentValueV2(
|
|
||||||
advancedFilterViewFilterIdComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [objectFilterDropdownSearchInput, setObjectFilterDropdownSearchInput] =
|
const [objectFilterDropdownSearchInput, setObjectFilterDropdownSearchInput] =
|
||||||
useRecoilComponentStateV2(objectFilterDropdownSearchInputComponentState);
|
useRecoilComponentStateV2(objectFilterDropdownSearchInputComponentState);
|
||||||
|
|
||||||
const { closeAdvancedFilterDropdown } = useAdvancedFilterDropdown(
|
|
||||||
advancedFilterViewFilterId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { filterableFieldMetadataItems } =
|
const { filterableFieldMetadataItems } =
|
||||||
useFilterableFieldMetadataItemsInRecordIndexContext();
|
useFilterableFieldMetadataItemsInRecordIndexContext();
|
||||||
|
|
||||||
@ -136,11 +126,9 @@ export const ObjectFilterDropdownFilterSelect = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItemId);
|
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItemId);
|
||||||
|
|
||||||
closeAdvancedFilterDropdown();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const shoudShowSeparator =
|
const shouldShowSeparator =
|
||||||
visibleColumnsFieldMetadataItems.length > 0 &&
|
visibleColumnsFieldMetadataItems.length > 0 &&
|
||||||
hiddenColumnsFieldMetadataItems.length > 0;
|
hiddenColumnsFieldMetadataItems.length > 0;
|
||||||
|
|
||||||
@ -186,7 +174,7 @@ export const ObjectFilterDropdownFilterSelect = ({
|
|||||||
</SelectableItem>
|
</SelectableItem>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
{shoudShowSeparator && <DropdownMenuSeparator />}
|
{shouldShowSeparator && <DropdownMenuSeparator />}
|
||||||
{hiddenColumnsFieldMetadataItems.map(
|
{hiddenColumnsFieldMetadataItems.map(
|
||||||
(hiddenFieldMetadataItem, index) => (
|
(hiddenFieldMetadataItem, index) => (
|
||||||
<SelectableItem
|
<SelectableItem
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown';
|
|
||||||
|
|
||||||
import { advancedFilterViewFilterGroupIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState';
|
|
||||||
import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState';
|
|
||||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||||
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
||||||
@ -15,8 +11,6 @@ import { selectedOperandInDropdownComponentState } from '@/object-record/object-
|
|||||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||||
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
|
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
|
||||||
import { getFilterableFieldTypeLabel } from '@/object-record/object-filter-dropdown/utils/getFilterableFieldTypeLabel';
|
import { getFilterableFieldTypeLabel } from '@/object-record/object-filter-dropdown/utils/getFilterableFieldTypeLabel';
|
||||||
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
|
|
||||||
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
|
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters';
|
import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters';
|
||||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||||
@ -72,20 +66,6 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
|
|||||||
objectFilterDropdownSearchInputComponentState,
|
objectFilterDropdownSearchInputComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const advancedFilterViewFilterId = useRecoilComponentValueV2(
|
|
||||||
advancedFilterViewFilterIdComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const advancedFilterViewFilterGroupId = useRecoilComponentValueV2(
|
|
||||||
advancedFilterViewFilterGroupIdComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { applyRecordFilter } = useApplyRecordFilter();
|
|
||||||
|
|
||||||
const { closeAdvancedFilterDropdown } = useAdvancedFilterDropdown(
|
|
||||||
advancedFilterViewFilterId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentRecordFilters = useRecoilComponentValueV2(
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
currentRecordFiltersComponentState,
|
currentRecordFiltersComponentState,
|
||||||
);
|
);
|
||||||
@ -109,30 +89,6 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
|
|||||||
subFieldName: subFieldName,
|
subFieldName: subFieldName,
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
if (
|
|
||||||
isDefined(advancedFilterViewFilterId) &&
|
|
||||||
isDefined(advancedFilterViewFilterGroupId)
|
|
||||||
) {
|
|
||||||
closeAdvancedFilterDropdown();
|
|
||||||
|
|
||||||
const { value, displayValue } = getInitialFilterValue(
|
|
||||||
type,
|
|
||||||
defaultOperand,
|
|
||||||
);
|
|
||||||
|
|
||||||
applyRecordFilter({
|
|
||||||
id: advancedFilterViewFilterId,
|
|
||||||
fieldMetadataId: fieldMetadataItem.id,
|
|
||||||
value,
|
|
||||||
operand: defaultOperand,
|
|
||||||
displayValue,
|
|
||||||
type,
|
|
||||||
label: fieldMetadataItem.label,
|
|
||||||
recordFilterGroupId: advancedFilterViewFilterGroupId,
|
|
||||||
subFieldName: subFieldName,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id);
|
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id);
|
||||||
|
|
||||||
setSubFieldNameUsedInDropdown(subFieldName);
|
setSubFieldNameUsedInDropdown(subFieldName);
|
||||||
@ -152,9 +108,7 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
|
|||||||
duplicateFilterInCurrentRecordFilters,
|
duplicateFilterInCurrentRecordFilters,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isSimpleFilter = !isDefined(advancedFilterViewFilterId);
|
if (filterIsAlreadyInCurrentRecordFilters) {
|
||||||
|
|
||||||
if (isSimpleFilter && filterIsAlreadyInCurrentRecordFilters) {
|
|
||||||
setSelectedFilter({
|
setSelectedFilter({
|
||||||
...duplicateFilterInCurrentRecordFilters,
|
...duplicateFilterInCurrentRecordFilters,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown';
|
|
||||||
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
|
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
|
||||||
import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState';
|
|
||||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||||
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
||||||
|
|
||||||
@ -61,16 +59,8 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
|
|||||||
selectedOperandInDropdownComponentState,
|
selectedOperandInDropdownComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const advancedFilterViewFilterId = useRecoilComponentValueV2(
|
|
||||||
advancedFilterViewFilterIdComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const setHotkeyScope = useSetHotkeyScope();
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
|
||||||
const { closeAdvancedFilterDropdown } = useAdvancedFilterDropdown(
|
|
||||||
advancedFilterViewFilterId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentRecordFilters = useRecoilComponentValueV2(
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
currentRecordFiltersComponentState,
|
currentRecordFiltersComponentState,
|
||||||
);
|
);
|
||||||
@ -80,8 +70,6 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleSelectFilter = (fieldMetadataItem: FieldMetadataItem) => {
|
const handleSelectFilter = (fieldMetadataItem: FieldMetadataItem) => {
|
||||||
closeAdvancedFilterDropdown();
|
|
||||||
|
|
||||||
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id);
|
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id);
|
||||||
|
|
||||||
const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type);
|
const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type);
|
||||||
@ -106,9 +94,7 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
|
|||||||
duplicateFilterInCurrentRecordFilters,
|
duplicateFilterInCurrentRecordFilters,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isSimpleFilter = !isDefined(advancedFilterViewFilterId);
|
if (filterIsAlreadyInCurrentRecordFilters) {
|
||||||
|
|
||||||
if (isSimpleFilter && filterIsAlreadyInCurrentRecordFilters) {
|
|
||||||
setSelectedFilter({
|
setSelectedFilter({
|
||||||
...duplicateFilterInCurrentRecordFilters,
|
...duplicateFilterInCurrentRecordFilters,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,48 @@
|
|||||||
|
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
|
||||||
|
|
||||||
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
|
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
|
||||||
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { MenuItemSelect, useIcons } from 'twenty-ui';
|
||||||
|
|
||||||
|
export type ObjectFilterDropdownFilterSelectMenuItemV2Props = {
|
||||||
|
fieldMetadataItemToSelect: FieldMetadataItem;
|
||||||
|
onClick: (selectedFieldMetadataItem: FieldMetadataItem) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ObjectFilterDropdownFilterSelectMenuItemV2 = ({
|
||||||
|
fieldMetadataItemToSelect,
|
||||||
|
onClick,
|
||||||
|
}: ObjectFilterDropdownFilterSelectMenuItemV2Props) => {
|
||||||
|
const { isSelectedItemIdSelector, resetSelectedItem } = useSelectableList(
|
||||||
|
OBJECT_FILTER_DROPDOWN_ID,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isSelectedItem = useRecoilValue(
|
||||||
|
isSelectedItemIdSelector(fieldMetadataItemToSelect.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { getIcon } = useIcons();
|
||||||
|
|
||||||
|
const Icon = getIcon(fieldMetadataItemToSelect.icon);
|
||||||
|
|
||||||
|
const shouldShowSubMenu = isCompositeField(fieldMetadataItemToSelect.type);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
resetSelectedItem();
|
||||||
|
|
||||||
|
onClick(fieldMetadataItemToSelect);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItemSelect
|
||||||
|
selected={false}
|
||||||
|
hovered={isSelectedItem}
|
||||||
|
onClick={handleClick}
|
||||||
|
LeftIcon={Icon}
|
||||||
|
text={fieldMetadataItemToSelect.label}
|
||||||
|
hasSubMenu={shouldShowSubMenu}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,7 +1,5 @@
|
|||||||
import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
||||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
import { advancedFilterViewFilterGroupIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState';
|
|
||||||
import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState';
|
|
||||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||||
@ -11,7 +9,6 @@ import { getRecordFilterOperands } from '@/object-record/record-filter/utils/get
|
|||||||
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 { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
@ -36,16 +33,6 @@ export const useSelectFilterUsedInDropdown = (componentInstanceId?: string) => {
|
|||||||
componentInstanceId,
|
componentInstanceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const advancedFilterViewFilterGroupId = useRecoilComponentValueV2(
|
|
||||||
advancedFilterViewFilterGroupIdComponentState,
|
|
||||||
componentInstanceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const advancedFilterViewFilterId = useRecoilComponentValueV2(
|
|
||||||
advancedFilterViewFilterIdComponentState,
|
|
||||||
componentInstanceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const setHotkeyScope = useSetHotkeyScope();
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
|
||||||
const { applyRecordFilter } = useApplyRecordFilter(componentInstanceId);
|
const { applyRecordFilter } = useApplyRecordFilter(componentInstanceId);
|
||||||
@ -83,16 +70,13 @@ export const useSelectFilterUsedInDropdown = (componentInstanceId?: string) => {
|
|||||||
firstOperand,
|
firstOperand,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isAdvancedFilter = isDefined(advancedFilterViewFilterId);
|
if (value !== '') {
|
||||||
|
|
||||||
if (isAdvancedFilter || value !== '') {
|
|
||||||
applyRecordFilter({
|
applyRecordFilter({
|
||||||
id: advancedFilterViewFilterId ?? v4(),
|
id: v4(),
|
||||||
fieldMetadataId: fieldMetadataItem.id,
|
fieldMetadataId: fieldMetadataItem.id,
|
||||||
displayValue,
|
displayValue,
|
||||||
operand: firstOperand,
|
operand: firstOperand,
|
||||||
value,
|
value,
|
||||||
recordFilterGroupId: advancedFilterViewFilterGroupId,
|
|
||||||
type: filterType,
|
type: filterType,
|
||||||
label: fieldMetadataItem.label,
|
label: fieldMetadataItem.label,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
|
||||||
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
|
||||||
|
|
||||||
export const advancedFilterViewFilterGroupIdComponentState =
|
|
||||||
createComponentStateV2<string | undefined>({
|
|
||||||
key: 'advancedFilterViewFilterGroupIdComponentState',
|
|
||||||
defaultValue: undefined,
|
|
||||||
componentInstanceContext: ObjectFilterDropdownComponentInstanceContext,
|
|
||||||
});
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
|
||||||
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
|
||||||
|
|
||||||
export const advancedFilterViewFilterIdComponentState = createComponentStateV2<
|
|
||||||
string | undefined
|
|
||||||
>({
|
|
||||||
key: 'advancedFilterViewFilterIdComponentState',
|
|
||||||
defaultValue: undefined,
|
|
||||||
componentInstanceContext: ObjectFilterDropdownComponentInstanceContext,
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user