Fix advanced filters (#10665)
This PR partially fixes advanced filters that were not working even with feature flag activated. Bugs fixed here : - Advanced filters are not applied - Root advanced filters cannot be created - Cannot close advanced filters dropdown - Can create multiple times the same non-advanced filter (reserved for advanced filters) upsertRecordFilter and removeRecordFilter have been refactored to take record filter id instead of field metadata id, because the user should be allowed to apply multiple filters for the same field. We now base view filter CRUD directly on id, otherwise it could lead to inconsistencies between advanced filters and simple filters. This PR also refactors an important hook : computeRecordGqlOperationFilter, so that it takes an object instead of multiple params. There are still bugs left, they will be taken in other PRs.
This commit is contained in:
@ -3,7 +3,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|||||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
||||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||||
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
||||||
|
|
||||||
export const computeContextStoreFilters = (
|
export const computeContextStoreFilters = (
|
||||||
@ -16,12 +16,12 @@ export const computeContextStoreFilters = (
|
|||||||
|
|
||||||
if (contextStoreTargetedRecordsRule.mode === 'exclusion') {
|
if (contextStoreTargetedRecordsRule.mode === 'exclusion') {
|
||||||
queryFilter = makeAndFilterVariables([
|
queryFilter = makeAndFilterVariables([
|
||||||
computeViewRecordGqlOperationFilter(
|
computeRecordGqlOperationFilter({
|
||||||
filterValueDependencies,
|
filterValueDependencies,
|
||||||
contextStoreFilters,
|
fields: objectMetadataItem?.fields ?? [],
|
||||||
objectMetadataItem?.fields ?? [],
|
recordFilters: contextStoreFilters,
|
||||||
[],
|
recordFilterGroups: [],
|
||||||
),
|
}),
|
||||||
contextStoreTargetedRecordsRule.excludedRecordIds.length > 0
|
contextStoreTargetedRecordsRule.excludedRecordIds.length > 0
|
||||||
? {
|
? {
|
||||||
not: {
|
not: {
|
||||||
@ -41,12 +41,12 @@ export const computeContextStoreFilters = (
|
|||||||
in: contextStoreTargetedRecordsRule.selectedRecordIds,
|
in: contextStoreTargetedRecordsRule.selectedRecordIds,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: computeViewRecordGqlOperationFilter(
|
: computeRecordGqlOperationFilter({
|
||||||
filterValueDependencies,
|
filterValueDependencies,
|
||||||
contextStoreFilters,
|
fields: objectMetadataItem?.fields ?? [],
|
||||||
objectMetadataItem?.fields ?? [],
|
recordFilters: contextStoreFilters,
|
||||||
[],
|
recordFilterGroups: [],
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryFilter;
|
return queryFilter;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||||
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
|
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
|
||||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
|
import { getAdvancedFilterAddFilterRuleSelectDropdownId } from '@/object-record/advanced-filter/utils/getAdvancedFilterAddFilterRuleSelectDropdownId';
|
||||||
import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup';
|
import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup';
|
||||||
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||||
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
|
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
|
||||||
@ -27,7 +28,9 @@ export const AdvancedFilterAddFilterRuleSelect = ({
|
|||||||
recordFilterGroup,
|
recordFilterGroup,
|
||||||
lastChildPosition = 0,
|
lastChildPosition = 0,
|
||||||
}: AdvancedFilterAddFilterRuleSelectProps) => {
|
}: AdvancedFilterAddFilterRuleSelectProps) => {
|
||||||
const dropdownId = `advanced-filter-add-filter-rule-${recordFilterGroup.id}`;
|
const dropdownId = getAdvancedFilterAddFilterRuleSelectDropdownId(
|
||||||
|
recordFilterGroup.id,
|
||||||
|
);
|
||||||
|
|
||||||
const { currentViewId } = useGetCurrentView();
|
const { currentViewId } = useGetCurrentView();
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte
|
|||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId';
|
|
||||||
import { isDefined } from 'twenty-shared';
|
import { isDefined } from 'twenty-shared';
|
||||||
import { MenuItem } from 'twenty-ui';
|
import { MenuItem } from 'twenty-ui';
|
||||||
|
|
||||||
@ -46,7 +45,7 @@ export const AdvancedFilterRuleOptionsDropdown = ({
|
|||||||
|
|
||||||
const handleRemove = async () => {
|
const handleRemove = async () => {
|
||||||
if (isDefined(viewFilterId)) {
|
if (isDefined(viewFilterId)) {
|
||||||
removeRecordFilter(viewFilterId);
|
removeRecordFilter({ recordFilterId: viewFilterId });
|
||||||
|
|
||||||
const isOnlyViewFilterInGroup =
|
const isOnlyViewFilterInGroup =
|
||||||
childViewFiltersAndViewFilterGroups.length === 1;
|
childViewFiltersAndViewFilterGroups.length === 1;
|
||||||
@ -66,7 +65,7 @@ export const AdvancedFilterRuleOptionsDropdown = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (const childViewFilter of childViewFilters) {
|
for (const childViewFilter of childViewFilters) {
|
||||||
removeRecordFilter(childViewFilter.id);
|
removeRecordFilter({ recordFilterId: childViewFilter.id });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('No view filter or view filter group to remove');
|
throw new Error('No view filter or view filter group to remove');
|
||||||
@ -86,7 +85,7 @@ export const AdvancedFilterRuleOptionsDropdown = ({
|
|||||||
<MenuItem text={removeButtonLabel} onClick={handleRemove} />
|
<MenuItem text={removeButtonLabel} onClick={handleRemove} />
|
||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }}
|
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||||
dropdownOffset={{ y: 8, x: 0 }}
|
dropdownOffset={{ y: 8, x: 0 }}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
export const getAdvancedFilterAddFilterRuleSelectDropdownId = (
|
||||||
|
recordFilterGroupId: string,
|
||||||
|
) => {
|
||||||
|
return `advanced-filter-add-filter-rule-${recordFilterGroupId}`;
|
||||||
|
};
|
||||||
@ -133,8 +133,8 @@ export const AdvancedFilterButton = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openAdvancedFilterDropdown();
|
|
||||||
closeObjectFilterDropdown();
|
closeObjectFilterDropdown();
|
||||||
|
openAdvancedFilterDropdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -9,12 +9,15 @@ import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-rec
|
|||||||
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
||||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||||
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
|
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 { 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 { 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 { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
|
||||||
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
|
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
|
||||||
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
|
import { 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';
|
||||||
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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||||
@ -81,55 +84,84 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
|
|||||||
advancedFilterViewFilterId,
|
advancedFilterViewFilterId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setSelectedFilter = useSetRecoilComponentStateV2(
|
||||||
|
selectedFilterComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
const handleSelectFilter = (
|
const handleSelectFilter = (
|
||||||
fieldMetadataItem: FieldMetadataItem | null | undefined,
|
fieldMetadataItem: FieldMetadataItem | null | undefined,
|
||||||
subFieldName?: string | null | undefined,
|
subFieldName?: string | null | undefined,
|
||||||
) => {
|
) => {
|
||||||
if (isDefined(fieldMetadataItem)) {
|
if (!isDefined(fieldMetadataItem)) {
|
||||||
if (
|
return;
|
||||||
isDefined(advancedFilterViewFilterId) &&
|
}
|
||||||
isDefined(advancedFilterViewFilterGroupId)
|
|
||||||
) {
|
|
||||||
closeAdvancedFilterDropdown();
|
|
||||||
|
|
||||||
const type = getFilterTypeFromFieldType(fieldMetadataItem.type);
|
const type = getFilterTypeFromFieldType(fieldMetadataItem.type);
|
||||||
|
|
||||||
const operand = getRecordFilterOperands({
|
const defaultOperand = getRecordFilterOperands({
|
||||||
filterType: type,
|
filterType: type,
|
||||||
subFieldName: subFieldName,
|
subFieldName: subFieldName,
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
const { value, displayValue } = getInitialFilterValue(type, operand);
|
if (
|
||||||
|
isDefined(advancedFilterViewFilterId) &&
|
||||||
|
isDefined(advancedFilterViewFilterGroupId)
|
||||||
|
) {
|
||||||
|
closeAdvancedFilterDropdown();
|
||||||
|
|
||||||
applyRecordFilter({
|
const { value, displayValue } = getInitialFilterValue(
|
||||||
id: advancedFilterViewFilterId,
|
type,
|
||||||
fieldMetadataId: fieldMetadataItem.id,
|
defaultOperand,
|
||||||
value,
|
|
||||||
operand,
|
|
||||||
displayValue,
|
|
||||||
type: getFilterTypeFromFieldType(fieldMetadataItem.type),
|
|
||||||
label: fieldMetadataItem.label,
|
|
||||||
recordFilterGroupId: advancedFilterViewFilterGroupId,
|
|
||||||
subFieldName: subFieldName,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id);
|
|
||||||
|
|
||||||
const type = getFilterTypeFromFieldType(fieldMetadataItem.type);
|
|
||||||
|
|
||||||
setSelectedOperandInDropdown(
|
|
||||||
getRecordFilterOperands({
|
|
||||||
filterType: type,
|
|
||||||
subFieldName: subFieldName,
|
|
||||||
})[0],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
setSubFieldNameUsedInDropdown(subFieldName);
|
applyRecordFilter({
|
||||||
|
id: advancedFilterViewFilterId,
|
||||||
|
fieldMetadataId: fieldMetadataItem.id,
|
||||||
|
value,
|
||||||
|
operand: defaultOperand,
|
||||||
|
displayValue,
|
||||||
|
type,
|
||||||
|
label: fieldMetadataItem.label,
|
||||||
|
recordFilterGroupId: advancedFilterViewFilterGroupId,
|
||||||
|
subFieldName: subFieldName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setObjectFilterDropdownSearchInput('');
|
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id);
|
||||||
|
|
||||||
setObjectFilterDropdownFilterIsSelected(true);
|
setSubFieldNameUsedInDropdown(subFieldName);
|
||||||
|
|
||||||
|
setObjectFilterDropdownSearchInput('');
|
||||||
|
|
||||||
|
setObjectFilterDropdownFilterIsSelected(true);
|
||||||
|
|
||||||
|
const duplicateFilterInCurrentRecordFilters =
|
||||||
|
findDuplicateRecordFilterInNonAdvancedRecordFilters({
|
||||||
|
recordFilters: currentRecordFilters,
|
||||||
|
fieldMetadataItemId: fieldMetadataItem.id,
|
||||||
|
subFieldName,
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterIsAlreadyInCurrentRecordFilters = isDefined(
|
||||||
|
duplicateFilterInCurrentRecordFilters,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isSimpleFilter = !isDefined(advancedFilterViewFilterId);
|
||||||
|
|
||||||
|
if (isSimpleFilter && filterIsAlreadyInCurrentRecordFilters) {
|
||||||
|
setSelectedFilter({
|
||||||
|
...duplicateFilterInCurrentRecordFilters,
|
||||||
|
});
|
||||||
|
|
||||||
|
setSelectedOperandInDropdown(
|
||||||
|
duplicateFilterInCurrentRecordFilters.operand,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setSelectedOperandInDropdown(defaultOperand);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,10 @@ import { selectedOperandInDropdownComponentState } from '@/object-record/object-
|
|||||||
|
|
||||||
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 { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
|
||||||
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
|
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
|
||||||
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
|
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';
|
||||||
import { RecordPickerHotkeyScope } from '@/object-record/record-picker/types/RecordPickerHotkeyScope';
|
import { RecordPickerHotkeyScope } from '@/object-record/record-picker/types/RecordPickerHotkeyScope';
|
||||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
@ -19,6 +22,7 @@ import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/
|
|||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { isDefined } from 'twenty-shared';
|
||||||
import { MenuItemSelect, useIcons } from 'twenty-ui';
|
import { MenuItemSelect, useIcons } from 'twenty-ui';
|
||||||
|
|
||||||
export type ObjectFilterDropdownFilterSelectMenuItemProps = {
|
export type ObjectFilterDropdownFilterSelectMenuItemProps = {
|
||||||
@ -67,6 +71,14 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
|
|||||||
advancedFilterViewFilterId,
|
advancedFilterViewFilterId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setSelectedFilter = useSetRecoilComponentStateV2(
|
||||||
|
selectedFilterComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
const handleSelectFilter = (fieldMetadataItem: FieldMetadataItem) => {
|
const handleSelectFilter = (fieldMetadataItem: FieldMetadataItem) => {
|
||||||
closeAdvancedFilterDropdown();
|
closeAdvancedFilterDropdown();
|
||||||
|
|
||||||
@ -78,13 +90,35 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
|
|||||||
setHotkeyScope(RecordPickerHotkeyScope.RecordPicker);
|
setHotkeyScope(RecordPickerHotkeyScope.RecordPicker);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedOperandInDropdown(
|
const defaultOperand = getRecordFilterOperands({
|
||||||
getRecordFilterOperands({
|
filterType,
|
||||||
filterType,
|
})[0];
|
||||||
})[0],
|
|
||||||
);
|
|
||||||
|
|
||||||
setObjectFilterDropdownFilterIsSelected(true);
|
setObjectFilterDropdownFilterIsSelected(true);
|
||||||
|
|
||||||
|
const duplicateFilterInCurrentRecordFilters =
|
||||||
|
findDuplicateRecordFilterInNonAdvancedRecordFilters({
|
||||||
|
recordFilters: currentRecordFilters,
|
||||||
|
fieldMetadataItemId: fieldMetadataItem.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterIsAlreadyInCurrentRecordFilters = isDefined(
|
||||||
|
duplicateFilterInCurrentRecordFilters,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isSimpleFilter = !isDefined(advancedFilterViewFilterId);
|
||||||
|
|
||||||
|
if (isSimpleFilter && filterIsAlreadyInCurrentRecordFilters) {
|
||||||
|
setSelectedFilter({
|
||||||
|
...duplicateFilterInCurrentRecordFilters,
|
||||||
|
});
|
||||||
|
|
||||||
|
setSelectedOperandInDropdown(
|
||||||
|
duplicateFilterInCurrentRecordFilters.operand,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setSelectedOperandInDropdown(defaultOperand);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
|
|||||||
@ -54,7 +54,9 @@ describe('useRemoveRecordFilter', () => {
|
|||||||
expect(result.current.currentRecordFilters[0]).toEqual(mockRecordFilter);
|
expect(result.current.currentRecordFilters[0]).toEqual(mockRecordFilter);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.removeRecordFilter(mockRecordFilter.fieldMetadataId);
|
result.current.removeRecordFilter({
|
||||||
|
recordFilterId: mockRecordFilter.id,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.currentRecordFilters).toHaveLength(0);
|
expect(result.current.currentRecordFilters).toHaveLength(0);
|
||||||
@ -96,7 +98,9 @@ describe('useRemoveRecordFilter', () => {
|
|||||||
expect(result.current.currentRecordFilters).toHaveLength(1);
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.removeRecordFilter('non-existent-field-metadata-id');
|
result.current.removeRecordFilter({
|
||||||
|
recordFilterId: 'non-existent-field-metadata-id',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.currentRecordFilters).toHaveLength(1);
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('useUpsertRecordFilter', () => {
|
describe('useUpsertRecordFilter', () => {
|
||||||
it('should add a new filter when fieldMetadataId does not exist', () => {
|
it('should add a new filter when record filter id does not exist', () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => {
|
() => {
|
||||||
const currentRecordFilters = useRecoilComponentValueV2(
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
@ -48,7 +48,7 @@ describe('useUpsertRecordFilter', () => {
|
|||||||
expect(result.current.currentRecordFilters[0]).toEqual(mockNewRecordFilter);
|
expect(result.current.currentRecordFilters[0]).toEqual(mockNewRecordFilter);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update an existing filter when fieldMetadataId exists', () => {
|
it('should update an existing filter when record filter id exists', () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => {
|
() => {
|
||||||
const currentRecordFilters = useRecoilComponentValueV2(
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const useRemoveRecordFilter = () => {
|
|||||||
|
|
||||||
const removeRecordFilter = useRecoilCallback(
|
const removeRecordFilter = useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
(fieldMetadataId: string) => {
|
({ recordFilterId }: { recordFilterId: string }) => {
|
||||||
const currentRecordFilters = getSnapshotValue(
|
const currentRecordFilters = getSnapshotValue(
|
||||||
snapshot,
|
snapshot,
|
||||||
currentRecordFiltersCallbackState,
|
currentRecordFiltersCallbackState,
|
||||||
@ -18,8 +18,7 @@ export const useRemoveRecordFilter = () => {
|
|||||||
|
|
||||||
const foundRecordFilterInCurrentRecordFilters =
|
const foundRecordFilterInCurrentRecordFilters =
|
||||||
currentRecordFilters.some(
|
currentRecordFilters.some(
|
||||||
(existingFilter) =>
|
(existingFilter) => existingFilter.id === recordFilterId,
|
||||||
existingFilter.fieldMetadataId === fieldMetadataId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (foundRecordFilterInCurrentRecordFilters) {
|
if (foundRecordFilterInCurrentRecordFilters) {
|
||||||
@ -27,8 +26,7 @@ export const useRemoveRecordFilter = () => {
|
|||||||
const newCurrentRecordFilters = [...currentRecordFilters];
|
const newCurrentRecordFilters = [...currentRecordFilters];
|
||||||
|
|
||||||
const indexOfFilterToRemove = newCurrentRecordFilters.findIndex(
|
const indexOfFilterToRemove = newCurrentRecordFilters.findIndex(
|
||||||
(existingFilter) =>
|
(existingFilter) => existingFilter.id === recordFilterId,
|
||||||
existingFilter.fieldMetadataId === fieldMetadataId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
newCurrentRecordFilters.splice(indexOfFilterToRemove, 1);
|
newCurrentRecordFilters.splice(indexOfFilterToRemove, 1);
|
||||||
|
|||||||
@ -19,9 +19,7 @@ export const useUpsertRecordFilter = () => {
|
|||||||
|
|
||||||
const foundRecordFilterInCurrentRecordFilters =
|
const foundRecordFilterInCurrentRecordFilters =
|
||||||
currentRecordFilters.some(
|
currentRecordFilters.some(
|
||||||
(existingFilter) =>
|
(existingFilter) => existingFilter.id === recordFilterToSet.id,
|
||||||
existingFilter.fieldMetadataId ===
|
|
||||||
recordFilterToSet.fieldMetadataId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!foundRecordFilterInCurrentRecordFilters) {
|
if (!foundRecordFilterInCurrentRecordFilters) {
|
||||||
@ -34,9 +32,7 @@ export const useUpsertRecordFilter = () => {
|
|||||||
const newCurrentRecordFilters = [...currentRecordFilters];
|
const newCurrentRecordFilters = [...currentRecordFilters];
|
||||||
|
|
||||||
const indexOfFilterToUpdate = newCurrentRecordFilters.findIndex(
|
const indexOfFilterToUpdate = newCurrentRecordFilters.findIndex(
|
||||||
(existingFilter) =>
|
(existingFilter) => existingFilter.id === recordFilterToSet.id,
|
||||||
existingFilter.fieldMetadataId ===
|
|
||||||
recordFilterToSet.fieldMetadataId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
newCurrentRecordFilters[indexOfFilterToUpdate] = {
|
newCurrentRecordFilters[indexOfFilterToUpdate] = {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||||
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
||||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
import { FieldMetadataType } from '~/generated/graphql';
|
import { FieldMetadataType } from '~/generated/graphql';
|
||||||
import { getCompaniesMock } from '~/testing/mock-data/companies';
|
import { getCompaniesMock } from '~/testing/mock-data/companies';
|
||||||
@ -40,12 +40,12 @@ describe('computeViewRecordGqlOperationFilter', () => {
|
|||||||
label: 'Name',
|
label: 'Name',
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = computeViewRecordGqlOperationFilter(
|
const result = computeRecordGqlOperationFilter({
|
||||||
mockFilterValueDependencies,
|
filterValueDependencies: mockFilterValueDependencies,
|
||||||
[nameFilter],
|
recordFilters: [nameFilter],
|
||||||
companyMockObjectMetadataItem.fields,
|
recordFilterGroups: [],
|
||||||
[],
|
fields: companyMockObjectMetadataItem.fields,
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
name: {
|
name: {
|
||||||
@ -85,12 +85,12 @@ describe('computeViewRecordGqlOperationFilter', () => {
|
|||||||
label: 'Employees',
|
label: 'Employees',
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = computeViewRecordGqlOperationFilter(
|
const result = computeRecordGqlOperationFilter({
|
||||||
mockFilterValueDependencies,
|
filterValueDependencies: mockFilterValueDependencies,
|
||||||
[nameFilter, employeesFilter],
|
recordFilters: [nameFilter, employeesFilter],
|
||||||
companyMockObjectMetadataItem.fields,
|
recordFilterGroups: [],
|
||||||
[],
|
fields: companyMockObjectMetadataItem.fields,
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
and: [
|
and: [
|
||||||
@ -156,17 +156,17 @@ describe('should work as expected for the different field types', () => {
|
|||||||
type: FieldMetadataType.ADDRESS,
|
type: FieldMetadataType.ADDRESS,
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = computeViewRecordGqlOperationFilter(
|
const result = computeRecordGqlOperationFilter({
|
||||||
mockFilterValueDependencies,
|
filterValueDependencies: mockFilterValueDependencies,
|
||||||
[
|
recordFilters: [
|
||||||
addressFilterContains,
|
addressFilterContains,
|
||||||
addressFilterDoesNotContain,
|
addressFilterDoesNotContain,
|
||||||
addressFilterIsEmpty,
|
addressFilterIsEmpty,
|
||||||
addressFilterIsNotEmpty,
|
addressFilterIsNotEmpty,
|
||||||
],
|
],
|
||||||
companyMockObjectMetadataItem.fields,
|
recordFilterGroups: [],
|
||||||
[],
|
fields: companyMockObjectMetadataItem.fields,
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
and: [
|
and: [
|
||||||
@ -523,17 +523,17 @@ describe('should work as expected for the different field types', () => {
|
|||||||
type: FieldMetadataType.PHONES,
|
type: FieldMetadataType.PHONES,
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = computeViewRecordGqlOperationFilter(
|
const result = computeRecordGqlOperationFilter({
|
||||||
mockFilterValueDependencies,
|
filterValueDependencies: mockFilterValueDependencies,
|
||||||
[
|
recordFilters: [
|
||||||
phonesFilterContains,
|
phonesFilterContains,
|
||||||
phonesFilterDoesNotContain,
|
phonesFilterDoesNotContain,
|
||||||
phonesFilterIsEmpty,
|
phonesFilterIsEmpty,
|
||||||
phonesFilterIsNotEmpty,
|
phonesFilterIsNotEmpty,
|
||||||
],
|
],
|
||||||
personMockObjectMetadataItem.fields,
|
recordFilterGroups: [],
|
||||||
[],
|
fields: personMockObjectMetadataItem.fields,
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
and: [
|
and: [
|
||||||
@ -657,17 +657,17 @@ describe('should work as expected for the different field types', () => {
|
|||||||
type: FieldMetadataType.EMAILS,
|
type: FieldMetadataType.EMAILS,
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = computeViewRecordGqlOperationFilter(
|
const result = computeRecordGqlOperationFilter({
|
||||||
mockFilterValueDependencies,
|
filterValueDependencies: mockFilterValueDependencies,
|
||||||
[
|
recordFilters: [
|
||||||
emailsFilterContains,
|
emailsFilterContains,
|
||||||
emailsFilterDoesNotContain,
|
emailsFilterDoesNotContain,
|
||||||
emailsFilterIsEmpty,
|
emailsFilterIsEmpty,
|
||||||
emailsFilterIsNotEmpty,
|
emailsFilterIsNotEmpty,
|
||||||
],
|
],
|
||||||
personMockObjectMetadataItem.fields,
|
recordFilterGroups: [],
|
||||||
[],
|
fields: personMockObjectMetadataItem.fields,
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
and: [
|
and: [
|
||||||
@ -793,18 +793,18 @@ describe('should work as expected for the different field types', () => {
|
|||||||
type: FieldMetadataType.DATE_TIME,
|
type: FieldMetadataType.DATE_TIME,
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = computeViewRecordGqlOperationFilter(
|
const result = computeRecordGqlOperationFilter({
|
||||||
mockFilterValueDependencies,
|
filterValueDependencies: mockFilterValueDependencies,
|
||||||
[
|
recordFilters: [
|
||||||
dateFilterIsAfter,
|
dateFilterIsAfter,
|
||||||
dateFilterIsBefore,
|
dateFilterIsBefore,
|
||||||
dateFilterIs,
|
dateFilterIs,
|
||||||
dateFilterIsEmpty,
|
dateFilterIsEmpty,
|
||||||
dateFilterIsNotEmpty,
|
dateFilterIsNotEmpty,
|
||||||
],
|
],
|
||||||
companyMockObjectMetadataItem.fields,
|
recordFilterGroups: [],
|
||||||
[],
|
fields: companyMockObjectMetadataItem.fields,
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
and: [
|
and: [
|
||||||
@ -894,17 +894,17 @@ describe('should work as expected for the different field types', () => {
|
|||||||
type: FieldMetadataType.NUMBER,
|
type: FieldMetadataType.NUMBER,
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = computeViewRecordGqlOperationFilter(
|
const result = computeRecordGqlOperationFilter({
|
||||||
mockFilterValueDependencies,
|
filterValueDependencies: mockFilterValueDependencies,
|
||||||
[
|
recordFilters: [
|
||||||
employeesFilterIsGreaterThan,
|
employeesFilterIsGreaterThan,
|
||||||
employeesFilterIsLessThan,
|
employeesFilterIsLessThan,
|
||||||
employeesFilterIsEmpty,
|
employeesFilterIsEmpty,
|
||||||
employeesFilterIsNotEmpty,
|
employeesFilterIsNotEmpty,
|
||||||
],
|
],
|
||||||
companyMockObjectMetadataItem.fields,
|
recordFilterGroups: [],
|
||||||
[],
|
fields: companyMockObjectMetadataItem.fields,
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
and: [
|
and: [
|
||||||
|
|||||||
@ -32,8 +32,7 @@ import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
|||||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||||
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
||||||
import { getEmptyRecordGqlOperationFilter } from '@/object-record/record-filter/utils/getEmptyRecordGqlOperationFilter';
|
import { getEmptyRecordGqlOperationFilter } from '@/object-record/record-filter/utils/getEmptyRecordGqlOperationFilter';
|
||||||
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
|
||||||
import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator';
|
|
||||||
import { resolveDateViewFilterValue } from '@/views/view-filter-value/utils/resolveDateViewFilterValue';
|
import { resolveDateViewFilterValue } from '@/views/view-filter-value/utils/resolveDateViewFilterValue';
|
||||||
import { resolveSelectViewFilterValue } from '@/views/view-filter-value/utils/resolveSelectViewFilterValue';
|
import { resolveSelectViewFilterValue } from '@/views/view-filter-value/utils/resolveSelectViewFilterValue';
|
||||||
import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
|
import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
|
||||||
@ -41,6 +40,8 @@ import { simpleRelationFilterValueSchema } from '@/views/view-filter-value/valid
|
|||||||
import { endOfDay, roundToNearestMinutes, startOfDay } from 'date-fns';
|
import { endOfDay, roundToNearestMinutes, startOfDay } from 'date-fns';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||||
|
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
|
||||||
import { FilterableFieldType } from '@/object-record/record-filter/types/FilterableFieldType';
|
import { FilterableFieldType } from '@/object-record/record-filter/types/FilterableFieldType';
|
||||||
|
|
||||||
type ComputeFilterRecordGqlOperationFilterParams = {
|
type ComputeFilterRecordGqlOperationFilterParams = {
|
||||||
@ -808,54 +809,55 @@ export const computeFilterRecordGqlOperationFilter = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const computeViewFilterGroupRecordGqlOperationFilter = (
|
const computeRecordFilterGroupRecordGqlOperationFilter = (
|
||||||
filterValueDependencies: RecordFilterValueDependencies,
|
filterValueDependencies: RecordFilterValueDependencies,
|
||||||
filters: RecordFilter[],
|
filters: RecordFilter[],
|
||||||
fields: Pick<Field, 'id' | 'name' | 'type'>[],
|
fields: Pick<Field, 'id' | 'name' | 'type'>[],
|
||||||
viewFilterGroups: ViewFilterGroup[],
|
recordFilterGroups: RecordFilterGroup[],
|
||||||
currentViewFilterGroupId?: string,
|
currentRecordFilterGroupId?: string,
|
||||||
): RecordGqlOperationFilter | undefined => {
|
): RecordGqlOperationFilter | undefined => {
|
||||||
const currentViewFilterGroup = viewFilterGroups.find(
|
const currentRecordFilterGroup = recordFilterGroups.find(
|
||||||
(viewFilterGroup) => viewFilterGroup.id === currentViewFilterGroupId,
|
(recordFilterGroup) => recordFilterGroup.id === currentRecordFilterGroupId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!currentViewFilterGroup) {
|
if (!currentRecordFilterGroup) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupFilters = filters.filter(
|
const recordFiltersInGroup = filters.filter(
|
||||||
(filter) => filter.recordFilterGroupId === currentViewFilterGroupId,
|
(filter) => filter.recordFilterGroupId === currentRecordFilterGroupId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const groupRecordGqlOperationFilters = groupFilters
|
const groupRecordGqlOperationFilters = recordFiltersInGroup
|
||||||
.map((filter) =>
|
.map((recordFilter) =>
|
||||||
computeFilterRecordGqlOperationFilter({
|
computeFilterRecordGqlOperationFilter({
|
||||||
filterValueDependencies,
|
filterValueDependencies,
|
||||||
filter,
|
filter: recordFilter,
|
||||||
fieldMetadataItems: fields,
|
fieldMetadataItems: fields,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.filter(isDefined);
|
.filter(isDefined);
|
||||||
|
|
||||||
const subGroupRecordGqlOperationFilters = viewFilterGroups
|
const subGroupRecordGqlOperationFilters = recordFilterGroups
|
||||||
.filter(
|
.filter(
|
||||||
(viewFilterGroup) =>
|
(recordFilterGroup) =>
|
||||||
viewFilterGroup.parentViewFilterGroupId === currentViewFilterGroupId,
|
recordFilterGroup.parentRecordFilterGroupId ===
|
||||||
|
currentRecordFilterGroupId,
|
||||||
)
|
)
|
||||||
.map((subViewFilterGroup) =>
|
.map((subViewFilterGroup) =>
|
||||||
computeViewFilterGroupRecordGqlOperationFilter(
|
computeRecordFilterGroupRecordGqlOperationFilter(
|
||||||
filterValueDependencies,
|
filterValueDependencies,
|
||||||
filters,
|
filters,
|
||||||
fields,
|
fields,
|
||||||
viewFilterGroups,
|
recordFilterGroups,
|
||||||
subViewFilterGroup.id,
|
subViewFilterGroup.id,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.filter(isDefined);
|
.filter(isDefined);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
currentViewFilterGroup.logicalOperator ===
|
currentRecordFilterGroup.logicalOperator ===
|
||||||
ViewFilterGroupLogicalOperator.AND
|
RecordFilterGroupLogicalOperator.AND
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
and: [
|
and: [
|
||||||
@ -864,7 +866,8 @@ const computeViewFilterGroupRecordGqlOperationFilter = (
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
} else if (
|
} else if (
|
||||||
currentViewFilterGroup.logicalOperator === ViewFilterGroupLogicalOperator.OR
|
currentRecordFilterGroup.logicalOperator ===
|
||||||
|
RecordFilterGroupLogicalOperator.OR
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
or: [
|
or: [
|
||||||
@ -874,38 +877,44 @@ const computeViewFilterGroupRecordGqlOperationFilter = (
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unknown logical operator ${currentViewFilterGroup.logicalOperator}`,
|
`Unknown logical operator ${currentRecordFilterGroup.logicalOperator}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const computeViewRecordGqlOperationFilter = (
|
export const computeRecordGqlOperationFilter = ({
|
||||||
filterValueDependencies: RecordFilterValueDependencies,
|
fields,
|
||||||
filters: RecordFilter[],
|
filterValueDependencies,
|
||||||
fields: Pick<Field, 'id' | 'name' | 'type'>[],
|
recordFilters,
|
||||||
viewFilterGroups: ViewFilterGroup[],
|
recordFilterGroups,
|
||||||
): RecordGqlOperationFilter => {
|
}: {
|
||||||
const regularRecordGqlOperationFilter: RecordGqlOperationFilter[] = filters
|
filterValueDependencies: RecordFilterValueDependencies;
|
||||||
.filter((filter) => !filter.recordFilterGroupId)
|
recordFilters: RecordFilter[];
|
||||||
.map((regularFilter) =>
|
fields: Pick<Field, 'id' | 'name' | 'type'>[];
|
||||||
computeFilterRecordGqlOperationFilter({
|
recordFilterGroups: RecordFilterGroup[];
|
||||||
filterValueDependencies,
|
}): RecordGqlOperationFilter => {
|
||||||
filter: regularFilter,
|
const regularRecordGqlOperationFilter: RecordGqlOperationFilter[] =
|
||||||
fieldMetadataItems: fields,
|
recordFilters
|
||||||
}),
|
.filter((filter) => !isDefined(filter.recordFilterGroupId))
|
||||||
)
|
.map((regularFilter) =>
|
||||||
.filter(isDefined);
|
computeFilterRecordGqlOperationFilter({
|
||||||
|
filterValueDependencies,
|
||||||
|
filter: regularFilter,
|
||||||
|
fieldMetadataItems: fields,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.filter(isDefined);
|
||||||
|
|
||||||
const outermostFilterGroupId = viewFilterGroups.find(
|
const outermostFilterGroupId = recordFilterGroups.find(
|
||||||
(viewFilterGroup) => !viewFilterGroup.parentViewFilterGroupId,
|
(recordFilterGroup) => !recordFilterGroup.parentRecordFilterGroupId,
|
||||||
)?.id;
|
)?.id;
|
||||||
|
|
||||||
const advancedRecordGqlOperationFilter =
|
const advancedRecordGqlOperationFilter =
|
||||||
computeViewFilterGroupRecordGqlOperationFilter(
|
computeRecordFilterGroupRecordGqlOperationFilter(
|
||||||
filterValueDependencies,
|
filterValueDependencies,
|
||||||
filters,
|
recordFilters,
|
||||||
fields,
|
fields,
|
||||||
viewFilterGroups,
|
recordFilterGroups,
|
||||||
outermostFilterGroupId,
|
outermostFilterGroupId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
|
import { compareStrictlyExceptForNullAndUndefined } from '~/utils/compareStrictlyExceptForNullAndUndefined';
|
||||||
|
|
||||||
|
export const findDuplicateRecordFilterInNonAdvancedRecordFilters = ({
|
||||||
|
recordFilters,
|
||||||
|
fieldMetadataItemId,
|
||||||
|
subFieldName,
|
||||||
|
}: {
|
||||||
|
recordFilters: RecordFilter[];
|
||||||
|
fieldMetadataItemId: string;
|
||||||
|
subFieldName?: string | null | undefined;
|
||||||
|
}): RecordFilter | undefined => {
|
||||||
|
const duplicateFilterInCurrentRecordFilters = recordFilters.find(
|
||||||
|
(recordFilter) =>
|
||||||
|
compareStrictlyExceptForNullAndUndefined(
|
||||||
|
recordFilter.fieldMetadataId,
|
||||||
|
fieldMetadataItemId,
|
||||||
|
) &&
|
||||||
|
compareStrictlyExceptForNullAndUndefined(
|
||||||
|
recordFilter.subFieldName,
|
||||||
|
subFieldName,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return duplicateFilterInCurrentRecordFilters;
|
||||||
|
};
|
||||||
@ -34,7 +34,6 @@ export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect =
|
|||||||
|
|
||||||
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
||||||
objectMetadataItem?.nameSingular ?? '',
|
objectMetadataItem?.nameSingular ?? '',
|
||||||
objectMetadataItem?.namePlural ?? '',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const contextStoreFilters = useRecoilComponentValueV2(
|
const contextStoreFilters = useRecoilComponentValueV2(
|
||||||
|
|||||||
@ -83,7 +83,6 @@ export const useExportFetchRecords = ({
|
|||||||
|
|
||||||
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
||||||
objectMetadataItem.nameSingular,
|
objectMetadataItem.nameSingular,
|
||||||
recordIndexId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const finalColumns = [
|
const finalColumns = [
|
||||||
|
|||||||
@ -1,17 +1,16 @@
|
|||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||||
|
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||||
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
|
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
|
||||||
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
||||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||||
import { tableViewFilterGroupsComponentState } from '@/object-record/record-table/states/tableViewFilterGroupsComponentState';
|
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
|
||||||
export const useFindManyRecordIndexTableParams = (
|
export const useFindManyRecordIndexTableParams = (
|
||||||
objectNameSingular: string,
|
objectNameSingular: string,
|
||||||
recordTableId?: string,
|
|
||||||
) => {
|
) => {
|
||||||
const { objectMetadataItem } = useObjectMetadataItem({
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
@ -23,9 +22,8 @@ export const useFindManyRecordIndexTableParams = (
|
|||||||
|
|
||||||
const currentRecordGroupDefinition = useCurrentRecordGroupDefinition();
|
const currentRecordGroupDefinition = useCurrentRecordGroupDefinition();
|
||||||
|
|
||||||
const tableViewFilterGroups = useRecoilComponentValueV2(
|
const currentRecordFilterGroups = useRecoilComponentValueV2(
|
||||||
tableViewFilterGroupsComponentState,
|
currentRecordFilterGroupsComponentState,
|
||||||
recordTableId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentRecordSorts = useRecoilComponentValueV2(
|
const currentRecordSorts = useRecoilComponentValueV2(
|
||||||
@ -38,12 +36,12 @@ export const useFindManyRecordIndexTableParams = (
|
|||||||
|
|
||||||
const { filterValueDependencies } = useFilterValueDependencies();
|
const { filterValueDependencies } = useFilterValueDependencies();
|
||||||
|
|
||||||
const stateFilter = computeViewRecordGqlOperationFilter(
|
const stateFilter = computeRecordGqlOperationFilter({
|
||||||
|
fields: objectMetadataItem?.fields ?? [],
|
||||||
filterValueDependencies,
|
filterValueDependencies,
|
||||||
currentRecordFilters,
|
recordFilterGroups: currentRecordFilterGroups,
|
||||||
objectMetadataItem?.fields ?? [],
|
recordFilters: currentRecordFilters,
|
||||||
tableViewFilterGroups,
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts);
|
const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts);
|
||||||
|
|
||||||
|
|||||||
@ -7,13 +7,14 @@ import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils
|
|||||||
import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/useSetRecordIdsForColumn';
|
import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/useSetRecordIdsForColumn';
|
||||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||||
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
|
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
|
||||||
import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
|
import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
|
||||||
import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState';
|
import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState';
|
||||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { mapViewFilterGroupsToRecordFilterGroups } from '@/views/utils/mapViewFilterGroupsToRecordFilterGroups';
|
||||||
import { isDefined } from 'twenty-shared';
|
import { isDefined } from 'twenty-shared';
|
||||||
|
|
||||||
type UseLoadRecordIndexBoardProps = {
|
type UseLoadRecordIndexBoardProps = {
|
||||||
@ -43,6 +44,10 @@ export const useLoadRecordIndexBoardColumn = ({
|
|||||||
recordIndexViewFilterGroupsState,
|
recordIndexViewFilterGroupsState,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const recordFilterGroups = mapViewFilterGroupsToRecordFilterGroups(
|
||||||
|
recordIndexViewFilterGroups,
|
||||||
|
);
|
||||||
|
|
||||||
const currentRecordFilters = useRecoilComponentValueV2(
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
currentRecordFiltersComponentState,
|
currentRecordFiltersComponentState,
|
||||||
);
|
);
|
||||||
@ -53,12 +58,12 @@ export const useLoadRecordIndexBoardColumn = ({
|
|||||||
|
|
||||||
const { filterValueDependencies } = useFilterValueDependencies();
|
const { filterValueDependencies } = useFilterValueDependencies();
|
||||||
|
|
||||||
const requestFilters = computeViewRecordGqlOperationFilter(
|
const requestFilters = computeRecordGqlOperationFilter({
|
||||||
filterValueDependencies,
|
filterValueDependencies,
|
||||||
currentRecordFilters,
|
recordFilters: currentRecordFilters,
|
||||||
objectMetadataItem?.fields ?? [],
|
recordFilterGroups,
|
||||||
recordIndexViewFilterGroups,
|
fields: objectMetadataItem.fields,
|
||||||
);
|
});
|
||||||
|
|
||||||
const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts);
|
const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts);
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import { useSetTableColumns } from '@/object-record/record-table/hooks/useSetTab
|
|||||||
import { viewFieldAggregateOperationState } from '@/object-record/record-table/record-table-footer/states/viewFieldAggregateOperationState';
|
import { viewFieldAggregateOperationState } from '@/object-record/record-table/record-table-footer/states/viewFieldAggregateOperationState';
|
||||||
import { tableFiltersComponentState } from '@/object-record/record-table/states/tableFiltersComponentState';
|
import { tableFiltersComponentState } from '@/object-record/record-table/states/tableFiltersComponentState';
|
||||||
import { tableSortsComponentState } from '@/object-record/record-table/states/tableSortsComponentState';
|
import { tableSortsComponentState } from '@/object-record/record-table/states/tableSortsComponentState';
|
||||||
import { tableViewFilterGroupsComponentState } from '@/object-record/record-table/states/tableViewFilterGroupsComponentState';
|
|
||||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||||
import { convertAggregateOperationToExtendedAggregateOperation } from '@/object-record/utils/convertAggregateOperationToExtendedAggregateOperation';
|
import { convertAggregateOperationToExtendedAggregateOperation } from '@/object-record/utils/convertAggregateOperationToExtendedAggregateOperation';
|
||||||
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
|
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
|
||||||
@ -205,12 +204,7 @@ export const useLoadRecordIndexStates = () => {
|
|||||||
|
|
||||||
onViewFieldsChange(view.viewFields, objectMetadataItem, recordIndexId);
|
onViewFieldsChange(view.viewFields, objectMetadataItem, recordIndexId);
|
||||||
onViewGroupsChange(view.viewGroups, objectMetadataItem, recordIndexId);
|
onViewGroupsChange(view.viewGroups, objectMetadataItem, recordIndexId);
|
||||||
set(
|
|
||||||
tableViewFilterGroupsComponentState.atomFamily({
|
|
||||||
instanceId: recordIndexId,
|
|
||||||
}),
|
|
||||||
view.viewFilterGroups ?? [],
|
|
||||||
);
|
|
||||||
set(
|
set(
|
||||||
tableFiltersComponentState.atomFamily({
|
tableFiltersComponentState.atomFamily({
|
||||||
instanceId: recordIndexId,
|
instanceId: recordIndexId,
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export const RecordTableEmptyStateSoftDelete = () => {
|
|||||||
throw new Error('Deleted filter not found');
|
throw new Error('Deleted filter not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRecordFilter(deletedFilter.fieldMetadataId);
|
removeRecordFilter({ recordFilterId: deletedFilter.id });
|
||||||
|
|
||||||
toggleSoftDeleteFilterState(false);
|
toggleSoftDeleteFilterState(false);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,11 +3,12 @@ import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
|
|||||||
import { buildRecordGqlFieldsAggregateForView } from '@/object-record/record-board/record-board-column/utils/buildRecordGqlFieldsAggregateForView';
|
import { buildRecordGqlFieldsAggregateForView } from '@/object-record/record-board/record-board-column/utils/buildRecordGqlFieldsAggregateForView';
|
||||||
import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel';
|
import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel';
|
||||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||||
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
|
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
|
||||||
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
||||||
import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState';
|
import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState';
|
||||||
import { UserContext } from '@/users/contexts/UserContext';
|
import { UserContext } from '@/users/contexts/UserContext';
|
||||||
|
import { mapViewFilterGroupsToRecordFilterGroups } from '@/views/utils/mapViewFilterGroupsToRecordFilterGroups';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared';
|
import { isDefined } from 'twenty-shared';
|
||||||
@ -26,6 +27,10 @@ export const useAggregateRecordsForHeader = ({
|
|||||||
recordIndexViewFilterGroupsState,
|
recordIndexViewFilterGroupsState,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const recordFilterGroups = mapViewFilterGroupsToRecordFilterGroups(
|
||||||
|
recordIndexViewFilterGroups,
|
||||||
|
);
|
||||||
|
|
||||||
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
|
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
|
||||||
|
|
||||||
const recordIndexKanbanAggregateOperation = useRecoilValue(
|
const recordIndexKanbanAggregateOperation = useRecoilValue(
|
||||||
@ -36,12 +41,12 @@ export const useAggregateRecordsForHeader = ({
|
|||||||
|
|
||||||
const { dateFormat, timeFormat, timeZone } = useContext(UserContext);
|
const { dateFormat, timeFormat, timeZone } = useContext(UserContext);
|
||||||
|
|
||||||
const requestFilters = computeViewRecordGqlOperationFilter(
|
const requestFilters = computeRecordGqlOperationFilter({
|
||||||
filterValueDependencies,
|
filterValueDependencies,
|
||||||
recordIndexFilters,
|
recordFilters: recordIndexFilters,
|
||||||
objectMetadataItem.fields,
|
recordFilterGroups,
|
||||||
recordIndexViewFilterGroups,
|
fields: objectMetadataItem.fields,
|
||||||
);
|
});
|
||||||
|
|
||||||
const recordGqlFieldsAggregate = buildRecordGqlFieldsAggregateForView({
|
const recordGqlFieldsAggregate = buildRecordGqlFieldsAggregateForView({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
|
|||||||
import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel';
|
import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel';
|
||||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||||
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
||||||
import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState';
|
import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState';
|
||||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||||
@ -13,6 +13,7 @@ import { ExtendedAggregateOperations } from '@/object-record/record-table/types/
|
|||||||
import { convertAggregateOperationToExtendedAggregateOperation } from '@/object-record/utils/convertAggregateOperationToExtendedAggregateOperation';
|
import { convertAggregateOperationToExtendedAggregateOperation } from '@/object-record/utils/convertAggregateOperationToExtendedAggregateOperation';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { UserContext } from '@/users/contexts/UserContext';
|
import { UserContext } from '@/users/contexts/UserContext';
|
||||||
|
import { mapViewFilterGroupsToRecordFilterGroups } from '@/views/utils/mapViewFilterGroupsToRecordFilterGroups';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined, isFieldMetadataDateKind } from 'twenty-shared';
|
import { isDefined, isFieldMetadataDateKind } from 'twenty-shared';
|
||||||
@ -33,13 +34,17 @@ export const useAggregateRecordsForRecordTableColumnFooter = (
|
|||||||
|
|
||||||
const { filterValueDependencies } = useFilterValueDependencies();
|
const { filterValueDependencies } = useFilterValueDependencies();
|
||||||
|
|
||||||
const requestFilters = computeViewRecordGqlOperationFilter(
|
const recordFilterGroups = mapViewFilterGroupsToRecordFilterGroups(
|
||||||
filterValueDependencies,
|
|
||||||
currentRecordFilters,
|
|
||||||
objectMetadataItem.fields,
|
|
||||||
recordIndexViewFilterGroups,
|
recordIndexViewFilterGroups,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const requestFilters = computeRecordGqlOperationFilter({
|
||||||
|
fields: objectMetadataItem.fields,
|
||||||
|
filterValueDependencies,
|
||||||
|
recordFilterGroups,
|
||||||
|
recordFilters: currentRecordFilters,
|
||||||
|
});
|
||||||
|
|
||||||
const { viewFieldId } = useContext(
|
const { viewFieldId } = useContext(
|
||||||
RecordTableColumnAggregateFooterCellContext,
|
RecordTableColumnAggregateFooterCellContext,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
|
|
||||||
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
|
||||||
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
|
||||||
|
|
||||||
export const tableViewFilterGroupsComponentState = createComponentStateV2<
|
|
||||||
ViewFilterGroup[]
|
|
||||||
>({
|
|
||||||
key: 'tableViewFilterGroupsComponentState',
|
|
||||||
defaultValue: [],
|
|
||||||
componentInstanceContext: RecordTableComponentInstanceContext,
|
|
||||||
});
|
|
||||||
@ -29,10 +29,6 @@ export const AdvancedFilterDropdownButton = () => {
|
|||||||
|
|
||||||
const { removeRecordFilter } = useRemoveRecordFilter();
|
const { removeRecordFilter } = useRemoveRecordFilter();
|
||||||
|
|
||||||
const handleDropdownClickOutside = useCallback(() => {}, []);
|
|
||||||
|
|
||||||
const handleDropdownClose = () => {};
|
|
||||||
|
|
||||||
const removeAdvancedFilter = useCallback(async () => {
|
const removeAdvancedFilter = useCallback(async () => {
|
||||||
if (!advancedRecordFilterIds) {
|
if (!advancedRecordFilterIds) {
|
||||||
throw new Error('No advanced view filters to remove');
|
throw new Error('No advanced view filters to remove');
|
||||||
@ -48,7 +44,7 @@ export const AdvancedFilterDropdownButton = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const recordFilterId of advancedRecordFilterIds) {
|
for (const recordFilterId of advancedRecordFilterIds) {
|
||||||
removeRecordFilter(recordFilterId);
|
removeRecordFilter({ recordFilterId });
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
advancedRecordFilterIds,
|
advancedRecordFilterIds,
|
||||||
@ -83,8 +79,6 @@ export const AdvancedFilterDropdownButton = () => {
|
|||||||
dropdownOffset={{ y: 8, x: 0 }}
|
dropdownOffset={{ y: 8, x: 0 }}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
dropdownMenuWidth={800}
|
dropdownMenuWidth={800}
|
||||||
onClickOutside={handleDropdownClickOutside}
|
|
||||||
onClose={handleDropdownClose}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,11 +29,11 @@ export const EditableFilterDropdownButton = ({
|
|||||||
const handleRemove = () => {
|
const handleRemove = () => {
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
|
|
||||||
removeRecordFilter(viewFilter.fieldMetadataId);
|
removeRecordFilter({ recordFilterId: viewFilter.id });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDropdownClickOutside = useCallback(() => {
|
const handleDropdownClickOutside = useCallback(() => {
|
||||||
const { value, operand, fieldMetadataId } = viewFilter;
|
const { value, operand } = viewFilter;
|
||||||
if (
|
if (
|
||||||
!value &&
|
!value &&
|
||||||
![
|
![
|
||||||
@ -44,7 +44,7 @@ export const EditableFilterDropdownButton = ({
|
|||||||
RecordFilterOperand.IsToday,
|
RecordFilterOperand.IsToday,
|
||||||
].includes(operand)
|
].includes(operand)
|
||||||
) {
|
) {
|
||||||
removeRecordFilter(fieldMetadataId);
|
removeRecordFilter({ recordFilterId: viewFilter.id });
|
||||||
}
|
}
|
||||||
}, [viewFilter, removeRecordFilter]);
|
}, [viewFilter, removeRecordFilter]);
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export const RecordFilterChip = ({ recordFilter }: RecordFilterChipProps) => {
|
|||||||
const FieldMetadataItemIcon = getIcon(fieldMetadataItem.icon);
|
const FieldMetadataItemIcon = getIcon(fieldMetadataItem.icon);
|
||||||
|
|
||||||
const handleRemoveClick = () => {
|
const handleRemoveClick = () => {
|
||||||
removeRecordFilter(recordFilter.fieldMetadataId);
|
removeRecordFilter({ recordFilterId: recordFilter.id });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export const SoftDeleteFilterChip = ({
|
|||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
|
|
||||||
const handleRemoveClick = () => {
|
const handleRemoveClick = () => {
|
||||||
removeRecordFilter(recordFilter.fieldMetadataId);
|
removeRecordFilter({ recordFilterId: recordFilter.id });
|
||||||
|
|
||||||
setIsSoftDeleteFilterActive(false);
|
setIsSoftDeleteFilterActive(false);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRe
|
|||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||||
import { isDefined } from 'twenty-shared';
|
import { isDefined } from 'twenty-shared';
|
||||||
import { v4 } from 'uuid';
|
|
||||||
|
|
||||||
export const usePersistViewFilterRecords = () => {
|
export const usePersistViewFilterRecords = () => {
|
||||||
const { objectMetadataItem } = useObjectMetadataItem({
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
@ -51,7 +50,7 @@ export const usePersistViewFilterRecords = () => {
|
|||||||
mutation: createOneRecordMutation,
|
mutation: createOneRecordMutation,
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
id: v4(),
|
id: viewFilter.id,
|
||||||
fieldMetadataId: viewFilter.fieldMetadataId,
|
fieldMetadataId: viewFilter.fieldMetadataId,
|
||||||
viewId: view.id,
|
viewId: view.id,
|
||||||
value: viewFilter.value,
|
value: viewFilter.value,
|
||||||
|
|||||||
@ -52,6 +52,7 @@ export const usePersistViewSortRecords = () => {
|
|||||||
fieldMetadataId: viewSort.fieldMetadataId,
|
fieldMetadataId: viewSort.fieldMetadataId,
|
||||||
viewId: view.id,
|
viewId: view.id,
|
||||||
direction: viewSort.direction,
|
direction: viewSort.direction,
|
||||||
|
id: viewSort.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
update: (cache, { data }) => {
|
update: (cache, { data }) => {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { useSetViewInUrl } from '@/views/hooks/useSetViewInUrl';
|
|||||||
export const useChangeView = () => {
|
export const useChangeView = () => {
|
||||||
const { setViewInUrl } = useSetViewInUrl();
|
const { setViewInUrl } = useSetViewInUrl();
|
||||||
|
|
||||||
const changeView = async (viewId: string) => {
|
const changeView = (viewId: string) => {
|
||||||
setViewInUrl(viewId);
|
setViewInUrl(viewId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -59,8 +59,8 @@ export const useSaveRecordFilterGroupsToViewFilterGroups = () => {
|
|||||||
newViewFilterGroups,
|
newViewFilterGroups,
|
||||||
);
|
);
|
||||||
|
|
||||||
const viewFilterIdsToDelete = viewFilterGroupsToDelete.map(
|
const viewFilterGroupIdsToDelete = viewFilterGroupsToDelete.map(
|
||||||
(viewFilter) => viewFilter.id,
|
(viewFilterGroup) => viewFilterGroup.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
await createViewFilterGroupRecords(
|
await createViewFilterGroupRecords(
|
||||||
@ -68,7 +68,7 @@ export const useSaveRecordFilterGroupsToViewFilterGroups = () => {
|
|||||||
currentView,
|
currentView,
|
||||||
);
|
);
|
||||||
await updateViewFilterGroupRecords(viewFilterGroupsToUpdate);
|
await updateViewFilterGroupRecords(viewFilterGroupsToUpdate);
|
||||||
await deleteViewFilterGroupRecords(viewFilterIdsToDelete);
|
await deleteViewFilterGroupRecords(viewFilterGroupIdsToDelete);
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
createViewFilterGroupRecords,
|
createViewFilterGroupRecords,
|
||||||
|
|||||||
@ -17,8 +17,11 @@ describe('getViewFiltersToCreate', () => {
|
|||||||
it('should return all filters when current filters array is empty', () => {
|
it('should return all filters when current filters array is empty', () => {
|
||||||
const currentViewFilters: ViewFilter[] = [];
|
const currentViewFilters: ViewFilter[] = [];
|
||||||
const newViewFilters: ViewFilter[] = [
|
const newViewFilters: ViewFilter[] = [
|
||||||
{ ...baseFilter },
|
{ ...baseFilter } satisfies ViewFilter,
|
||||||
{ ...baseFilter, id: 'filter-2', fieldMetadataId: 'field-2' },
|
{
|
||||||
|
...baseFilter,
|
||||||
|
id: 'filter-2',
|
||||||
|
} satisfies ViewFilter,
|
||||||
];
|
];
|
||||||
|
|
||||||
const result = getViewFiltersToCreate(currentViewFilters, newViewFilters);
|
const result = getViewFiltersToCreate(currentViewFilters, newViewFilters);
|
||||||
@ -40,8 +43,7 @@ describe('getViewFiltersToCreate', () => {
|
|||||||
const newFilterWithDifferentFieldMetadata = {
|
const newFilterWithDifferentFieldMetadata = {
|
||||||
...baseFilter,
|
...baseFilter,
|
||||||
id: 'filter-2',
|
id: 'filter-2',
|
||||||
fieldMetadataId: 'field-2',
|
} satisfies ViewFilter;
|
||||||
};
|
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [existingFilter];
|
const currentViewFilters: ViewFilter[] = [existingFilter];
|
||||||
|
|
||||||
@ -55,25 +57,6 @@ describe('getViewFiltersToCreate', () => {
|
|||||||
expect(result).toEqual([newFilterWithDifferentFieldMetadata]);
|
expect(result).toEqual([newFilterWithDifferentFieldMetadata]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle filters with different viewFilterGroupIds', () => {
|
|
||||||
const existingFilter = { ...baseFilter };
|
|
||||||
const filterWithDifferentGroup = {
|
|
||||||
...baseFilter,
|
|
||||||
viewFilterGroupId: 'group-2',
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [existingFilter];
|
|
||||||
|
|
||||||
const newViewFilters: ViewFilter[] = [
|
|
||||||
existingFilter,
|
|
||||||
filterWithDifferentGroup,
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = getViewFiltersToCreate(currentViewFilters, newViewFilters);
|
|
||||||
|
|
||||||
expect(result).toEqual([filterWithDifferentGroup]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle empty arrays for both inputs', () => {
|
it('should handle empty arrays for both inputs', () => {
|
||||||
const currentViewFilters: ViewFilter[] = [];
|
const currentViewFilters: ViewFilter[] = [];
|
||||||
const newViewFilters: ViewFilter[] = [];
|
const newViewFilters: ViewFilter[] = [];
|
||||||
@ -82,36 +65,4 @@ describe('getViewFiltersToCreate', () => {
|
|||||||
|
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should consider filters with same fieldMetadataId but different viewFilterGroupId as new', () => {
|
|
||||||
const currentViewFilters: ViewFilter[] = [baseFilter];
|
|
||||||
const filterWithSameFieldMetadataIdButDifferentGroup = {
|
|
||||||
...baseFilter,
|
|
||||||
id: 'filter-2',
|
|
||||||
viewFilterGroupId: 'group-2',
|
|
||||||
};
|
|
||||||
const newViewFilters: ViewFilter[] = [
|
|
||||||
filterWithSameFieldMetadataIdButDifferentGroup,
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = getViewFiltersToCreate(currentViewFilters, newViewFilters);
|
|
||||||
|
|
||||||
expect(result).toEqual([filterWithSameFieldMetadataIdButDifferentGroup]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should consider filters with same viewFilterGroupId but different fieldMetadataId as new', () => {
|
|
||||||
const currentViewFilters: ViewFilter[] = [baseFilter];
|
|
||||||
const filterWithSameGroupButDifferentFieldMetadata = {
|
|
||||||
...baseFilter,
|
|
||||||
id: 'filter-2',
|
|
||||||
fieldMetadataId: 'field-2',
|
|
||||||
};
|
|
||||||
const newViewFilters: ViewFilter[] = [
|
|
||||||
filterWithSameGroupButDifferentFieldMetadata,
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = getViewFiltersToCreate(currentViewFilters, newViewFilters);
|
|
||||||
|
|
||||||
expect(result).toEqual([filterWithSameGroupButDifferentFieldMetadata]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -38,8 +38,7 @@ describe('getViewFiltersToDelete', () => {
|
|||||||
const filterToKeep = {
|
const filterToKeep = {
|
||||||
...baseFilter,
|
...baseFilter,
|
||||||
id: 'filter-2',
|
id: 'filter-2',
|
||||||
fieldMetadataId: 'field-2',
|
} satisfies ViewFilter;
|
||||||
};
|
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [filterToDelete, filterToKeep];
|
const currentViewFilters: ViewFilter[] = [filterToDelete, filterToKeep];
|
||||||
const newViewFilters: ViewFilter[] = [filterToKeep];
|
const newViewFilters: ViewFilter[] = [filterToKeep];
|
||||||
@ -57,43 +56,4 @@ describe('getViewFiltersToDelete', () => {
|
|||||||
|
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should identify filters to delete based on fieldMetadataId and viewFilterGroupId', () => {
|
|
||||||
const filterInGroup1 = { ...baseFilter };
|
|
||||||
const filterInGroup2 = {
|
|
||||||
...baseFilter,
|
|
||||||
viewFilterGroupId: 'group-2',
|
|
||||||
};
|
|
||||||
const filterWithDifferentField = {
|
|
||||||
...baseFilter,
|
|
||||||
fieldMetadataId: 'field-2',
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [
|
|
||||||
filterInGroup1,
|
|
||||||
filterInGroup2,
|
|
||||||
filterWithDifferentField,
|
|
||||||
];
|
|
||||||
const newViewFilters: ViewFilter[] = [filterInGroup1];
|
|
||||||
|
|
||||||
const result = getViewFiltersToDelete(currentViewFilters, newViewFilters);
|
|
||||||
|
|
||||||
expect(result).toEqual([filterInGroup2, filterWithDifferentField]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not delete filters that match in both fieldMetadataId and viewFilterGroupId', () => {
|
|
||||||
const existingFilter = { ...baseFilter };
|
|
||||||
const matchingFilter = {
|
|
||||||
...baseFilter,
|
|
||||||
value: 'different-value',
|
|
||||||
displayValue: 'different-value',
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [existingFilter];
|
|
||||||
const newViewFilters: ViewFilter[] = [matchingFilter];
|
|
||||||
|
|
||||||
const result = getViewFiltersToDelete(currentViewFilters, newViewFilters);
|
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -33,12 +33,12 @@ describe('getViewFiltersToUpdate', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return filters that exist in both arrays but have different values', () => {
|
it('should return filters that exist in both arrays but have different values', () => {
|
||||||
const existingFilter = { ...baseFilter };
|
const existingFilter = { ...baseFilter } satisfies ViewFilter;
|
||||||
const updatedFilter = {
|
const updatedFilter = {
|
||||||
...baseFilter,
|
...baseFilter,
|
||||||
value: 'updated-value',
|
value: 'updated-value',
|
||||||
displayValue: 'updated-value',
|
displayValue: 'updated-value',
|
||||||
};
|
} satisfies ViewFilter;
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [existingFilter];
|
const currentViewFilters: ViewFilter[] = [existingFilter];
|
||||||
const newViewFilters: ViewFilter[] = [updatedFilter];
|
const newViewFilters: ViewFilter[] = [updatedFilter];
|
||||||
@ -49,8 +49,8 @@ describe('getViewFiltersToUpdate', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not return filters that exist in both arrays with same values', () => {
|
it('should not return filters that exist in both arrays with same values', () => {
|
||||||
const existingFilter = { ...baseFilter };
|
const existingFilter = { ...baseFilter } satisfies ViewFilter;
|
||||||
const sameFilter = { ...baseFilter };
|
const sameFilter = { ...baseFilter } satisfies ViewFilter;
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [existingFilter];
|
const currentViewFilters: ViewFilter[] = [existingFilter];
|
||||||
const newViewFilters: ViewFilter[] = [sameFilter];
|
const newViewFilters: ViewFilter[] = [sameFilter];
|
||||||
@ -69,44 +69,12 @@ describe('getViewFiltersToUpdate', () => {
|
|||||||
expect(result).toEqual([]);
|
expect(result).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not update filters with same fieldMetadataId but different viewFilterGroupId', () => {
|
|
||||||
const existingFilter = { ...baseFilter };
|
|
||||||
const filterInDifferentGroup = {
|
|
||||||
...baseFilter,
|
|
||||||
viewFilterGroupId: 'group-2',
|
|
||||||
value: 'updated-value',
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [existingFilter];
|
|
||||||
const newViewFilters: ViewFilter[] = [filterInDifferentGroup];
|
|
||||||
|
|
||||||
const result = getViewFiltersToUpdate(currentViewFilters, newViewFilters);
|
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not update filters with same viewFilterGroupId but different fieldMetadataId', () => {
|
|
||||||
const existingFilter = { ...baseFilter };
|
|
||||||
const filterWithDifferentField = {
|
|
||||||
...baseFilter,
|
|
||||||
fieldMetadataId: 'field-2',
|
|
||||||
value: 'updated-value',
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [existingFilter];
|
|
||||||
const newViewFilters: ViewFilter[] = [filterWithDifferentField];
|
|
||||||
|
|
||||||
const result = getViewFiltersToUpdate(currentViewFilters, newViewFilters);
|
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update filter when operand changes', () => {
|
it('should update filter when operand changes', () => {
|
||||||
const existingFilter = { ...baseFilter };
|
const existingFilter = { ...baseFilter } satisfies ViewFilter;
|
||||||
const filterWithNewOperand = {
|
const filterWithNewOperand = {
|
||||||
...baseFilter,
|
...baseFilter,
|
||||||
operand: ViewFilterOperand.DoesNotContain,
|
operand: ViewFilterOperand.DoesNotContain,
|
||||||
};
|
} satisfies ViewFilter;
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [existingFilter];
|
const currentViewFilters: ViewFilter[] = [existingFilter];
|
||||||
const newViewFilters: ViewFilter[] = [filterWithNewOperand];
|
const newViewFilters: ViewFilter[] = [filterWithNewOperand];
|
||||||
@ -117,11 +85,11 @@ describe('getViewFiltersToUpdate', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update filter when position changes', () => {
|
it('should update filter when position changes', () => {
|
||||||
const existingFilter = { ...baseFilter };
|
const existingFilter = { ...baseFilter } satisfies ViewFilter;
|
||||||
const filterWithNewPosition = {
|
const filterWithNewPosition = {
|
||||||
...baseFilter,
|
...baseFilter,
|
||||||
positionInViewFilterGroup: 1,
|
positionInViewFilterGroup: 1,
|
||||||
};
|
} satisfies ViewFilter;
|
||||||
|
|
||||||
const currentViewFilters: ViewFilter[] = [existingFilter];
|
const currentViewFilters: ViewFilter[] = [existingFilter];
|
||||||
const newViewFilters: ViewFilter[] = [filterWithNewPosition];
|
const newViewFilters: ViewFilter[] = [filterWithNewPosition];
|
||||||
|
|||||||
@ -4,8 +4,9 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|||||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||||
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
||||||
|
|
||||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||||
import { View } from '@/views/types/View';
|
import { View } from '@/views/types/View';
|
||||||
|
import { mapViewFilterGroupsToRecordFilterGroups } from '@/views/utils/mapViewFilterGroupsToRecordFilterGroups';
|
||||||
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
|
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
|
||||||
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
|
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
|
||||||
import { isDefined } from 'twenty-shared';
|
import { isDefined } from 'twenty-shared';
|
||||||
@ -30,13 +31,22 @@ export const getQueryVariablesFromView = ({
|
|||||||
|
|
||||||
const { viewFilterGroups, viewFilters, viewSorts } = view;
|
const { viewFilterGroups, viewFilters, viewSorts } = view;
|
||||||
|
|
||||||
const filter = computeViewRecordGqlOperationFilter(
|
const recordFilterGroups = mapViewFilterGroupsToRecordFilterGroups(
|
||||||
filterValueDependencies,
|
|
||||||
mapViewFiltersToFilters(viewFilters, fieldMetadataItems),
|
|
||||||
objectMetadataItem?.fields ?? [],
|
|
||||||
viewFilterGroups ?? [],
|
viewFilterGroups ?? [],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const recordFilters = mapViewFiltersToFilters(
|
||||||
|
viewFilters,
|
||||||
|
fieldMetadataItems,
|
||||||
|
);
|
||||||
|
|
||||||
|
const filter = computeRecordGqlOperationFilter({
|
||||||
|
fields: objectMetadataItem?.fields ?? [],
|
||||||
|
filterValueDependencies,
|
||||||
|
recordFilterGroups,
|
||||||
|
recordFilters,
|
||||||
|
});
|
||||||
|
|
||||||
const orderBy = turnSortsIntoOrderBy(
|
const orderBy = turnSortsIntoOrderBy(
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
mapViewSortsToSorts(viewSorts),
|
mapViewSortsToSorts(viewSorts),
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||||
import { isDefined } from 'twenty-shared';
|
import { isDefined } from 'twenty-shared';
|
||||||
|
import { compareStrictlyExceptForNullAndUndefined } from '~/utils/compareStrictlyExceptForNullAndUndefined';
|
||||||
|
|
||||||
export const getViewFiltersToCreate = (
|
export const getViewFiltersToCreate = (
|
||||||
currentViewFilters: ViewFilter[],
|
currentViewFilters: ViewFilter[],
|
||||||
@ -8,8 +9,10 @@ export const getViewFiltersToCreate = (
|
|||||||
return newViewFilters.filter((newViewFilter) => {
|
return newViewFilters.filter((newViewFilter) => {
|
||||||
const correspondingViewFilter = currentViewFilters.find(
|
const correspondingViewFilter = currentViewFilters.find(
|
||||||
(currentViewFilter) =>
|
(currentViewFilter) =>
|
||||||
currentViewFilter.fieldMetadataId === newViewFilter.fieldMetadataId &&
|
compareStrictlyExceptForNullAndUndefined(
|
||||||
currentViewFilter.viewFilterGroupId === newViewFilter.viewFilterGroupId,
|
currentViewFilter.id,
|
||||||
|
newViewFilter.id,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const shouldCreateBecauseViewFilterIsNew = !isDefined(
|
const shouldCreateBecauseViewFilterIsNew = !isDefined(
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||||
|
import { compareStrictlyExceptForNullAndUndefined } from '~/utils/compareStrictlyExceptForNullAndUndefined';
|
||||||
|
|
||||||
export const getViewFiltersToDelete = (
|
export const getViewFiltersToDelete = (
|
||||||
currentViewFilters: ViewFilter[],
|
currentViewFilters: ViewFilter[],
|
||||||
@ -6,11 +7,11 @@ export const getViewFiltersToDelete = (
|
|||||||
) => {
|
) => {
|
||||||
return currentViewFilters.filter(
|
return currentViewFilters.filter(
|
||||||
(currentViewFilter) =>
|
(currentViewFilter) =>
|
||||||
!newViewFilters.some(
|
!newViewFilters.some((newViewFilter) =>
|
||||||
(newViewFilter) =>
|
compareStrictlyExceptForNullAndUndefined(
|
||||||
newViewFilter.fieldMetadataId === currentViewFilter.fieldMetadataId &&
|
currentViewFilter.id,
|
||||||
newViewFilter.viewFilterGroupId ===
|
newViewFilter.id,
|
||||||
currentViewFilter.viewFilterGroupId,
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||||
import { areViewFiltersEqual } from '@/views/utils/areViewFiltersEqual';
|
import { areViewFiltersEqual } from '@/views/utils/areViewFiltersEqual';
|
||||||
import { isDefined } from 'twenty-shared';
|
import { isDefined } from 'twenty-shared';
|
||||||
|
import { compareStrictlyExceptForNullAndUndefined } from '~/utils/compareStrictlyExceptForNullAndUndefined';
|
||||||
|
|
||||||
export const getViewFiltersToUpdate = (
|
export const getViewFiltersToUpdate = (
|
||||||
currentViewFilters: ViewFilter[],
|
currentViewFilters: ViewFilter[],
|
||||||
@ -9,8 +10,10 @@ export const getViewFiltersToUpdate = (
|
|||||||
return newViewFilters.filter((newViewFilter) => {
|
return newViewFilters.filter((newViewFilter) => {
|
||||||
const correspondingViewFilter = currentViewFilters.find(
|
const correspondingViewFilter = currentViewFilters.find(
|
||||||
(currentViewFilter) =>
|
(currentViewFilter) =>
|
||||||
currentViewFilter.fieldMetadataId === newViewFilter.fieldMetadataId &&
|
compareStrictlyExceptForNullAndUndefined(
|
||||||
currentViewFilter.viewFilterGroupId === newViewFilter.viewFilterGroupId,
|
currentViewFilter.id,
|
||||||
|
newViewFilter.id,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isDefined(correspondingViewFilter)) {
|
if (!isDefined(correspondingViewFilter)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user