Feat: Advanced filter (#7700)
Design:  Not ready to be merged yet! --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -0,0 +1,27 @@
|
||||
import { IconFilterCog } from 'twenty-ui';
|
||||
|
||||
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
|
||||
import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId';
|
||||
import { plural } from 'pluralize';
|
||||
|
||||
type AdvancedFilterChipProps = {
|
||||
onRemove: () => void;
|
||||
advancedFilterCount?: number;
|
||||
};
|
||||
|
||||
export const AdvancedFilterChip = ({
|
||||
onRemove,
|
||||
advancedFilterCount,
|
||||
}: AdvancedFilterChipProps) => {
|
||||
const labelText = 'advanced rule';
|
||||
const chipLabel = `${advancedFilterCount ?? 0} ${advancedFilterCount === 1 ? labelText : plural(labelText)}`;
|
||||
return (
|
||||
<SortOrFilterChip
|
||||
testId={ADVANCED_FILTER_DROPDOWN_ID}
|
||||
labelKey={chipLabel}
|
||||
labelValue=""
|
||||
Icon={IconFilterCog}
|
||||
onRemove={onRemove}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,83 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
|
||||
import { AdvancedFilterRootLevelViewFilterGroup } from '@/object-record/advanced-filter/components/AdvancedFilterRootLevelViewFilterGroup';
|
||||
import { useDeleteCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useDeleteCombinedViewFilterGroup';
|
||||
import { AdvancedFilterChip } from '@/views/components/AdvancedFilterChip';
|
||||
import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId';
|
||||
import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters';
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const AdvancedFilterDropdownButton = () => {
|
||||
const { deleteCombinedViewFilter } = useDeleteCombinedViewFilters();
|
||||
const { deleteCombinedViewFilterGroup } = useDeleteCombinedViewFilterGroup();
|
||||
|
||||
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
||||
|
||||
const advancedViewFilterIds =
|
||||
currentViewWithCombinedFiltersAndSorts?.viewFilters
|
||||
.filter((viewFilter) => isDefined(viewFilter.viewFilterGroupId))
|
||||
.map((viewFilter) => viewFilter.id);
|
||||
|
||||
const handleDropdownClickOutside = useCallback(() => {}, []);
|
||||
|
||||
const handleDropdownClose = () => {};
|
||||
|
||||
const removeAdvancedFilter = useCallback(async () => {
|
||||
if (!advancedViewFilterIds) {
|
||||
throw new Error('No advanced view filters to remove');
|
||||
}
|
||||
|
||||
const viewFilterGroupIds =
|
||||
currentViewWithCombinedFiltersAndSorts?.viewFilterGroups?.map(
|
||||
(viewFilter) => viewFilter.id,
|
||||
) ?? [];
|
||||
|
||||
for (const viewFilterGroupId of viewFilterGroupIds) {
|
||||
await deleteCombinedViewFilterGroup(viewFilterGroupId);
|
||||
}
|
||||
|
||||
for (const viewFilterId of advancedViewFilterIds) {
|
||||
await deleteCombinedViewFilter(viewFilterId);
|
||||
}
|
||||
}, [
|
||||
advancedViewFilterIds,
|
||||
deleteCombinedViewFilter,
|
||||
deleteCombinedViewFilterGroup,
|
||||
currentViewWithCombinedFiltersAndSorts?.viewFilterGroups,
|
||||
]);
|
||||
|
||||
const outermostViewFilterGroupId =
|
||||
currentViewWithCombinedFiltersAndSorts?.viewFilterGroups.find(
|
||||
(viewFilterGroup) => !viewFilterGroup.parentViewFilterGroupId,
|
||||
)?.id;
|
||||
|
||||
if (!outermostViewFilterGroupId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownId={ADVANCED_FILTER_DROPDOWN_ID}
|
||||
clickableComponent={
|
||||
<AdvancedFilterChip
|
||||
onRemove={removeAdvancedFilter}
|
||||
advancedFilterCount={advancedViewFilterIds?.length}
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<AdvancedFilterRootLevelViewFilterGroup
|
||||
rootLevelViewFilterGroupId={outermostViewFilterGroupId}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }}
|
||||
dropdownOffset={{ y: 8, x: 0 }}
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownMenuWidth={800}
|
||||
onClickOutside={handleDropdownClickOutside}
|
||||
onClose={handleDropdownClose}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,6 +1,5 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
|
||||
import { MultipleFiltersDropdownContent } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent';
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { FilterOperand } from '@/object-record/object-filter-dropdown/types/FilterOperand';
|
||||
@ -11,6 +10,7 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
|
||||
import { EditableFilterChip } from '@/views/components/EditableFilterChip';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
|
||||
import { ObjectFilterOperandSelectAndInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterOperandSelectAndInput';
|
||||
import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters';
|
||||
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
@ -98,7 +98,7 @@ export const EditableFilterDropdownButton = ({
|
||||
<EditableFilterChip viewFilter={viewFilter} onRemove={handleRemove} />
|
||||
}
|
||||
dropdownComponents={
|
||||
<MultipleFiltersDropdownContent
|
||||
<ObjectFilterOperandSelectAndInput
|
||||
filterDropdownId={viewFilterDropdownId}
|
||||
/>
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { AdvancedFilterDropdownButton } from '@/views/components/AdvancedFilterDropdownButton';
|
||||
import { EditableFilterDropdownButton } from '@/views/components/EditableFilterDropdownButton';
|
||||
import { EditableSortChip } from '@/views/components/EditableSortChip';
|
||||
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
|
||||
@ -137,11 +138,16 @@ export const ViewBarDetails = ({
|
||||
|
||||
const otherViewFilters =
|
||||
currentViewWithCombinedFiltersAndSorts.viewFilters.filter(
|
||||
(viewFilter) => viewFilter.variant && viewFilter.variant !== 'default',
|
||||
(viewFilter) =>
|
||||
viewFilter.variant &&
|
||||
viewFilter.variant !== 'default' &&
|
||||
!viewFilter.viewFilterGroupId,
|
||||
);
|
||||
const defaultViewFilters =
|
||||
currentViewWithCombinedFiltersAndSorts.viewFilters.filter(
|
||||
(viewFilter) => !viewFilter.variant || viewFilter.variant === 'default',
|
||||
(viewFilter) =>
|
||||
(!viewFilter.variant || viewFilter.variant === 'default') &&
|
||||
!viewFilter.viewFilterGroupId,
|
||||
);
|
||||
|
||||
return {
|
||||
@ -166,6 +172,10 @@ export const ViewBarDetails = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const showAdvancedFilterDropdownButton =
|
||||
currentViewWithCombinedFiltersAndSorts?.viewFilterGroups &&
|
||||
currentViewWithCombinedFiltersAndSorts?.viewFilterGroups.length > 0;
|
||||
|
||||
return (
|
||||
<StyledBar>
|
||||
<StyledFilterContainer>
|
||||
@ -199,6 +209,7 @@ export const ViewBarDetails = ({
|
||||
<StyledSeperator />
|
||||
</StyledSeperatorContainer>
|
||||
)}
|
||||
{showAdvancedFilterDropdownButton && <AdvancedFilterDropdownButton />}
|
||||
{mapViewFiltersToFilters(
|
||||
defaultViewFilters,
|
||||
availableFilterDefinitions,
|
||||
|
||||
Reference in New Issue
Block a user