Tt filter step input per variable type (#13371)
- add fieldMetadataId to step output schema - use it to display FormFieldInput in Filter input - few fixes for a few fields Next step: - Handle composite fields - Design review
This commit is contained in:
@ -47,6 +47,7 @@ import { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isF
|
||||
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
|
||||
import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText';
|
||||
import { isFieldUuid } from '@/object-record/record-field/types/guards/isFieldUuid';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { JsonValue } from 'type-fest';
|
||||
|
||||
type FormFieldInputProps = {
|
||||
@ -70,7 +71,7 @@ export const FormFieldInput = ({
|
||||
error,
|
||||
onError,
|
||||
}: FormFieldInputProps) => {
|
||||
return isFieldNumber(field) ? (
|
||||
return isFieldNumber(field) || field.type === FieldMetadataType.NUMERIC ? (
|
||||
<FormNumberFieldInput
|
||||
label={field.label}
|
||||
defaultValue={defaultValue as string | number | undefined}
|
||||
|
||||
@ -48,7 +48,7 @@ export const FormBooleanFieldInput = ({
|
||||
}
|
||||
: {
|
||||
type: 'static',
|
||||
value: defaultValue ?? false,
|
||||
value: Boolean(defaultValue),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useLis
|
||||
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
|
||||
import { css } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { ChangeEvent, KeyboardEvent, useId, useRef, useState } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -106,9 +107,10 @@ export const FormDateTimeFieldInput = ({
|
||||
},
|
||||
);
|
||||
|
||||
const draftValueAsDate = isDefined(draftValue.value)
|
||||
? new Date(draftValue.value)
|
||||
: null;
|
||||
const draftValueAsDate =
|
||||
isDefined(draftValue.value) && isNonEmptyString(draftValue.value)
|
||||
? new Date(draftValue.value)
|
||||
: null;
|
||||
|
||||
const [pickerDate, setPickerDate] =
|
||||
useState<Nullable<Date>>(draftValueAsDate);
|
||||
|
||||
@ -10,7 +10,9 @@ import { useChildStepFiltersAndChildStepFilterGroups } from '@/workflow/workflow
|
||||
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/context/WorkflowStepFilterContext';
|
||||
import { rootLevelStepFilterGroupComponentSelector } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/rootLevelStepFilterGroupComponentSelector';
|
||||
import { isStepFilterGroupChildAStepFilterGroup } from '@/workflow/workflow-steps/workflow-actions/filter-action/utils/isStepFilterGroupChildAStepFilterGroup';
|
||||
import { useAvailableVariablesInWorkflowStep } from '@/workflow/workflow-variables/hooks/useAvailableVariablesInWorkflowStep';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
@ -27,6 +29,10 @@ const StyledChildContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledDangerContainer = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.danger};
|
||||
`;
|
||||
|
||||
type WorkflowEditActionFilterBodyProps = {
|
||||
action: WorkflowFilterAction;
|
||||
actionOptions:
|
||||
@ -43,6 +49,8 @@ export const WorkflowEditActionFilterBody = ({
|
||||
action,
|
||||
actionOptions,
|
||||
}: WorkflowEditActionFilterBodyProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const rootStepFilterGroup = useRecoilComponentValueV2(
|
||||
rootLevelStepFilterGroupComponentSelector,
|
||||
);
|
||||
@ -69,6 +77,22 @@ export const WorkflowEditActionFilterBody = ({
|
||||
});
|
||||
};
|
||||
|
||||
const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep(
|
||||
{},
|
||||
);
|
||||
|
||||
const noAvailableVariables = availableVariablesInWorkflowStep.length === 0;
|
||||
|
||||
if (noAvailableVariables) {
|
||||
return (
|
||||
<WorkflowStepBody>
|
||||
<StyledDangerContainer>
|
||||
{t`No Available Step Outputs`}
|
||||
</StyledDangerContainer>
|
||||
</WorkflowStepBody>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<WorkflowStepFilterContext.Provider
|
||||
value={{
|
||||
|
||||
@ -67,6 +67,7 @@ export const WorkflowStepFilterFieldSelect = ({
|
||||
stepOutputKey: variableName,
|
||||
displayValue: variableLabel ?? '',
|
||||
type: variableType ?? 'unknown',
|
||||
value: '',
|
||||
},
|
||||
});
|
||||
},
|
||||
@ -85,10 +86,11 @@ export const WorkflowStepFilterFieldSelect = ({
|
||||
|
||||
const isSelectedFieldNotFound = !isDefined(variableLabel);
|
||||
const label = isSelectedFieldNotFound ? t`No Field Selected` : variableLabel;
|
||||
const dropdownId = `step-filter-field-${stepFilter.id}`;
|
||||
|
||||
return (
|
||||
<WorkflowVariablesDropdown
|
||||
instanceId={`step-filter-field-${stepFilter.id}`}
|
||||
instanceId={dropdownId}
|
||||
onVariableSelect={handleChange}
|
||||
disabled={readonly}
|
||||
clickableComponent={
|
||||
|
||||
@ -1,15 +1,45 @@
|
||||
import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
||||
import { configurableViewFilterOperands } from '@/object-record/object-filter-dropdown/utils/configurableViewFilterOperands';
|
||||
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
|
||||
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { useWorkflowStepContextOrThrow } from '@/workflow/states/context/WorkflowStepContext';
|
||||
import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/stepsOutputSchemaFamilySelector';
|
||||
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';
|
||||
import { extractRawVariableNamePart } from '@/workflow/workflow-variables/utils/extractRawVariableNamePart';
|
||||
import { searchVariableThroughOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughOutputSchema';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isObject, isString } from '@sniptt/guards';
|
||||
import { useContext } from 'react';
|
||||
import { StepFilter } from 'twenty-shared/src/types';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { FieldMetadataType, StepFilter } from 'twenty-shared/src/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { JsonValue } from 'type-fest';
|
||||
|
||||
type WorkflowStepFilterValueInputProps = {
|
||||
stepFilter: StepFilter;
|
||||
};
|
||||
|
||||
const isFilterableFieldMetadataType = (
|
||||
type: string,
|
||||
): type is FieldMetadataType => {
|
||||
return [
|
||||
FieldMetadataType.TEXT,
|
||||
FieldMetadataType.NUMBER,
|
||||
FieldMetadataType.BOOLEAN,
|
||||
FieldMetadataType.DATE_TIME,
|
||||
FieldMetadataType.DATE,
|
||||
FieldMetadataType.NUMERIC,
|
||||
FieldMetadataType.SELECT,
|
||||
FieldMetadataType.MULTI_SELECT,
|
||||
FieldMetadataType.RAW_JSON,
|
||||
FieldMetadataType.RICH_TEXT_V2,
|
||||
FieldMetadataType.ARRAY,
|
||||
].includes(type as FieldMetadataType);
|
||||
};
|
||||
|
||||
export const WorkflowStepFilterValueInput = ({
|
||||
stepFilter,
|
||||
}: WorkflowStepFilterValueInputProps) => {
|
||||
@ -17,15 +47,76 @@ export const WorkflowStepFilterValueInput = ({
|
||||
const { readonly } = useContext(WorkflowStepFilterContext);
|
||||
|
||||
const { upsertStepFilterSettings } = useUpsertStepFilterSettings();
|
||||
const { workflowVersionId } = useWorkflowStepContextOrThrow();
|
||||
|
||||
const stepId = extractRawVariableNamePart({
|
||||
rawVariableName: stepFilter.stepOutputKey,
|
||||
part: 'stepId',
|
||||
});
|
||||
|
||||
const stepsOutputSchema = useRecoilValue(
|
||||
stepsOutputSchemaFamilySelector({
|
||||
workflowVersionId,
|
||||
stepIds: [stepId],
|
||||
}),
|
||||
);
|
||||
const { variableType, fieldMetadataId } = searchVariableThroughOutputSchema({
|
||||
stepOutputSchema: stepsOutputSchema?.[0],
|
||||
rawVariableName: stepFilter.stepOutputKey,
|
||||
isFullRecord: false,
|
||||
});
|
||||
|
||||
const handleValueChange = (value: JsonValue) => {
|
||||
const valueToUpsert = isString(value)
|
||||
? value
|
||||
: Array.isArray(value) || isObject(value)
|
||||
? JSON.stringify(value)
|
||||
: String(value);
|
||||
|
||||
const handleValueChange = (value: string) => {
|
||||
upsertStepFilterSettings({
|
||||
stepFilterToUpsert: {
|
||||
...stepFilter,
|
||||
value,
|
||||
value: valueToUpsert,
|
||||
},
|
||||
});
|
||||
};
|
||||
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
|
||||
|
||||
const isDisabled = !stepFilter.operand;
|
||||
|
||||
const operandHasNoInput =
|
||||
(stepFilter && !configurableViewFilterOperands.has(stepFilter.operand)) ??
|
||||
true;
|
||||
|
||||
if (isDisabled || operandHasNoInput) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isDefined(variableType) && isFilterableFieldMetadataType(variableType)) {
|
||||
const selectedFieldMetadataItem = isDefined(fieldMetadataId)
|
||||
? getFieldMetadataItemById(fieldMetadataId)
|
||||
: undefined;
|
||||
|
||||
const field = {
|
||||
type: variableType as FieldMetadataType,
|
||||
label: '',
|
||||
metadata: {
|
||||
fieldName: selectedFieldMetadataItem?.name ?? '',
|
||||
options: selectedFieldMetadataItem?.options ?? [],
|
||||
} as FieldMetadata,
|
||||
};
|
||||
|
||||
return (
|
||||
<FormFieldInput
|
||||
field={field}
|
||||
defaultValue={stepFilter.value}
|
||||
onChange={handleValueChange}
|
||||
readonly={readonly}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
placeholder={t`Enter value`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormTextFieldInput
|
||||
|
||||
@ -38,7 +38,6 @@ export const FILTER_OPERANDS_MAP = {
|
||||
],
|
||||
DATE_TIME: [
|
||||
ViewFilterOperand.Is,
|
||||
ViewFilterOperand.IsRelative,
|
||||
ViewFilterOperand.IsInPast,
|
||||
ViewFilterOperand.IsInFuture,
|
||||
ViewFilterOperand.IsToday,
|
||||
@ -48,7 +47,6 @@ export const FILTER_OPERANDS_MAP = {
|
||||
],
|
||||
DATE: [
|
||||
ViewFilterOperand.Is,
|
||||
ViewFilterOperand.IsRelative,
|
||||
ViewFilterOperand.IsInPast,
|
||||
ViewFilterOperand.IsInFuture,
|
||||
ViewFilterOperand.IsToday,
|
||||
|
||||
@ -18,7 +18,7 @@ import { useRecordIndexContextOrThrow } from '@/object-record/record-index/conte
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { WorkflowAdvancedFilterValueFormCompositeFieldInput } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterValueFormCompositeFieldInput';
|
||||
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
|
||||
import { isObject } from '@sniptt/guards';
|
||||
import { isObject, isString } from '@sniptt/guards';
|
||||
import { useContext } from 'react';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -56,7 +56,7 @@ export const WorkflowAdvancedFilterValueFormInput = ({
|
||||
useApplyObjectFilterDropdownFilterValue();
|
||||
|
||||
const handleChange = (newValue: JsonValue) => {
|
||||
if (typeof newValue === 'string') {
|
||||
if (isString(newValue)) {
|
||||
applyObjectFilterDropdownFilterValue(newValue);
|
||||
} else if (Array.isArray(newValue) || isObject(newValue)) {
|
||||
applyObjectFilterDropdownFilterValue(JSON.stringify(newValue));
|
||||
|
||||
@ -7,6 +7,7 @@ type Leaf = {
|
||||
label?: string;
|
||||
description?: string;
|
||||
value: any;
|
||||
fieldMetadataId?: string;
|
||||
};
|
||||
|
||||
type Node = {
|
||||
@ -16,6 +17,7 @@ type Node = {
|
||||
label?: string;
|
||||
value: OutputSchema;
|
||||
description?: string;
|
||||
fieldMetadataId?: string;
|
||||
};
|
||||
|
||||
type Link = {
|
||||
@ -28,7 +30,11 @@ type Link = {
|
||||
export type BaseOutputSchema = Record<string, Leaf | Node>;
|
||||
|
||||
export type RecordOutputSchema = {
|
||||
object: { nameSingular: string; fieldIdName: string } & Leaf;
|
||||
object: {
|
||||
nameSingular: string;
|
||||
fieldIdName: string;
|
||||
objectMetadataId: string;
|
||||
} & Leaf;
|
||||
fields: BaseOutputSchema;
|
||||
_outputSchemaType: 'RECORD';
|
||||
};
|
||||
|
||||
@ -12,6 +12,7 @@ describe('filterOutputSchema', () => {
|
||||
fieldIdName: 'id',
|
||||
isLeaf: true,
|
||||
value: 'Fake value',
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
fields: {},
|
||||
};
|
||||
@ -35,6 +36,7 @@ describe('filterOutputSchema', () => {
|
||||
fieldIdName: 'id',
|
||||
isLeaf: true,
|
||||
value: 'Fake value',
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
fields,
|
||||
});
|
||||
|
||||
@ -16,6 +16,7 @@ const mockStep = {
|
||||
label: 'Company',
|
||||
value: 'John',
|
||||
isLeaf: true,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
fields: {
|
||||
name: { label: 'Name', value: 'Twenty', isLeaf: true },
|
||||
|
||||
@ -16,6 +16,7 @@ const mockStep = {
|
||||
label: 'Company',
|
||||
value: 'John',
|
||||
isLeaf: true,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
fields: {
|
||||
name: { label: 'Name', value: 'Twenty', isLeaf: true },
|
||||
|
||||
@ -19,6 +19,7 @@ describe('searchVariableThroughOutputSchema', () => {
|
||||
label: 'Company',
|
||||
value: 'John',
|
||||
isLeaf: true,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
fields: {
|
||||
name: { label: 'Name', value: 'Twenty', isLeaf: true },
|
||||
@ -38,6 +39,7 @@ describe('searchVariableThroughOutputSchema', () => {
|
||||
label: 'Person',
|
||||
value: 'Jane',
|
||||
isLeaf: true,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
fields: {
|
||||
firstName: { label: 'First Name', value: 'Jane', isLeaf: true },
|
||||
@ -270,6 +272,7 @@ describe('searchVariableThroughOutputSchema', () => {
|
||||
isLeaf: true,
|
||||
fieldIdName: 'properties.after.id',
|
||||
nameSingular: 'company',
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
_outputSchemaType: 'RECORD',
|
||||
},
|
||||
|
||||
@ -43,6 +43,17 @@ const getVariableType = (key: string, outputSchema: OutputSchema): string => {
|
||||
return outputSchema[key]?.type ?? 'unknown';
|
||||
};
|
||||
|
||||
const getFieldMetadataId = (
|
||||
key: string,
|
||||
outputSchema: OutputSchema,
|
||||
): string | undefined => {
|
||||
if (isRecordOutputSchema(outputSchema)) {
|
||||
return outputSchema.fields[key]?.fieldMetadataId;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const searchCurrentStepOutputSchema = ({
|
||||
stepOutputSchema,
|
||||
path,
|
||||
@ -120,6 +131,10 @@ const searchCurrentStepOutputSchema = ({
|
||||
isSelectedFieldInNextKey ? nextKey : selectedField,
|
||||
currentSubStep,
|
||||
),
|
||||
fieldMetadataId: getFieldMetadataId(
|
||||
isSelectedFieldInNextKey ? nextKey : selectedField,
|
||||
currentSubStep,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
@ -160,7 +175,7 @@ export const searchVariableThroughOutputSchema = ({
|
||||
};
|
||||
}
|
||||
|
||||
const { variableLabel, variablePathLabel, variableType } =
|
||||
const { variableLabel, variablePathLabel, variableType, fieldMetadataId } =
|
||||
searchCurrentStepOutputSchema({
|
||||
stepOutputSchema,
|
||||
path,
|
||||
@ -172,5 +187,6 @@ export const searchVariableThroughOutputSchema = ({
|
||||
variableLabel,
|
||||
variablePathLabel: `${variablePathLabel} > ${variableLabel}`,
|
||||
variableType,
|
||||
fieldMetadataId,
|
||||
};
|
||||
};
|
||||
|
||||
@ -28,9 +28,17 @@ type Link = {
|
||||
|
||||
export type BaseOutputSchema = Record<string, Leaf | Node>;
|
||||
|
||||
export type FieldOutputSchema =
|
||||
| ((Leaf | Node) & { fieldMetadataId?: string })
|
||||
| RecordOutputSchema;
|
||||
|
||||
export type RecordOutputSchema = {
|
||||
object: { nameSingular: string; fieldIdName: string } & Leaf;
|
||||
fields: BaseOutputSchema;
|
||||
object: {
|
||||
nameSingular: string;
|
||||
fieldIdName: string;
|
||||
objectMetadataId: string;
|
||||
} & Leaf;
|
||||
fields: Record<string, FieldOutputSchema>;
|
||||
_outputSchemaType: 'RECORD';
|
||||
};
|
||||
|
||||
|
||||
@ -55,6 +55,7 @@ describe('generateFakeField', () => {
|
||||
const result = generateFakeField({
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Text Field',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@ -63,6 +64,7 @@ describe('generateFakeField', () => {
|
||||
icon: undefined,
|
||||
label: 'Text Field',
|
||||
value: 'Fake Text',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(generateFakeValueSpy).toHaveBeenCalledWith(
|
||||
@ -76,6 +78,7 @@ describe('generateFakeField', () => {
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Text Field',
|
||||
value: 'Test value',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@ -84,6 +87,7 @@ describe('generateFakeField', () => {
|
||||
icon: undefined,
|
||||
label: 'Text Field',
|
||||
value: 'Test value',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(generateFakeValueSpy).not.toHaveBeenCalled();
|
||||
@ -96,6 +100,7 @@ describe('generateFakeField', () => {
|
||||
type: FieldMetadataType.NUMBER,
|
||||
label: 'Number Field',
|
||||
icon: 'IconNumber',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@ -104,6 +109,7 @@ describe('generateFakeField', () => {
|
||||
icon: 'IconNumber',
|
||||
label: 'Number Field',
|
||||
value: 42,
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
});
|
||||
|
||||
@ -115,6 +121,7 @@ describe('generateFakeField', () => {
|
||||
const result = generateFakeField({
|
||||
type: FieldMetadataType.DATE,
|
||||
label: 'Date Field',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@ -123,6 +130,7 @@ describe('generateFakeField', () => {
|
||||
icon: undefined,
|
||||
label: 'Date Field',
|
||||
value: fakeDate,
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -140,6 +148,7 @@ describe('generateFakeField', () => {
|
||||
const result = generateFakeField({
|
||||
type: FieldMetadataType.LINKS,
|
||||
label: 'Links Field',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@ -161,6 +170,7 @@ describe('generateFakeField', () => {
|
||||
value: 'https://example.com',
|
||||
},
|
||||
},
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(generateFakeValueSpy).toHaveBeenCalledTimes(2);
|
||||
@ -179,6 +189,7 @@ describe('generateFakeField', () => {
|
||||
type: FieldMetadataType.CURRENCY,
|
||||
label: 'Currency Field',
|
||||
icon: 'IconCurrency',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@ -200,6 +211,7 @@ describe('generateFakeField', () => {
|
||||
value: 'USD',
|
||||
},
|
||||
},
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -213,6 +225,7 @@ describe('generateFakeField', () => {
|
||||
const result = generateFakeField({
|
||||
type: unknownType,
|
||||
label: 'Unknown Field',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@ -221,6 +234,7 @@ describe('generateFakeField', () => {
|
||||
icon: undefined,
|
||||
label: 'Unknown Field',
|
||||
value: 'Unknown Value',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
});
|
||||
|
||||
@ -230,6 +244,7 @@ describe('generateFakeField', () => {
|
||||
const result = generateFakeField({
|
||||
type: FieldMetadataType.BOOLEAN,
|
||||
label: '',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@ -238,6 +253,7 @@ describe('generateFakeField', () => {
|
||||
icon: undefined,
|
||||
label: '',
|
||||
value: 'Fake Boolean',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -59,6 +59,7 @@ describe('generateFakeFormResponse', () => {
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"age": {
|
||||
"fieldMetadataId": undefined,
|
||||
"icon": undefined,
|
||||
"isLeaf": true,
|
||||
"label": "Age",
|
||||
@ -72,6 +73,7 @@ describe('generateFakeFormResponse', () => {
|
||||
"_outputSchemaType": "RECORD",
|
||||
"fields": {
|
||||
"domainName": {
|
||||
"fieldMetadataId": "domainNameFieldMetadataId",
|
||||
"icon": "test-field-icon",
|
||||
"isLeaf": false,
|
||||
"label": "Domain Name",
|
||||
@ -98,6 +100,7 @@ describe('generateFakeFormResponse', () => {
|
||||
},
|
||||
},
|
||||
"name": {
|
||||
"fieldMetadataId": "nameFieldMetadataId",
|
||||
"icon": "test-field-icon",
|
||||
"isLeaf": true,
|
||||
"label": "Name",
|
||||
@ -111,11 +114,13 @@ describe('generateFakeFormResponse', () => {
|
||||
"isLeaf": true,
|
||||
"label": "Company",
|
||||
"nameSingular": "company",
|
||||
"objectMetadataId": "20202020-c03c-45d6-a4b0-04afe1357c5c",
|
||||
"value": "A company",
|
||||
},
|
||||
},
|
||||
},
|
||||
"date": {
|
||||
"fieldMetadataId": undefined,
|
||||
"icon": undefined,
|
||||
"isLeaf": true,
|
||||
"label": "Date",
|
||||
@ -123,6 +128,7 @@ describe('generateFakeFormResponse', () => {
|
||||
"value": "mm/dd/yyyy",
|
||||
},
|
||||
"name": {
|
||||
"fieldMetadataId": undefined,
|
||||
"icon": undefined,
|
||||
"isLeaf": true,
|
||||
"label": "Name",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps';
|
||||
import { generateFakeObjectRecordEvent } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record-event';
|
||||
import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields';
|
||||
import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps';
|
||||
|
||||
jest.mock(
|
||||
'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields',
|
||||
@ -13,8 +13,16 @@ describe('generateFakeObjectRecordEvent', () => {
|
||||
});
|
||||
|
||||
const mockFields = {
|
||||
field1: { type: 'TEXT', value: 'test' },
|
||||
field2: { type: 'NUMBER', value: 123 },
|
||||
field1: {
|
||||
type: 'TEXT',
|
||||
value: 'test',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
},
|
||||
field2: {
|
||||
type: 'NUMBER',
|
||||
value: 123,
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174001',
|
||||
},
|
||||
};
|
||||
|
||||
const companyMockObjectMetadataItem =
|
||||
@ -55,10 +63,19 @@ describe('generateFakeObjectRecordEvent', () => {
|
||||
value: 'A company',
|
||||
nameSingular: 'company',
|
||||
fieldIdName: 'properties.after.id',
|
||||
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
|
||||
},
|
||||
fields: {
|
||||
'properties.after.field1': { type: 'TEXT', value: 'test' },
|
||||
'properties.after.field2': { type: 'NUMBER', value: 123 },
|
||||
'properties.after.field1': {
|
||||
type: 'TEXT',
|
||||
value: 'test',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
},
|
||||
'properties.after.field2': {
|
||||
type: 'NUMBER',
|
||||
value: 123,
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174001',
|
||||
},
|
||||
},
|
||||
_outputSchemaType: 'RECORD',
|
||||
});
|
||||
@ -78,10 +95,19 @@ describe('generateFakeObjectRecordEvent', () => {
|
||||
value: 'A company',
|
||||
nameSingular: 'company',
|
||||
fieldIdName: 'properties.after.id',
|
||||
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
|
||||
},
|
||||
fields: {
|
||||
'properties.after.field1': { type: 'TEXT', value: 'test' },
|
||||
'properties.after.field2': { type: 'NUMBER', value: 123 },
|
||||
'properties.after.field1': {
|
||||
type: 'TEXT',
|
||||
value: 'test',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
},
|
||||
'properties.after.field2': {
|
||||
type: 'NUMBER',
|
||||
value: 123,
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174001',
|
||||
},
|
||||
},
|
||||
_outputSchemaType: 'RECORD',
|
||||
});
|
||||
@ -101,10 +127,19 @@ describe('generateFakeObjectRecordEvent', () => {
|
||||
value: 'A company',
|
||||
nameSingular: 'company',
|
||||
fieldIdName: 'properties.before.id',
|
||||
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
|
||||
},
|
||||
fields: {
|
||||
'properties.before.field1': { type: 'TEXT', value: 'test' },
|
||||
'properties.before.field2': { type: 'NUMBER', value: 123 },
|
||||
'properties.before.field1': {
|
||||
type: 'TEXT',
|
||||
value: 'test',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
},
|
||||
'properties.before.field2': {
|
||||
type: 'NUMBER',
|
||||
value: 123,
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174001',
|
||||
},
|
||||
},
|
||||
_outputSchemaType: 'RECORD',
|
||||
});
|
||||
@ -124,10 +159,19 @@ describe('generateFakeObjectRecordEvent', () => {
|
||||
value: 'A company',
|
||||
nameSingular: 'company',
|
||||
fieldIdName: 'properties.before.id',
|
||||
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
|
||||
},
|
||||
fields: {
|
||||
'properties.before.field1': { type: 'TEXT', value: 'test' },
|
||||
'properties.before.field2': { type: 'NUMBER', value: 123 },
|
||||
'properties.before.field1': {
|
||||
type: 'TEXT',
|
||||
value: 'test',
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
},
|
||||
'properties.before.field2': {
|
||||
type: 'NUMBER',
|
||||
value: 123,
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174001',
|
||||
},
|
||||
},
|
||||
_outputSchemaType: 'RECORD',
|
||||
});
|
||||
|
||||
@ -43,6 +43,7 @@ describe('generateFakeObjectRecord', () => {
|
||||
value: 'A company',
|
||||
nameSingular: 'company',
|
||||
fieldIdName: 'id',
|
||||
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
|
||||
},
|
||||
fields: {
|
||||
field1: { type: 'TEXT', value: 'test' },
|
||||
|
||||
@ -13,12 +13,14 @@ export const generateFakeField = ({
|
||||
label,
|
||||
icon,
|
||||
value,
|
||||
fieldMetadataId,
|
||||
}: {
|
||||
type: FieldMetadataType;
|
||||
label: string;
|
||||
fieldMetadataId?: string;
|
||||
icon?: string;
|
||||
value?: string;
|
||||
}): Leaf | Node => {
|
||||
}): (Leaf | Node) & { fieldMetadataId?: string } => {
|
||||
const compositeType = compositeTypeDefinitions.get(type);
|
||||
|
||||
if (compositeType) {
|
||||
@ -27,6 +29,7 @@ export const generateFakeField = ({
|
||||
type: type,
|
||||
icon: icon,
|
||||
label: label,
|
||||
fieldMetadataId,
|
||||
value: compositeType.properties.reduce((acc, property) => {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
acc[property.name] = {
|
||||
@ -47,5 +50,6 @@ export const generateFakeField = ({
|
||||
icon: icon,
|
||||
label: label,
|
||||
value: value || generateFakeValue(type, 'FieldMetadataType'),
|
||||
fieldMetadataId,
|
||||
};
|
||||
};
|
||||
|
||||
@ -60,7 +60,10 @@ export const generateFakeFormResponse = async ({
|
||||
}),
|
||||
);
|
||||
|
||||
return result.filter(isDefined).reduce((acc, curr) => {
|
||||
return { ...acc, ...curr };
|
||||
}, {});
|
||||
return result.filter(isDefined).reduce(
|
||||
(acc, curr) => {
|
||||
return { ...acc, ...curr };
|
||||
},
|
||||
{} as Record<string, Leaf | Node>,
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import {
|
||||
BaseOutputSchema,
|
||||
FieldOutputSchema,
|
||||
RecordOutputSchema,
|
||||
} from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
|
||||
import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields';
|
||||
@ -20,7 +20,7 @@ const generateFakeObjectRecordEventWithPrefix = ({
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as BaseOutputSchema,
|
||||
{} as Record<string, FieldOutputSchema>,
|
||||
);
|
||||
|
||||
return {
|
||||
@ -33,6 +33,7 @@ const generateFakeObjectRecordEventWithPrefix = ({
|
||||
nameSingular:
|
||||
objectMetadataInfo.objectMetadataItemWithFieldsMaps.nameSingular,
|
||||
fieldIdName: `${prefix}.id`,
|
||||
objectMetadataId: objectMetadataInfo.objectMetadataItemWithFieldsMaps.id,
|
||||
},
|
||||
fields: prefixedRecordFields,
|
||||
_outputSchemaType: 'RECORD',
|
||||
|
||||
@ -19,6 +19,7 @@ export const generateFakeObjectRecord = ({
|
||||
nameSingular:
|
||||
objectMetadataInfo.objectMetadataItemWithFieldsMaps.nameSingular,
|
||||
fieldIdName: 'id',
|
||||
objectMetadataId: objectMetadataInfo.objectMetadataItemWithFieldsMaps.id,
|
||||
},
|
||||
fields: generateObjectRecordFields({
|
||||
objectMetadataInfo,
|
||||
|
||||
@ -2,7 +2,7 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import { BaseOutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
|
||||
import { FieldOutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
|
||||
import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field';
|
||||
import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record';
|
||||
import { shouldGenerateFieldFakeValue } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value';
|
||||
@ -15,11 +15,11 @@ export const generateObjectRecordFields = ({
|
||||
}: {
|
||||
objectMetadataInfo: ObjectMetadataInfo;
|
||||
depth?: number;
|
||||
}): BaseOutputSchema => {
|
||||
}): Record<string, FieldOutputSchema> => {
|
||||
const objectMetadata = objectMetadataInfo.objectMetadataItemWithFieldsMaps;
|
||||
|
||||
return Object.values(objectMetadata.fieldsById).reduce(
|
||||
(acc: BaseOutputSchema, field) => {
|
||||
(acc: Record<string, FieldOutputSchema>, field) => {
|
||||
if (!shouldGenerateFieldFakeValue(field)) {
|
||||
return acc;
|
||||
}
|
||||
@ -29,6 +29,7 @@ export const generateObjectRecordFields = ({
|
||||
type: field.type,
|
||||
label: field.label,
|
||||
icon: field.icon ?? undefined,
|
||||
fieldMetadataId: field.id,
|
||||
});
|
||||
|
||||
return acc;
|
||||
@ -51,6 +52,7 @@ export const generateObjectRecordFields = ({
|
||||
isLeaf: false,
|
||||
icon: field.icon ?? undefined,
|
||||
label: field.label,
|
||||
fieldMetadataId: field.id,
|
||||
value: generateFakeObjectRecord({
|
||||
objectMetadataInfo: {
|
||||
objectMetadataItemWithFieldsMaps: relationTargetObjectMetadata,
|
||||
@ -63,6 +65,6 @@ export const generateObjectRecordFields = ({
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as BaseOutputSchema,
|
||||
{} as Record<string, FieldOutputSchema>,
|
||||
);
|
||||
};
|
||||
|
||||
@ -68,30 +68,11 @@ describe('evaluateFilterConditions', () => {
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle null checks', () => {
|
||||
const filter1 = createFilter(ViewFilterOperand.Is, null, 'null');
|
||||
const filter2 = createFilter(ViewFilterOperand.Is, undefined, 'NULL');
|
||||
const filter3 = createFilter(ViewFilterOperand.Is, 'value', 'null');
|
||||
it('should return true when values are equal but different types', () => {
|
||||
const filter = createFilter(ViewFilterOperand.Is, '123', 123);
|
||||
const result = evaluateFilterConditions({ filters: [filter] });
|
||||
|
||||
expect(evaluateFilterConditions({ filters: [filter1] })).toBe(true);
|
||||
expect(evaluateFilterConditions({ filters: [filter2] })).toBe(true);
|
||||
expect(evaluateFilterConditions({ filters: [filter3] })).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle not null checks', () => {
|
||||
const filter1 = createFilter(ViewFilterOperand.Is, 'value', 'not null');
|
||||
const filter2 = createFilter(ViewFilterOperand.Is, 'value', 'NOT NULL');
|
||||
const filter3 = createFilter(ViewFilterOperand.Is, null, 'not null');
|
||||
const filter4 = createFilter(
|
||||
ViewFilterOperand.Is,
|
||||
undefined,
|
||||
'not null',
|
||||
);
|
||||
|
||||
expect(evaluateFilterConditions({ filters: [filter1] })).toBe(true);
|
||||
expect(evaluateFilterConditions({ filters: [filter2] })).toBe(true);
|
||||
expect(evaluateFilterConditions({ filters: [filter3] })).toBe(false);
|
||||
expect(evaluateFilterConditions({ filters: [filter4] })).toBe(false);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@ -182,12 +163,12 @@ describe('evaluateFilterConditions', () => {
|
||||
const filter1 = createFilter(
|
||||
ViewFilterOperand.Contains,
|
||||
['apple', 'banana', 'cherry'],
|
||||
'apple',
|
||||
['apple'],
|
||||
);
|
||||
const filter2 = createFilter(
|
||||
ViewFilterOperand.Contains,
|
||||
['apple', 'banana', 'cherry'],
|
||||
'grape',
|
||||
['grape'],
|
||||
);
|
||||
|
||||
expect(evaluateFilterConditions({ filters: [filter1] })).toBe(true);
|
||||
@ -198,12 +179,12 @@ describe('evaluateFilterConditions', () => {
|
||||
const filter1 = createFilter(
|
||||
ViewFilterOperand.DoesNotContain,
|
||||
['apple', 'banana', 'cherry'],
|
||||
'apple',
|
||||
['apple'],
|
||||
);
|
||||
const filter2 = createFilter(
|
||||
ViewFilterOperand.DoesNotContain,
|
||||
['apple', 'banana', 'cherry'],
|
||||
'grape',
|
||||
['grape'],
|
||||
);
|
||||
|
||||
expect(evaluateFilterConditions({ filters: [filter1] })).toBe(false);
|
||||
|
||||
@ -16,17 +16,19 @@ function evaluateFilter(filter: ResolvedFilter): boolean {
|
||||
|
||||
switch (filter.operand) {
|
||||
case ViewFilterOperand.Is:
|
||||
if (String(rightValue).toLowerCase() === 'null') {
|
||||
return leftValue === null || leftValue === undefined;
|
||||
switch (typeof leftValue) {
|
||||
case 'string':
|
||||
return (
|
||||
String(leftValue).toLowerCase() === String(rightValue).toLowerCase()
|
||||
);
|
||||
case 'boolean':
|
||||
return Boolean(leftValue) === Boolean(rightValue);
|
||||
default:
|
||||
return leftValue === rightValue;
|
||||
}
|
||||
if (String(rightValue).toLowerCase() === 'not null') {
|
||||
return leftValue !== null && leftValue !== undefined;
|
||||
}
|
||||
|
||||
return leftValue == rightValue;
|
||||
|
||||
case ViewFilterOperand.IsNot:
|
||||
return leftValue != rightValue;
|
||||
return String(leftValue) !== String(rightValue);
|
||||
|
||||
case ViewFilterOperand.GreaterThanOrEqual:
|
||||
return Number(leftValue) >= Number(rightValue);
|
||||
@ -36,14 +38,38 @@ function evaluateFilter(filter: ResolvedFilter): boolean {
|
||||
|
||||
case ViewFilterOperand.Contains:
|
||||
if (Array.isArray(leftValue)) {
|
||||
return leftValue.includes(rightValue);
|
||||
try {
|
||||
const parsedRightValue = Array.isArray(rightValue)
|
||||
? rightValue
|
||||
: JSON.parse(rightValue as string);
|
||||
|
||||
if (Array.isArray(parsedRightValue)) {
|
||||
return parsedRightValue.every((item) => leftValue.includes(item));
|
||||
} else {
|
||||
return leftValue.includes(parsedRightValue);
|
||||
}
|
||||
} catch (error) {
|
||||
return leftValue.includes(rightValue);
|
||||
}
|
||||
}
|
||||
|
||||
return String(leftValue).includes(String(rightValue));
|
||||
|
||||
case ViewFilterOperand.DoesNotContain:
|
||||
if (Array.isArray(leftValue)) {
|
||||
return !leftValue.includes(rightValue);
|
||||
try {
|
||||
const parsedRightValue = Array.isArray(rightValue)
|
||||
? rightValue
|
||||
: JSON.parse(rightValue as string);
|
||||
|
||||
if (Array.isArray(parsedRightValue)) {
|
||||
return !parsedRightValue.every((item) => leftValue.includes(item));
|
||||
} else {
|
||||
return !leftValue.includes(parsedRightValue);
|
||||
}
|
||||
} catch (error) {
|
||||
return !leftValue.includes(rightValue);
|
||||
}
|
||||
}
|
||||
|
||||
return !String(leftValue).includes(String(rightValue));
|
||||
@ -67,17 +93,43 @@ function evaluateFilter(filter: ResolvedFilter): boolean {
|
||||
case ViewFilterOperand.IsNotNull:
|
||||
return leftValue !== null && leftValue !== undefined;
|
||||
|
||||
case ViewFilterOperand.IsRelative:
|
||||
case ViewFilterOperand.IsInPast:
|
||||
if (typeof leftValue === 'string') {
|
||||
return Date.now() - new Date(leftValue).getTime() > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case ViewFilterOperand.IsInFuture:
|
||||
if (typeof leftValue === 'string') {
|
||||
return Date.now() - new Date(leftValue).getTime() < 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case ViewFilterOperand.IsToday:
|
||||
if (typeof leftValue === 'string') {
|
||||
return new Date(leftValue).toDateString() === new Date().toDateString();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case ViewFilterOperand.IsBefore:
|
||||
if (typeof leftValue === 'string' && typeof rightValue === 'string') {
|
||||
return new Date(leftValue).getTime() < new Date(rightValue).getTime();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case ViewFilterOperand.IsAfter:
|
||||
// Date/time operands - for now, return false as placeholder
|
||||
// These would need proper date logic implementation
|
||||
if (typeof leftValue === 'string' && typeof rightValue === 'string') {
|
||||
return new Date(leftValue).getTime() > new Date(rightValue).getTime();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case ViewFilterOperand.VectorSearch:
|
||||
case ViewFilterOperand.IsRelative:
|
||||
return false;
|
||||
|
||||
default:
|
||||
|
||||
@ -40,6 +40,7 @@ const settings: WorkflowFormActionSettings = {
|
||||
label: 'Id',
|
||||
value: '123e4567-e89b-12d3-a456-426614174000',
|
||||
isLeaf: true,
|
||||
fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
},
|
||||
},
|
||||
object: {
|
||||
@ -49,6 +50,7 @@ const settings: WorkflowFormActionSettings = {
|
||||
isLeaf: true,
|
||||
fieldIdName: 'id',
|
||||
nameSingular: 'company',
|
||||
objectMetadataId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
},
|
||||
_outputSchemaType: 'RECORD',
|
||||
},
|
||||
|
||||
@ -16,8 +16,8 @@ export type StepFilter = {
|
||||
id: string;
|
||||
type: string;
|
||||
label: string;
|
||||
value: string;
|
||||
operand: ViewFilterOperand;
|
||||
value: string;
|
||||
displayValue: string;
|
||||
stepFilterGroupId: string;
|
||||
stepOutputKey: string;
|
||||
|
||||
Reference in New Issue
Block a user