Allow to select full object and object id in filters (#14083)

Today WorkflowVariablesDropdownAllItems is used for variable picker in
all workflow steps, and for step field picker in filters.
Using that component in filters prevent us from knowing if a full record
or only the id has been selected.

This PR:
- creates a new component WorkflowDropdownStepOutputItems, mostly
copying the logic of WorkflowVariablesDropdownAllItems
- call it WorkflowStepFilterFieldSelect, removing the display logic from
that parent component
- store isFullRecord in filter. So we now know if we should display a
record picker or a uuid picker

Before - selecting id makes picker behaves like when we select an object


https://github.com/user-attachments/assets/bde34dc5-8011-4983-8d0f-d8cb0cb3c045

After - selecting object and id are two different things


https://github.com/user-attachments/assets/49289990-3e6d-4ad7-abc1-e3ade2a821bb
This commit is contained in:
Thomas Trompette
2025-08-26 15:31:15 +02:00
committed by GitHub
parent 1640aea44a
commit f4312422ce
23 changed files with 335 additions and 256 deletions
@@ -0,0 +1,238 @@
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
import { useGetFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { useWorkflowVersionIdOrThrow } from '@/workflow/hooks/useWorkflowVersionIdOrThrow';
import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/stepsOutputSchemaFamilySelector';
import { useUpsertStepFilterSettings } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useUpsertStepFilterSettings';
import { getStepFilterOperands } from '@/workflow/workflow-steps/workflow-actions/filter-action/utils/getStepFilterOperands';
import { useVariableDropdown } from '@/workflow/workflow-variables/hooks/useVariableDropdown';
import { extractRawVariableNamePart } from '@/workflow/workflow-variables/utils/extractRawVariableNamePart';
import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath';
import { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel';
import { getVariableTemplateFromPath } from '@/workflow/workflow-variables/utils/getVariableTemplateFromPath';
import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema';
import { searchVariableThroughOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughOutputSchema';
import { useLingui } from '@lingui/react/macro';
import { useRecoilCallback } from 'recoil';
import { type StepFilter } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
IconChevronLeft,
OverflowingTextWithTooltip,
useIcons,
} from 'twenty-ui/display';
import { MenuItemSelect } from 'twenty-ui/navigation';
type WorkflowDropdownStepOutputItemsProps = {
stepFilter: StepFilter;
step: StepOutputSchema;
onSelect: () => void;
onBack: () => void;
};
export const WorkflowDropdownStepOutputItems = ({
stepFilter,
step,
onSelect,
onBack,
}: WorkflowDropdownStepOutputItemsProps) => {
const { t } = useLingui();
const { getIcon } = useIcons();
const { upsertStepFilterSettings } = useUpsertStepFilterSettings();
const { getFieldMetadataItemByIdOrThrow } =
useGetFieldMetadataItemByIdOrThrow();
const workflowVersionId = useWorkflowVersionIdOrThrow();
const updateStepFilter = useRecoilCallback(
({ snapshot }) =>
({
rawVariableName,
isFullRecord,
}: {
rawVariableName: string;
isFullRecord: boolean;
}) => {
const stepId = extractRawVariableNamePart({
rawVariableName,
part: 'stepId',
});
const [currentStepOutputSchema] = snapshot
.getLoadable(
stepsOutputSchemaFamilySelector({
workflowVersionId,
stepIds: [stepId],
}),
)
.getValue();
const { variableType, fieldMetadataId, compositeFieldSubFieldName } =
searchVariableThroughOutputSchema({
stepOutputSchema: currentStepOutputSchema,
rawVariableName,
isFullRecord: false,
});
const { fieldMetadataItem: filterFieldMetadataItem } = isDefined(
fieldMetadataId,
)
? getFieldMetadataItemByIdOrThrow(fieldMetadataId)
: { fieldMetadataItem: undefined };
const filterType = isDefined(fieldMetadataId)
? (filterFieldMetadataItem?.type ?? 'unknown')
: variableType;
const availableOperandsForFilter = getStepFilterOperands({
filterType,
subFieldName: compositeFieldSubFieldName,
});
const defaultOperand = availableOperandsForFilter[0];
upsertStepFilterSettings({
stepFilterToUpsert: {
...stepFilter,
stepOutputKey: rawVariableName,
isFullRecord,
type: filterType ?? 'unknown',
value: '',
fieldMetadataId,
compositeFieldSubFieldName,
operand: defaultOperand,
},
});
},
[
workflowVersionId,
getFieldMetadataItemByIdOrThrow,
upsertStepFilterSettings,
stepFilter,
],
);
const handleStepFilterFieldSelect = (key: string) => {
updateStepFilter({
rawVariableName: key,
isFullRecord: false,
});
onSelect();
};
const {
searchInputValue,
setSearchInputValue,
handleSelectField,
goBack,
filteredOptions,
currentPath,
} = useVariableDropdown({
step,
onSelect: handleStepFilterFieldSelect,
onBack,
});
const getDisplayedSubStepObject = () => {
const currentSubStep = getCurrentSubStepFromPath(step, currentPath);
if (!isRecordOutputSchema(currentSubStep)) {
return;
}
return currentSubStep.object;
};
const handleSelectObject = () => {
const currentSubStep = getCurrentSubStepFromPath(step, currentPath);
if (!isRecordOutputSchema(currentSubStep)) {
return;
}
updateStepFilter({
rawVariableName: getVariableTemplateFromPath({
stepId: step.id,
path: [...currentPath, currentSubStep.object.fieldIdName],
}),
isFullRecord: true,
});
onSelect();
};
const displayedSubStepObject = getDisplayedSubStepObject();
const shouldDisplaySubStepObject = searchInputValue
? displayedSubStepObject?.label &&
displayedSubStepObject.label
.toLowerCase()
.includes(searchInputValue.toLowerCase())
: true;
const shouldDisplayObject =
shouldDisplaySubStepObject && displayedSubStepObject?.label;
const nameSingular = displayedSubStepObject?.nameSingular;
return (
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
onClick={goBack}
Icon={IconChevronLeft}
/>
}
>
<OverflowingTextWithTooltip
text={getStepHeaderLabel(step, currentPath)}
/>
</DropdownMenuHeader>
<DropdownMenuSearchInput
autoFocus
value={searchInputValue}
onChange={(event) => setSearchInputValue(event.target.value)}
/>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer hasMaxHeight>
{shouldDisplayObject && (
<MenuItemSelect
selected={false}
focused={false}
onClick={handleSelectObject}
text={displayedSubStepObject?.label || ''}
hasSubMenu={false}
LeftIcon={
displayedSubStepObject.icon
? getIcon(displayedSubStepObject.icon)
: undefined
}
contextualText={t`Pick a ${nameSingular} record`}
/>
)}
{filteredOptions.length > 0 && shouldDisplayObject && (
<DropdownMenuSeparator />
)}
{filteredOptions.map(([key, subStep]) => (
<MenuItemSelect
key={key}
selected={false}
focused={false}
onClick={() => handleSelectField(key)}
text={subStep.label || key}
hasSubMenu={!subStep.isLeaf}
LeftIcon={subStep.icon ? getIcon(subStep.icon) : undefined}
contextualText={
subStep.isLeaf ? subStep?.value?.toString() : undefined
}
/>
))}
</DropdownMenuItemsContainer>
</DropdownContent>
);
};
@@ -137,7 +137,6 @@ export const WorkflowStepFilterAddFilterRuleSelect = ({
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownOffset={{ y: 8, x: 0 }}
dropdownPlacement="bottom-start"
/>
);
@@ -1,19 +1,18 @@
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 { useUpsertStepFilterSettings } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useUpsertStepFilterSettings';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { WorkflowDropdownStepOutputItems } from '@/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowDropdownStepOutputItems';
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';
import { WorkflowVariablesDropdown } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdown';
import { WorkflowVariablesDropdownWorkflowStepItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems';
import { useAvailableVariablesInWorkflowStep } from '@/workflow/workflow-variables/hooks/useAvailableVariablesInWorkflowStep';
import { useSearchVariable } from '@/workflow/workflow-variables/hooks/useSearchVariable';
import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
import { extractRawVariableNamePart } from '@/workflow/workflow-variables/utils/extractRawVariableNamePart';
import { searchVariableThroughOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughOutputSchema';
import { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
import { useContext } from 'react';
import { useRecoilCallback, useRecoilValue } from 'recoil';
import { useContext, useState } from 'react';
import { type StepFilter } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
@@ -34,139 +33,76 @@ export const WorkflowStepFilterFieldSelect = ({
stepFilter,
}: WorkflowStepFilterFieldSelectProps) => {
const { readonly } = useContext(WorkflowStepFilterContext);
const shouldDisplayRecordFields = true;
const shouldDisplayRecordObjects = true;
const { upsertStepFilterSettings } = useUpsertStepFilterSettings();
const { t } = useLingui();
const workflowVersionId = useWorkflowVersionIdOrThrow();
const theme = useTheme();
const { closeDropdown } = useCloseDropdown();
const { getIcon } = useIcons();
const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep({
shouldDisplayRecordFields: true,
shouldDisplayRecordObjects: true,
fieldTypesToExclude: NON_SELECTABLE_FIELD_TYPES,
});
const noAvailableVariables = availableVariablesInWorkflowStep.length === 0;
const initialStep =
availableVariablesInWorkflowStep.length === 1
? availableVariablesInWorkflowStep[0]
: undefined;
const [selectedStep, setSelectedStep] = useState<
StepOutputSchema | undefined
>(initialStep);
const stepId = extractRawVariableNamePart({
rawVariableName: stepFilter.stepOutputKey,
part: 'stepId',
});
const stepsOutputSchema = useRecoilValue(
stepsOutputSchemaFamilySelector({
workflowVersionId,
stepIds: [stepId],
}),
);
const { getIcon } = useIcons();
const { variableLabel } = useSearchVariable({
stepId,
rawVariableName: stepFilter.stepOutputKey,
isFullRecord: stepFilter.isFullRecord ?? false,
});
const {
fieldMetadataItem: filterFieldMetadataItem,
objectMetadataItem: filterObjectMetadataItem,
} = useFieldMetadataItemById(stepFilter.fieldMetadataId ?? '');
const { getFieldMetadataItemByIdOrThrow } =
useGetFieldMetadataItemByIdOrThrow();
const dropdownId = `step-filter-field-${stepFilter.id}`;
const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep({
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
});
const handleStepSelect = (stepId: string) => {
setSelectedStep(
availableVariablesInWorkflowStep.find((step) => step.id === stepId),
);
};
const noAvailableVariables = availableVariablesInWorkflowStep.length === 0;
const handleSubItemSelect = () => {
setSelectedStep(initialStep);
closeDropdown(dropdownId);
};
const handleChange = useRecoilCallback(
({ snapshot }) =>
(variableName: string) => {
const stepId = extractRawVariableNamePart({
rawVariableName: variableName,
part: 'stepId',
});
const [currentStepOutputSchema] = snapshot
.getLoadable(
stepsOutputSchemaFamilySelector({
workflowVersionId,
stepIds: [stepId],
}),
)
.getValue();
const {
variableLabel,
variableType,
fieldMetadataId,
compositeFieldSubFieldName,
} = searchVariableThroughOutputSchema({
stepOutputSchema: currentStepOutputSchema,
rawVariableName: variableName,
isFullRecord: false,
});
const {
fieldMetadataItem: filterFieldMetadataItem,
objectMetadataItem: filterObjectMetadataItem,
} = isDefined(fieldMetadataId)
? getFieldMetadataItemByIdOrThrow(fieldMetadataId)
: { fieldMetadataItem: undefined, objectMetadataItem: undefined };
const filterType = isDefined(fieldMetadataId)
? (filterFieldMetadataItem?.type ?? 'unknown')
: variableType;
const isFullRecord =
filterFieldMetadataItem?.name === 'id' &&
isDefined(filterObjectMetadataItem?.labelSingular);
upsertStepFilterSettings({
stepFilterToUpsert: {
...stepFilter,
stepOutputKey: variableName,
displayValue: isFullRecord
? filterObjectMetadataItem.labelSingular
: (variableLabel ?? ''),
type: filterType ?? 'unknown',
value: '',
fieldMetadataId,
compositeFieldSubFieldName,
operand: getViewFilterOperands({
filterType,
subFieldName: compositeFieldSubFieldName,
})?.[0],
},
});
},
[
workflowVersionId,
getFieldMetadataItemByIdOrThrow,
upsertStepFilterSettings,
stepFilter,
],
);
if (!isDefined(stepId)) {
return null;
}
const isFullRecord =
filterFieldMetadataItem?.name === 'id' &&
isDefined(filterObjectMetadataItem?.labelSingular);
const { variableLabel } = searchVariableThroughOutputSchema({
stepOutputSchema: stepsOutputSchema?.[0],
rawVariableName: stepFilter.stepOutputKey,
isFullRecord,
});
const handleBack = () => {
setSelectedStep(undefined);
};
const isSelectedFieldNotFound = !isDefined(variableLabel);
const label = isSelectedFieldNotFound
? t`Select a field from a previous step`
: variableLabel;
const icon = isFullRecord
const icon = stepFilter.isFullRecord
? getIcon(filterObjectMetadataItem?.icon)
: filterFieldMetadataItem?.icon
? getIcon(filterFieldMetadataItem.icon)
: undefined;
const dropdownId = `step-filter-field-${stepFilter.id}`;
if (readonly || noAvailableVariables) {
const disabledLabel = noAvailableVariables
? t`No available fields to select`
: label;
if (noAvailableVariables) {
return (
<Dropdown
dropdownId={dropdownId}
@@ -174,25 +110,7 @@ export const WorkflowStepFilterFieldSelect = ({
<SelectControl
selectedOption={{
value: stepFilter.stepOutputKey,
label: t`No available fields to select`,
}}
isDisabled={true}
/>
}
dropdownComponents={[]}
/>
);
}
if (readonly === true) {
return (
<Dropdown
dropdownId={dropdownId}
clickableComponent={
<SelectControl
selectedOption={{
value: stepFilter.stepOutputKey,
label,
label: disabledLabel,
Icon: icon,
}}
isDisabled={true}
@@ -204,25 +122,40 @@ export const WorkflowStepFilterFieldSelect = ({
}
return (
<>
<WorkflowVariablesDropdown
instanceId={dropdownId}
onVariableSelect={handleChange}
clickableComponent={
<SelectControl
selectedOption={{
label,
value: stepFilter.stepOutputKey,
Icon: icon,
}}
textAccent={isSelectedFieldNotFound ? 'placeholder' : 'default'}
<Dropdown
dropdownId={dropdownId}
clickableComponent={
<SelectControl
selectedOption={{
label,
value: stepFilter.stepOutputKey,
Icon: icon,
}}
textAccent={isSelectedFieldNotFound ? 'placeholder' : 'default'}
isDisabled={readonly}
/>
}
dropdownComponents={
!isDefined(selectedStep) ? (
<WorkflowVariablesDropdownWorkflowStepItems
dropdownId={dropdownId}
steps={availableVariablesInWorkflowStep}
onSelect={handleStepSelect}
/>
}
shouldDisplayRecordFields={shouldDisplayRecordFields}
shouldDisplayRecordObjects={shouldDisplayRecordObjects}
shouldEnableSelectRelationObject={true}
fieldTypesToExclude={NON_SELECTABLE_FIELD_TYPES}
/>
</>
) : (
<WorkflowDropdownStepOutputItems
stepFilter={stepFilter}
step={selectedStep}
onSelect={handleSubItemSelect}
onBack={handleBack}
/>
)
}
dropdownPlacement="bottom-end"
dropdownOffset={{
x: parseInt(theme.spacing(0.5), 10),
y: parseInt(theme.spacing(1), 10),
}}
/>
);
};
@@ -4,7 +4,7 @@ import { Select } from '@/ui/input/components/Select';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
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';
import { getStepFilterOperands } from '@/workflow/workflow-steps/workflow-actions/filter-action/utils/getStepFilterOperands';
import { useContext } from 'react';
import { type StepFilter, type ViewFilterOperand } from 'twenty-shared/types';
@@ -18,7 +18,7 @@ export const WorkflowStepFilterOperandSelect = ({
const { readonly } = useContext(WorkflowStepFilterContext);
const { upsertStepFilterSettings } = useUpsertStepFilterSettings();
const operands = getViewFilterOperands({
const operands = getStepFilterOperands({
filterType: stepFilter.type,
subFieldName: stepFilter.compositeFieldSubFieldName,
});
@@ -102,7 +102,8 @@ export const WorkflowStepFilterValueInput = ({
variableType === FieldMetadataType.SELECT;
const isFullRecord =
selectedFieldMetadataItem?.name === 'id' &&
isDefined(stepFilter.isFullRecord) &&
stepFilter.isFullRecord &&
isDefined(objectMetadataItem?.nameSingular);
const isDateField =
@@ -24,9 +24,7 @@ const TEXT_STEP_FILTER: StepFilter = {
id: 'filter-1',
stepFilterGroupId: 'filter-group-1',
stepOutputKey: 'company.name',
displayValue: 'Company Name',
type: 'text',
label: 'Company Name',
value: 'Acme',
operand: ViewFilterOperand.Contains,
};
@@ -13,9 +13,7 @@ const DEFAULT_STEP_FILTER: StepFilter = {
id: 'filter-1',
stepFilterGroupId: 'filter-group-1',
stepOutputKey: '',
displayValue: '',
type: 'text',
label: 'New Filter',
operand: ViewFilterOperand.Is,
value: '',
positionInStepFilterGroup: 0,
@@ -14,9 +14,7 @@ const DEFAULT_STEP_FILTER: StepFilter = {
id: 'filter-1',
stepFilterGroupId: 'filter-group-1',
stepOutputKey: 'company.name',
displayValue: 'Company Name',
type: 'text',
label: 'Company Name',
operand: ViewFilterOperand.Contains,
value: '',
positionInStepFilterGroup: 0,
@@ -26,9 +24,7 @@ const GREATER_THAN_FILTER: StepFilter = {
id: 'filter-1',
stepFilterGroupId: 'filter-group-1',
stepOutputKey: 'company.employees',
displayValue: 'Employee Count',
type: 'number',
label: 'Employee Count',
operand: ViewFilterOperand.GreaterThanOrEqual,
value: '100',
positionInStepFilterGroup: 0,
@@ -14,9 +14,7 @@ const TEXT_FILTER: StepFilter = {
id: 'filter-1',
stepFilterGroupId: 'filter-group-1',
stepOutputKey: 'company.name',
displayValue: 'Company Name',
type: 'text',
label: 'Company Name',
operand: ViewFilterOperand.Contains,
value: 'Acme',
positionInStepFilterGroup: 0,
@@ -26,9 +24,7 @@ const NUMBER_FILTER: StepFilter = {
id: 'filter-2',
stepFilterGroupId: 'filter-group-1',
stepOutputKey: 'company.employees',
displayValue: 'Employee Count',
type: 'number',
label: 'Employee Count',
operand: ViewFilterOperand.GreaterThanOrEqual,
value: '100',
positionInStepFilterGroup: 0,
@@ -49,10 +49,8 @@ export const useAddRootStepFilter = () => {
const newStepFilter: StepFilter = {
id: v4(),
type: 'unknown',
label: 'New Filter',
value: '',
operand: ViewFilterOperand.Is,
displayValue: '',
stepFilterGroupId: newStepFilterGroup.id,
stepOutputKey: '',
positionInStepFilterGroup: 0,
@@ -85,7 +85,7 @@ export const COMPOSITE_FIELD_FILTER_OPERANDS_MAP = {
},
};
export const getViewFilterOperands = ({
export const getStepFilterOperands = ({
filterType,
subFieldName,
}: {
@@ -39,7 +39,6 @@ export const WorkflowVariablesDropdown = ({
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
fieldTypesToExclude,
shouldEnableSelectRelationObject,
multiline,
clickableComponent,
}: {
@@ -48,7 +47,6 @@ export const WorkflowVariablesDropdown = ({
shouldDisplayRecordFields: boolean;
shouldDisplayRecordObjects: boolean;
fieldTypesToExclude?: InputSchemaPropertyType[];
shouldEnableSelectRelationObject?: boolean;
disabled?: boolean;
multiline?: boolean;
clickableComponent?: React.ReactNode;
@@ -134,7 +132,6 @@ export const WorkflowVariablesDropdown = ({
step={selectedStep}
onSelect={handleSubItemSelect}
onBack={handleBack}
shouldEnableSelectRelationObject={shouldEnableSelectRelationObject}
/>
) : (
<WorkflowVariablesDropdownFieldItems
@@ -24,14 +24,12 @@ type WorkflowVariablesDropdownAllItemsProps = {
step: StepOutputSchema;
onSelect: (value: string) => void;
onBack: () => void;
shouldEnableSelectRelationObject?: boolean;
};
export const WorkflowVariablesDropdownAllItems = ({
step,
onSelect,
onBack,
shouldEnableSelectRelationObject,
}: WorkflowVariablesDropdownAllItemsProps) => {
const { t } = useLingui();
const { getIcon } = useIcons();
@@ -65,25 +63,12 @@ export const WorkflowVariablesDropdownAllItems = ({
return;
}
const isRelationField = currentSubStep.object.isRelationField ?? false;
const isRelationObjectSelectable =
shouldEnableSelectRelationObject ?? false;
if (isRelationField && isRelationObjectSelectable) {
onSelect(
getVariableTemplateFromPath({
stepId: step.id,
path: currentPath,
}),
);
} else {
onSelect(
getVariableTemplateFromPath({
stepId: step.id,
path: [...currentPath, currentSubStep.object.fieldIdName],
}),
);
}
onSelect(
getVariableTemplateFromPath({
stepId: step.id,
path: [...currentPath, currentSubStep.object.fieldIdName],
}),
);
};
const displayedSubStepObject = getDisplayedSubStepObject();
@@ -37,10 +37,8 @@ export type FieldOutputSchema =
export type RecordOutputSchema = {
object: {
nameSingular: string;
fieldIdName: string;
objectMetadataId: string;
isRelationField?: boolean;
} & Leaf;
fields: Record<string, FieldOutputSchema>;
_outputSchemaType: 'RECORD';
@@ -116,7 +116,6 @@ describe('generateFakeFormResponse', () => {
icon: 'test-company-icon',
isLeaf: true,
label: 'Company',
nameSingular: 'company',
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
value: 'A company',
},
@@ -61,7 +61,6 @@ describe('generateFakeObjectRecordEvent', () => {
icon: 'test-company-icon',
label: 'Company',
value: 'A company',
nameSingular: 'company',
fieldIdName: 'properties.after.id',
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
},
@@ -93,7 +92,6 @@ describe('generateFakeObjectRecordEvent', () => {
icon: 'test-company-icon',
label: 'Company',
value: 'A company',
nameSingular: 'company',
fieldIdName: 'properties.after.id',
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
},
@@ -125,7 +123,6 @@ describe('generateFakeObjectRecordEvent', () => {
icon: 'test-company-icon',
label: 'Company',
value: 'A company',
nameSingular: 'company',
fieldIdName: 'properties.before.id',
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
},
@@ -157,7 +154,6 @@ describe('generateFakeObjectRecordEvent', () => {
icon: 'test-company-icon',
label: 'Company',
value: 'A company',
nameSingular: 'company',
fieldIdName: 'properties.before.id',
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
},
@@ -41,7 +41,6 @@ describe('generateFakeObjectRecord', () => {
icon: 'test-company-icon',
label: 'Company',
value: 'A company',
nameSingular: 'company',
fieldIdName: 'id',
objectMetadataId: '20202020-c03c-45d6-a4b0-04afe1357c5c',
},
@@ -30,8 +30,6 @@ const generateFakeObjectRecordEventWithPrefix = ({
objectMetadataInfo.objectMetadataItemWithFieldsMaps.icon ?? undefined,
label: objectMetadataInfo.objectMetadataItemWithFieldsMaps.labelSingular,
value: objectMetadataInfo.objectMetadataItemWithFieldsMaps.description,
nameSingular:
objectMetadataInfo.objectMetadataItemWithFieldsMaps.nameSingular,
fieldIdName: `${prefix}.id`,
objectMetadataId: objectMetadataInfo.objectMetadataItemWithFieldsMaps.id,
},
@@ -5,11 +5,9 @@ import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builde
export const generateFakeObjectRecord = ({
objectMetadataInfo,
depth = 0,
isRelationField,
}: {
objectMetadataInfo: ObjectMetadataInfo;
depth?: number;
isRelationField?: boolean;
}): RecordOutputSchema => {
return {
object: {
@@ -18,11 +16,8 @@ export const generateFakeObjectRecord = ({
objectMetadataInfo.objectMetadataItemWithFieldsMaps.icon ?? undefined,
label: objectMetadataInfo.objectMetadataItemWithFieldsMaps.labelSingular,
value: objectMetadataInfo.objectMetadataItemWithFieldsMaps.description,
nameSingular:
objectMetadataInfo.objectMetadataItemWithFieldsMaps.nameSingular,
fieldIdName: 'id',
objectMetadataId: objectMetadataInfo.objectMetadataItemWithFieldsMaps.id,
isRelationField,
},
fields: generateObjectRecordFields({
objectMetadataInfo,
@@ -60,7 +60,6 @@ export const generateObjectRecordFields = ({
objectMetadataMaps: objectMetadataInfo.objectMetadataMaps,
},
depth: depth + 1,
isRelationField: true,
}),
};
}
@@ -21,10 +21,8 @@ describe('evaluateFilterConditions', () => {
): ResolvedFilter => ({
id: 'filter1',
type: type,
label: 'Test Filter',
rightOperand,
operand,
displayValue: String(rightOperand),
stepFilterGroupId: 'group1',
leftOperand,
});
@@ -784,10 +782,8 @@ describe('evaluateFilterConditions', () => {
const filter: ResolvedFilter = {
id: 'filter1',
type: 'CURRENCY',
label: 'Currency Filter',
rightOperand: 'USD',
operand: ViewFilterOperand.Is,
displayValue: 'USD',
stepFilterGroupId: 'group1',
leftOperand: 'USD',
compositeFieldSubFieldName: 'currencyCode',
@@ -800,10 +796,8 @@ describe('evaluateFilterConditions', () => {
const filter: ResolvedFilter = {
id: 'filter1',
type: 'CURRENCY',
label: 'Currency Filter',
rightOperand: 100,
operand: ViewFilterOperand.GreaterThanOrEqual,
displayValue: '100',
stepFilterGroupId: 'group1',
leftOperand: 150,
compositeFieldSubFieldName: 'amountMicros',
@@ -1215,20 +1209,16 @@ describe('evaluateFilterConditions', () => {
{
id: 'filter1',
type: 'RELATION',
label: 'Name Filter',
rightOperand: 'John',
operand: ViewFilterOperand.Is,
displayValue: 'John',
stepFilterGroupId: 'group1',
leftOperand: 'John',
},
{
id: 'filter2',
type: 'NUMBER',
label: 'Age Filter',
rightOperand: 25,
operand: ViewFilterOperand.GreaterThanOrEqual,
displayValue: '25',
stepFilterGroupId: 'group1',
leftOperand: 30,
},
@@ -1244,20 +1234,16 @@ describe('evaluateFilterConditions', () => {
{
id: 'filter1',
type: 'RELATION',
label: 'Name Filter',
rightOperand: 'John',
operand: ViewFilterOperand.Is,
displayValue: 'John',
stepFilterGroupId: 'group1',
leftOperand: 'John',
},
{
id: 'filter2',
type: 'NUMBER',
label: 'Age Filter',
rightOperand: 25,
operand: ViewFilterOperand.GreaterThanOrEqual,
displayValue: '25',
stepFilterGroupId: 'group1',
leftOperand: 20, // This will fail
},
@@ -1283,20 +1269,16 @@ describe('evaluateFilterConditions', () => {
{
id: 'filter1',
type: 'RELATION',
label: 'Name Filter',
rightOperand: 'John',
operand: ViewFilterOperand.Is,
displayValue: 'John',
stepFilterGroupId: 'group1',
leftOperand: 'John',
},
{
id: 'filter2',
type: 'NUMBER',
label: 'Age Filter',
rightOperand: 25,
operand: ViewFilterOperand.GreaterThanOrEqual,
displayValue: '25',
stepFilterGroupId: 'group1',
leftOperand: 30,
},
@@ -1319,20 +1301,16 @@ describe('evaluateFilterConditions', () => {
{
id: 'filter1',
type: 'RELATION',
label: 'Name Filter',
rightOperand: 'John',
operand: ViewFilterOperand.Is,
displayValue: 'John',
stepFilterGroupId: 'group1',
leftOperand: 'Jane', // This will fail
},
{
id: 'filter2',
type: 'NUMBER',
label: 'Age Filter',
rightOperand: 25,
operand: ViewFilterOperand.GreaterThanOrEqual,
displayValue: '25',
stepFilterGroupId: 'group1',
leftOperand: 30,
},
@@ -1357,20 +1335,16 @@ describe('evaluateFilterConditions', () => {
{
id: 'filter1',
type: 'RELATION',
label: 'Name Filter',
rightOperand: 'John',
operand: ViewFilterOperand.Is,
displayValue: 'John',
stepFilterGroupId: 'group1',
leftOperand: 'Jane', // This will fail
},
{
id: 'filter2',
type: 'NUMBER',
label: 'Age Filter',
rightOperand: 25,
operand: ViewFilterOperand.GreaterThanOrEqual,
displayValue: '25',
stepFilterGroupId: 'group1',
leftOperand: 30, // This will pass
},
@@ -1393,20 +1367,16 @@ describe('evaluateFilterConditions', () => {
{
id: 'filter1',
type: 'RELATION',
label: 'Name Filter',
rightOperand: 'John',
operand: ViewFilterOperand.Is,
displayValue: 'John',
stepFilterGroupId: 'group1',
leftOperand: 'Jane', // This will fail
},
{
id: 'filter2',
type: 'NUMBER',
label: 'Age Filter',
rightOperand: 25,
operand: ViewFilterOperand.GreaterThanOrEqual,
displayValue: '25',
stepFilterGroupId: 'group1',
leftOperand: 20, // This will fail
},
@@ -1435,20 +1405,16 @@ describe('evaluateFilterConditions', () => {
{
id: 'filter1',
type: 'RELATION',
label: 'Name Filter',
rightOperand: 'John',
operand: ViewFilterOperand.Is,
displayValue: 'John',
stepFilterGroupId: 'group1',
leftOperand: 'John',
},
{
id: 'filter2',
type: 'NUMBER',
label: 'Age Filter',
rightOperand: 25,
operand: ViewFilterOperand.GreaterThanOrEqual,
displayValue: '25',
stepFilterGroupId: 'group2',
leftOperand: 30,
},
@@ -1485,30 +1451,24 @@ describe('evaluateFilterConditions', () => {
{
id: 'filter1',
type: 'RELATION',
label: 'Filter 1',
rightOperand: 'John',
operand: ViewFilterOperand.Is,
displayValue: 'John',
stepFilterGroupId: 'child1',
leftOperand: 'Jane', // This will fail
},
{
id: 'filter2',
type: 'RELATION',
label: 'Filter 2',
rightOperand: 'Smith',
operand: ViewFilterOperand.Is,
displayValue: 'Smith',
stepFilterGroupId: 'child1',
leftOperand: 'Smith', // This will pass (OR group passes)
},
{
id: 'filter3',
type: 'NUMBER',
label: 'Filter 3',
rightOperand: 25,
operand: ViewFilterOperand.GreaterThanOrEqual,
displayValue: '25',
stepFilterGroupId: 'child2',
leftOperand: 30, // This will pass (AND group passes)
},
@@ -1548,10 +1508,8 @@ describe('evaluateFilterConditions', () => {
{
id: 'filter1',
type: 'RELATION',
label: 'Name Filter',
rightOperand: 'John',
operand: ViewFilterOperand.Is,
displayValue: 'John',
stepFilterGroupId: 'nonexistent',
leftOperand: 'John',
},
@@ -49,7 +49,6 @@ const settings: WorkflowFormActionSettings = {
value: 'A company',
isLeaf: true,
fieldIdName: 'id',
nameSingular: 'company',
objectMetadataId: '123e4567-e89b-12d3-a456-426614174000',
},
_outputSchemaType: 'RECORD',
@@ -15,13 +15,12 @@ export type StepFilterGroup = {
export type StepFilter = {
id: string;
type: string;
label: string;
stepOutputKey: string;
operand: ViewFilterOperand;
value: string;
displayValue: string;
stepFilterGroupId: string;
positionInStepFilterGroup?: number;
fieldMetadataId?: string;
compositeFieldSubFieldName?: string;
isFullRecord?: boolean;
};