Fix filters + add behaviour for select (#13997)
On field selection, we need to retrieve the fieldMetadata to set the filter type. Current code was using the current step filter fieldMetdataId instead of the new one. Also adding a special behaviour for Select filter: it behaves as arrays/multiselect but the opearands are not the same. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+5
-17
@@ -1,24 +1,12 @@
|
||||
import { CustomError } from '@/error-handler/CustomError';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getFieldMetadataItemById } from '@/object-metadata/utils/getFieldMetadataItemById';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const FIELD_METADATA_ITEM_NOT_FOUND_ERROR_CODE =
|
||||
'FIELD_METADATA_ITEM_NOT_FOUND';
|
||||
|
||||
export const useFieldMetadataItemById = (fieldMetadataId: string) => {
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const fieldMetadataItem = objectMetadataItems
|
||||
.flatMap((objectMetadataItem) => objectMetadataItem.fields)
|
||||
.find((field) => field.id === fieldMetadataId);
|
||||
|
||||
if (!isDefined(fieldMetadataItem)) {
|
||||
throw new CustomError(
|
||||
`Field metadata item not found for id ${fieldMetadataId}`,
|
||||
FIELD_METADATA_ITEM_NOT_FOUND_ERROR_CODE,
|
||||
);
|
||||
}
|
||||
|
||||
return { fieldMetadataItem };
|
||||
return getFieldMetadataItemById({
|
||||
fieldMetadataId,
|
||||
objectMetadataItems,
|
||||
});
|
||||
};
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getFieldMetadataItemByIdOrThrow } from '@/object-metadata/utils/getFieldMetadataItemByIdOrThrow';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const useFieldMetadataItemByIdOrThrow = (fieldMetadataId: string) => {
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
return getFieldMetadataItemByIdOrThrow({
|
||||
fieldMetadataId,
|
||||
objectMetadataItems,
|
||||
});
|
||||
};
|
||||
+26
-20
@@ -1,23 +1,29 @@
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getFieldMetadataItemByIdOrThrow } from '@/object-metadata/utils/getFieldMetadataItemByIdOrThrow';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
export const useGetFieldMetadataItemById = () => {
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const getFieldMetadataItemById = (fieldMetadataId: string) => {
|
||||
const fieldMetadataItem = objectMetadataItems
|
||||
.flatMap((objectMetadataItem) => objectMetadataItem.fields)
|
||||
.find((field) => field.id === fieldMetadataId);
|
||||
|
||||
if (!isDefined(fieldMetadataItem)) {
|
||||
throw new Error(
|
||||
`Field metadata item not found for id ${fieldMetadataId}`,
|
||||
);
|
||||
}
|
||||
|
||||
return fieldMetadataItem;
|
||||
};
|
||||
|
||||
return { getFieldMetadataItemById };
|
||||
type GetFieldMetadataItemByIdOrThrowResult = {
|
||||
fieldMetadataItem: FieldMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
};
|
||||
|
||||
export const useGetFieldMetadataItemByIdOrThrow = () => {
|
||||
const getFieldMetadataItemById = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(fieldMetadataId: string): GetFieldMetadataItemByIdOrThrowResult => {
|
||||
const objectMetadataItems = snapshot
|
||||
.getLoadable(objectMetadataItemsState)
|
||||
.getValue();
|
||||
|
||||
return getFieldMetadataItemByIdOrThrow({
|
||||
fieldMetadataId,
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return { getFieldMetadataItemByIdOrThrow: getFieldMetadataItemById };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getFieldMetadataItemByIdOrThrow } from '@/object-metadata/utils/getFieldMetadataItemByIdOrThrow';
|
||||
|
||||
type GetFieldMetadataItemByIdParams = {
|
||||
fieldMetadataId: string;
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
};
|
||||
|
||||
export const getFieldMetadataItemById = ({
|
||||
fieldMetadataId,
|
||||
objectMetadataItems,
|
||||
}: GetFieldMetadataItemByIdParams) => {
|
||||
try {
|
||||
return getFieldMetadataItemByIdOrThrow({
|
||||
fieldMetadataId,
|
||||
objectMetadataItems,
|
||||
});
|
||||
} catch {
|
||||
return {
|
||||
fieldMetadataItem: undefined,
|
||||
objectMetadataItem: undefined,
|
||||
};
|
||||
}
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { CustomError } from '@/error-handler/CustomError';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type GetFieldMetadataItemByIdParams = {
|
||||
fieldMetadataId: string;
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
};
|
||||
|
||||
const FIELD_METADATA_ITEM_NOT_FOUND_ERROR_CODE =
|
||||
'FIELD_METADATA_ITEM_NOT_FOUND';
|
||||
|
||||
export const getFieldMetadataItemByIdOrThrow = ({
|
||||
fieldMetadataId,
|
||||
objectMetadataItems,
|
||||
}: GetFieldMetadataItemByIdParams) => {
|
||||
const objectMetadataItem = objectMetadataItems.find((objectMetadataItem) =>
|
||||
objectMetadataItem.fields.some((field) => field.id === fieldMetadataId),
|
||||
);
|
||||
|
||||
if (!isDefined(objectMetadataItem)) {
|
||||
throw new CustomError(
|
||||
`Object metadata item not found for field id ${fieldMetadataId}`,
|
||||
FIELD_METADATA_ITEM_NOT_FOUND_ERROR_CODE,
|
||||
);
|
||||
}
|
||||
|
||||
const fieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.id === fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!isDefined(fieldMetadataItem)) {
|
||||
throw new CustomError(
|
||||
`Field metadata item not found for field id ${fieldMetadataId}`,
|
||||
FIELD_METADATA_ITEM_NOT_FOUND_ERROR_CODE,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
fieldMetadataItem,
|
||||
objectMetadataItem,
|
||||
};
|
||||
};
|
||||
+4
-6
@@ -1,4 +1,3 @@
|
||||
import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
||||
import { getAdvancedFilterInputPlaceholderText } from '@/object-record/advanced-filter/utils/getAdvancedFilterInputPlacedholderText';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { SelectControl } from '@/ui/input/components/SelectControl';
|
||||
@@ -8,6 +7,7 @@ import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
// TODO: factorize this with https://github.com/twentyhq/core-team-issues/issues/752
|
||||
@@ -48,11 +48,9 @@ export const AdvancedFilterValueInputDropdownButtonClickableSelect = ({
|
||||
|
||||
const shouldUsePlaceholder = !isNonEmptyString(recordFilter?.value);
|
||||
|
||||
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
|
||||
|
||||
const fieldMetadataItem = isNonEmptyString(recordFilter?.fieldMetadataId)
|
||||
? getFieldMetadataItemById(recordFilter?.fieldMetadataId)
|
||||
: undefined;
|
||||
const { fieldMetadataItem } = useFieldMetadataItemById(
|
||||
recordFilter?.fieldMetadataId ?? '',
|
||||
);
|
||||
|
||||
const placeholderText = isDefined(fieldMetadataItem)
|
||||
? getAdvancedFilterInputPlaceholderText(fieldMetadataItem)
|
||||
|
||||
+4
-6
@@ -1,4 +1,4 @@
|
||||
import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
|
||||
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
@@ -17,11 +17,9 @@ export const useRecordFilterField = (recordFilterId: string) => {
|
||||
(recordFilter) => recordFilter.id === recordFilterId,
|
||||
);
|
||||
|
||||
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
|
||||
|
||||
const fieldMetadataItem = isNonEmptyString(recordFilter?.fieldMetadataId)
|
||||
? getFieldMetadataItemById(recordFilter?.fieldMetadataId)
|
||||
: undefined;
|
||||
const { fieldMetadataItem } = useFieldMetadataItemById(
|
||||
recordFilter?.fieldMetadataId ?? '',
|
||||
);
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
|
||||
+5
-3
@@ -1,4 +1,4 @@
|
||||
import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
||||
import { useGetFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||
@@ -45,7 +45,8 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
||||
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
|
||||
const { getFieldMetadataItemByIdOrThrow } =
|
||||
useGetFieldMetadataItemByIdOrThrow();
|
||||
|
||||
const setSubFieldNameUsedInDropdown = useSetRecoilComponentState(
|
||||
subFieldNameUsedInDropdownComponentState,
|
||||
@@ -64,7 +65,8 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
||||
}: SelectFilterParams) => {
|
||||
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItemId);
|
||||
|
||||
const fieldMetadataItem = getFieldMetadataItemById(fieldMetadataItemId);
|
||||
const { fieldMetadataItem } =
|
||||
getFieldMetadataItemByIdOrThrow(fieldMetadataItemId);
|
||||
|
||||
if (!isDefined(fieldMetadataItem)) {
|
||||
return;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { useFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useFieldMetadataItemByIdOrThrow';
|
||||
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
|
||||
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
@@ -21,7 +21,7 @@ export const EditableFilterChip = ({
|
||||
}: EditableFilterChipProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const { fieldMetadataItem } = useFieldMetadataItemById(
|
||||
const { fieldMetadataItem } = useFieldMetadataItemByIdOrThrow(
|
||||
recordFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { useFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useFieldMetadataItemByIdOrThrow';
|
||||
import { useRemoveRecordSort } from '@/object-record/record-sort/hooks/useRemoveRecordSort';
|
||||
import { useUpsertRecordSort } from '@/object-record/record-sort/hooks/useUpsertRecordSort';
|
||||
import { type RecordSort } from '@/object-record/record-sort/types/RecordSort';
|
||||
@@ -18,7 +18,7 @@ export const EditableSortChip = ({ recordSort }: EditableSortChipProps) => {
|
||||
removeRecordSort(recordSort.fieldMetadataId);
|
||||
};
|
||||
|
||||
const { fieldMetadataItem } = useFieldMetadataItemById(
|
||||
const { fieldMetadataItem } = useFieldMetadataItemByIdOrThrow(
|
||||
recordSort.fieldMetadataId,
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
import { type DropResult, type ResponderProvided } from '@hello-pangea/dnd';
|
||||
|
||||
import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
||||
import { useGetFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
||||
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
|
||||
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
|
||||
import { useObjectOptionsForTable } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForTable';
|
||||
@@ -32,7 +32,8 @@ export const ViewFieldsVisibleDropdownSection = () => {
|
||||
viewBarId: recordIndexId,
|
||||
});
|
||||
|
||||
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
|
||||
const { getFieldMetadataItemByIdOrThrow } =
|
||||
useGetFieldMetadataItemByIdOrThrow();
|
||||
|
||||
const handleReorderFields =
|
||||
viewType === ViewType.Kanban
|
||||
@@ -93,7 +94,7 @@ export const ViewFieldsVisibleDropdownSection = () => {
|
||||
index +
|
||||
(isDefined(fieldMetadataItemLabelIdentifier) ? 1 : 0);
|
||||
|
||||
const fieldMetadataItem = getFieldMetadataItemById(
|
||||
const { fieldMetadataItem } = getFieldMetadataItemByIdOrThrow(
|
||||
recordField.fieldMetadataItemId,
|
||||
);
|
||||
|
||||
|
||||
+40
-28
@@ -1,8 +1,9 @@
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { useGetFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
||||
import { SelectControl } from '@/ui/input/components/SelectControl';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { useWorkflowVersionIdOrThrow } from '@/workflow/hooks/useWorkflowVersionIdOrThrow';
|
||||
import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/stepsOutputSchemaFamilySelector';
|
||||
import { useFilterFieldMetadataItem } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useFilterFieldMetadataItem';
|
||||
import { useUpsertStepFilterSettings } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useUpsertStepFilterSettings';
|
||||
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/context/WorkflowStepFilterContext';
|
||||
import { getViewFilterOperands } from '@/workflow/workflow-steps/workflow-actions/filter-action/utils/getStepFilterOperands';
|
||||
@@ -25,6 +26,8 @@ type WorkflowStepFilterFieldSelectProps = {
|
||||
const NON_SELECTABLE_FIELD_TYPES = [
|
||||
FieldMetadataType.ACTOR,
|
||||
FieldMetadataType.RICH_TEXT_V2,
|
||||
FieldMetadataType.ARRAY,
|
||||
FieldMetadataType.RATING,
|
||||
];
|
||||
|
||||
export const WorkflowStepFilterFieldSelect = ({
|
||||
@@ -53,10 +56,11 @@ export const WorkflowStepFilterFieldSelect = ({
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const {
|
||||
fieldMetadataItem: filterFieldMetadataItem,
|
||||
objectMetadataItem: filterObjectMetadataItem,
|
||||
} = useFilterFieldMetadataItem(stepFilter.fieldMetadataId ?? '');
|
||||
const { fieldMetadataItem: filterFieldMetadataItem } =
|
||||
useFieldMetadataItemById(stepFilter.fieldMetadataId ?? '');
|
||||
|
||||
const { getFieldMetadataItemByIdOrThrow } =
|
||||
useGetFieldMetadataItemByIdOrThrow();
|
||||
|
||||
const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep({
|
||||
shouldDisplayRecordFields,
|
||||
@@ -92,6 +96,13 @@ export const WorkflowStepFilterFieldSelect = ({
|
||||
isFullRecord: false,
|
||||
});
|
||||
|
||||
const {
|
||||
fieldMetadataItem: filterFieldMetadataItem,
|
||||
objectMetadataItem: filterObjectMetadataItem,
|
||||
} = isDefined(fieldMetadataId)
|
||||
? getFieldMetadataItemByIdOrThrow(fieldMetadataId)
|
||||
: { fieldMetadataItem: undefined, objectMetadataItem: undefined };
|
||||
|
||||
const filterType = isDefined(fieldMetadataId)
|
||||
? (filterFieldMetadataItem?.type ?? 'unknown')
|
||||
: variableType;
|
||||
@@ -119,11 +130,10 @@ export const WorkflowStepFilterFieldSelect = ({
|
||||
});
|
||||
},
|
||||
[
|
||||
workflowVersionId,
|
||||
getFieldMetadataItemByIdOrThrow,
|
||||
upsertStepFilterSettings,
|
||||
stepFilter,
|
||||
workflowVersionId,
|
||||
filterFieldMetadataItem,
|
||||
filterObjectMetadataItem,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -185,25 +195,27 @@ export const WorkflowStepFilterFieldSelect = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<WorkflowVariablesDropdown
|
||||
instanceId={dropdownId}
|
||||
onVariableSelect={handleChange}
|
||||
clickableComponent={
|
||||
<SelectControl
|
||||
selectedOption={{
|
||||
value: stepFilter.stepOutputKey,
|
||||
label: label,
|
||||
Icon: filterFieldMetadataItem?.icon
|
||||
? getIcon(filterFieldMetadataItem.icon)
|
||||
: undefined,
|
||||
}}
|
||||
textAccent={isSelectedFieldNotFound ? 'placeholder' : 'default'}
|
||||
/>
|
||||
}
|
||||
shouldDisplayRecordFields={shouldDisplayRecordFields}
|
||||
shouldDisplayRecordObjects={shouldDisplayRecordObjects}
|
||||
shouldEnableSelectRelationObject={true}
|
||||
fieldTypesToExclude={NON_SELECTABLE_FIELD_TYPES}
|
||||
/>
|
||||
<>
|
||||
<WorkflowVariablesDropdown
|
||||
instanceId={dropdownId}
|
||||
onVariableSelect={handleChange}
|
||||
clickableComponent={
|
||||
<SelectControl
|
||||
selectedOption={{
|
||||
value: stepFilter.stepOutputKey,
|
||||
label: label,
|
||||
Icon: filterFieldMetadataItem?.icon
|
||||
? getIcon(filterFieldMetadataItem.icon)
|
||||
: undefined,
|
||||
}}
|
||||
textAccent={isSelectedFieldNotFound ? 'placeholder' : 'default'}
|
||||
/>
|
||||
}
|
||||
shouldDisplayRecordFields={shouldDisplayRecordFields}
|
||||
shouldDisplayRecordObjects={shouldDisplayRecordObjects}
|
||||
shouldEnableSelectRelationObject={true}
|
||||
fieldTypesToExclude={NON_SELECTABLE_FIELD_TYPES}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,3 +1,4 @@
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { configurableViewFilterOperands } from '@/object-record/object-filter-dropdown/utils/configurableViewFilterOperands';
|
||||
import { FormFieldInput } from '@/object-record/record-field/ui/components/FormFieldInput';
|
||||
import { FormMultiSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormMultiSelectFieldInput';
|
||||
@@ -7,7 +8,6 @@ import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/c
|
||||
import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
|
||||
import { WorkflowStepFilterValueCompositeInput } from '@/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueCompositeInput';
|
||||
import { useFilterFieldMetadataItem } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useFilterFieldMetadataItem';
|
||||
import { useUpsertStepFilterSettings } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useUpsertStepFilterSettings';
|
||||
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/context/WorkflowStepFilterContext';
|
||||
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
|
||||
@@ -91,7 +91,7 @@ export const WorkflowStepFilterValueInput = ({
|
||||
} = stepFilter;
|
||||
|
||||
const { fieldMetadataItem: selectedFieldMetadataItem, objectMetadataItem } =
|
||||
useFilterFieldMetadataItem(fieldMetadataId ?? '');
|
||||
useFieldMetadataItemById(fieldMetadataId ?? '');
|
||||
|
||||
if (isDisabled || operandHasNoInput) {
|
||||
return null;
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
|
||||
export const useFilterFieldMetadataItem = (fieldMetadataId: string) => {
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const objectMetadataItem = objectMetadataItems.find((objectMetadataItem) =>
|
||||
objectMetadataItem.fields.some((field) => field.id === fieldMetadataId),
|
||||
);
|
||||
|
||||
const fieldMetadataItem = objectMetadataItem?.fields.find(
|
||||
(field) => field.id === fieldMetadataId,
|
||||
);
|
||||
|
||||
return {
|
||||
objectMetadataItem,
|
||||
fieldMetadataItem,
|
||||
};
|
||||
};
|
||||
+74
@@ -314,6 +314,80 @@ describe('evaluateFilterConditions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Select filter operands', () => {
|
||||
it('should return true when there are common values (SELECT)', () => {
|
||||
const filter = createFilter(
|
||||
ViewFilterOperand.Is,
|
||||
['John'],
|
||||
['John', 'Jane'],
|
||||
'SELECT',
|
||||
);
|
||||
const result = evaluateFilterConditions({ filters: [filter] });
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when there are no common values (SELECT)', () => {
|
||||
const filter = createFilter(
|
||||
ViewFilterOperand.Is,
|
||||
['John'],
|
||||
['Jane'],
|
||||
'SELECT',
|
||||
);
|
||||
const result = evaluateFilterConditions({ filters: [filter] });
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when there are no common values (IsNot)', () => {
|
||||
const filter = createFilter(
|
||||
ViewFilterOperand.IsNot,
|
||||
['John'],
|
||||
['Jane'],
|
||||
'SELECT',
|
||||
);
|
||||
const result = evaluateFilterConditions({ filters: [filter] });
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when there are no values (IsEmpty)', () => {
|
||||
const filter = createFilter(
|
||||
ViewFilterOperand.IsEmpty,
|
||||
[],
|
||||
'',
|
||||
'SELECT',
|
||||
);
|
||||
const result = evaluateFilterConditions({ filters: [filter] });
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when there is a value (IsEmpty)', () => {
|
||||
const filter = createFilter(
|
||||
ViewFilterOperand.IsEmpty,
|
||||
['John'],
|
||||
'',
|
||||
'SELECT',
|
||||
);
|
||||
const result = evaluateFilterConditions({ filters: [filter] });
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when there are values (IsNotEmpty)', () => {
|
||||
const filter = createFilter(
|
||||
ViewFilterOperand.IsNotEmpty,
|
||||
['John'],
|
||||
'',
|
||||
'SELECT',
|
||||
);
|
||||
const result = evaluateFilterConditions({ filters: [filter] });
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Boolean filter operands', () => {
|
||||
it('should return true when boolean values are equal', () => {
|
||||
const filter = createFilter(
|
||||
|
||||
+39
-38
@@ -1,4 +1,9 @@
|
||||
import { isObject, isString } from '@sniptt/guards';
|
||||
import {
|
||||
isNonEmptyArray,
|
||||
isNonEmptyString,
|
||||
isObject,
|
||||
isString,
|
||||
} from '@sniptt/guards';
|
||||
import {
|
||||
type StepFilter,
|
||||
type StepFilterGroup,
|
||||
@@ -21,7 +26,6 @@ function evaluateFilter(filter: ResolvedFilter): boolean {
|
||||
case 'DATE_TIME':
|
||||
return evaluateDateFilter(filter);
|
||||
case 'TEXT':
|
||||
case 'SELECT':
|
||||
case 'MULTI_SELECT':
|
||||
case 'FULL_NAME':
|
||||
case 'EMAILS':
|
||||
@@ -31,6 +35,8 @@ function evaluateFilter(filter: ResolvedFilter): boolean {
|
||||
case 'ARRAY':
|
||||
case 'RAW_JSON':
|
||||
return evaluateTextAndArrayFilter(filter);
|
||||
case 'SELECT':
|
||||
return evaluateSelectFilter(filter);
|
||||
case 'BOOLEAN':
|
||||
return evaluateBooleanFilter(filter);
|
||||
case 'UUID':
|
||||
@@ -121,20 +127,11 @@ function evaluateTextAndArrayFilter(filter: ResolvedFilter): boolean {
|
||||
case ViewFilterOperand.DoesNotContain:
|
||||
return !contains(filter.leftOperand, filter.rightOperand);
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
return (
|
||||
filter.leftOperand === null ||
|
||||
filter.leftOperand === undefined ||
|
||||
filter.leftOperand === '' ||
|
||||
(Array.isArray(filter.leftOperand) && filter.leftOperand.length === 0)
|
||||
);
|
||||
return !isNotEmptyTextOrArray(filter.leftOperand);
|
||||
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
return (
|
||||
filter.leftOperand !== null &&
|
||||
filter.leftOperand !== undefined &&
|
||||
filter.leftOperand !== '' &&
|
||||
(!Array.isArray(filter.leftOperand) || filter.leftOperand.length > 0)
|
||||
);
|
||||
return isNotEmptyTextOrArray(filter.leftOperand);
|
||||
|
||||
default:
|
||||
throw new Error(
|
||||
`Operand ${filter.operand} not supported for this filter type`,
|
||||
@@ -142,6 +139,10 @@ function evaluateTextAndArrayFilter(filter: ResolvedFilter): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
function isNotEmptyTextOrArray(value: unknown): boolean {
|
||||
return isNonEmptyString(value) || isNonEmptyArray(value);
|
||||
}
|
||||
|
||||
function evaluateBooleanFilter(filter: ResolvedFilter): boolean {
|
||||
switch (filter.operand) {
|
||||
case ViewFilterOperand.Is:
|
||||
@@ -255,17 +256,9 @@ function evaluateCurrencyFilter(filter: ResolvedFilter): boolean {
|
||||
case ViewFilterOperand.IsNot:
|
||||
return filter.leftOperand !== filter.rightOperand;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
return (
|
||||
filter.leftOperand === null ||
|
||||
filter.leftOperand === undefined ||
|
||||
filter.leftOperand === ''
|
||||
);
|
||||
return !isNonEmptyString(filter.leftOperand);
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
return (
|
||||
filter.leftOperand !== null &&
|
||||
filter.leftOperand !== undefined &&
|
||||
filter.leftOperand !== ''
|
||||
);
|
||||
return isNonEmptyString(filter.leftOperand);
|
||||
default:
|
||||
throw new Error(
|
||||
`Operand ${filter.operand} not supported for currency filter`,
|
||||
@@ -288,10 +281,10 @@ function evaluateNumberFilter(filter: ResolvedFilter): boolean {
|
||||
return Number(leftValue) <= Number(rightValue);
|
||||
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
return leftValue === null || leftValue === undefined || leftValue === '';
|
||||
return !isNonEmptyString(leftValue);
|
||||
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
return leftValue !== null && leftValue !== undefined && leftValue !== '';
|
||||
return isNonEmptyString(leftValue);
|
||||
|
||||
default:
|
||||
throw new Error(
|
||||
@@ -310,19 +303,9 @@ function evaluateDefaultFilter(filter: ResolvedFilter): boolean {
|
||||
case ViewFilterOperand.IsNot:
|
||||
return leftValue != rightValue;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
return (
|
||||
leftValue === null ||
|
||||
leftValue === undefined ||
|
||||
leftValue === '' ||
|
||||
(Array.isArray(leftValue) && leftValue.length === 0)
|
||||
);
|
||||
return !isNotEmptyTextOrArray(leftValue);
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
return (
|
||||
leftValue !== null &&
|
||||
leftValue !== undefined &&
|
||||
leftValue !== '' &&
|
||||
(!Array.isArray(leftValue) || leftValue.length > 0)
|
||||
);
|
||||
return isNotEmptyTextOrArray(leftValue);
|
||||
case ViewFilterOperand.Contains:
|
||||
return contains(leftValue, rightValue);
|
||||
case ViewFilterOperand.DoesNotContain:
|
||||
@@ -338,6 +321,24 @@ function evaluateDefaultFilter(filter: ResolvedFilter): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
function evaluateSelectFilter(filter: ResolvedFilter): boolean {
|
||||
switch (filter.operand) {
|
||||
case ViewFilterOperand.Is:
|
||||
return contains(filter.leftOperand, filter.rightOperand);
|
||||
case ViewFilterOperand.IsNot:
|
||||
return !contains(filter.leftOperand, filter.rightOperand);
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
return !isNotEmptyTextOrArray(filter.leftOperand);
|
||||
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
return isNotEmptyTextOrArray(filter.leftOperand);
|
||||
default:
|
||||
throw new Error(
|
||||
`Operand ${filter.operand} not supported for select filter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function evaluateFilterConditions({
|
||||
filterGroups = [],
|
||||
filters = [],
|
||||
|
||||
@@ -170,4 +170,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user