diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx
index fe4947fdb..8026fdf62 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx
@@ -5,7 +5,6 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
import {
BaseOutputSchema,
LinkOutputSchema,
- OutputSchema,
StepOutputSchema,
} from '@/workflow/workflow-variables/types/StepOutputSchema';
import { isBaseOutputSchema } from '@/workflow/workflow-variables/utils/isBaseOutputSchema';
@@ -15,6 +14,8 @@ import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { workflowDiagramTriggerNodeSelectionState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionState';
import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState';
import { WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/constants/WorkflowServerlessFunctionTabListComponentId';
+import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath';
+import { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel';
import { isLinkOutputSchema } from '@/workflow/workflow-variables/utils/isLinkOutputSchema';
import { useState } from 'react';
import { useSetRecoilState } from 'recoil';
@@ -48,22 +49,8 @@ export const WorkflowVariablesDropdownFieldItems = ({
workflowDiagramTriggerNodeSelectionState,
);
- const getCurrentSubStep = (): OutputSchema => {
- let currentSubStep = step.outputSchema;
-
- for (const key of currentPath) {
- if (isRecordOutputSchema(currentSubStep)) {
- currentSubStep = currentSubStep.fields[key]?.value;
- } else if (isBaseOutputSchema(currentSubStep)) {
- currentSubStep = currentSubStep[key]?.value;
- }
- }
-
- return currentSubStep;
- };
-
const getDisplayedSubStepFields = () => {
- const currentSubStep = getCurrentSubStep();
+ const currentSubStep = getCurrentSubStepFromPath(step, currentPath);
if (isLinkOutputSchema(currentSubStep)) {
return { link: currentSubStep.link };
@@ -75,7 +62,7 @@ export const WorkflowVariablesDropdownFieldItems = ({
};
const handleSelectField = (key: string) => {
- const currentSubStep = getCurrentSubStep();
+ const currentSubStep = getCurrentSubStepFromPath(step, currentPath);
const handleSelectBaseOutputSchema = (
baseOutputSchema: BaseOutputSchema,
@@ -115,20 +102,6 @@ export const WorkflowVariablesDropdownFieldItems = ({
}
};
- const getHeaderLabel = () => {
- if (currentPath.length === 0) {
- return step.name;
- }
-
- const subStepName = currentPath.at(-1);
-
- if (isDefined(subStepName) && isRecordOutputSchema(step.outputSchema)) {
- return step.outputSchema.fields[subStepName].label;
- }
-
- return subStepName;
- };
-
const displayedObject = getDisplayedSubStepFields();
const options = displayedObject ? Object.entries(displayedObject) : [];
@@ -147,7 +120,9 @@ export const WorkflowVariablesDropdownFieldItems = ({
onClick={goBack}
style={{ position: 'fixed' }}
>
-
+
{
- let currentSubStep = step.outputSchema;
-
- for (const key of currentPath) {
- if (isRecordOutputSchema(currentSubStep)) {
- currentSubStep = currentSubStep.fields[key]?.value;
- } else if (isBaseOutputSchema(currentSubStep)) {
- currentSubStep = currentSubStep[key]?.value;
- }
- }
-
- return currentSubStep;
- };
-
const getDisplayedSubStepFields = () => {
- const currentSubStep = getCurrentSubStep();
+ const currentSubStep = getCurrentSubStepFromPath(step, currentPath);
if (isRecordOutputSchema(currentSubStep)) {
return currentSubStep.fields;
@@ -57,7 +42,7 @@ export const WorkflowVariablesDropdownObjectItems = ({
};
const getDisplayedSubStepObject = () => {
- const currentSubStep = getCurrentSubStep();
+ const currentSubStep = getCurrentSubStepFromPath(step, currentPath);
if (!isRecordOutputSchema(currentSubStep)) {
return;
@@ -67,7 +52,7 @@ export const WorkflowVariablesDropdownObjectItems = ({
};
const handleSelectObject = () => {
- const currentSubStep = getCurrentSubStep();
+ const currentSubStep = getCurrentSubStepFromPath(step, currentPath);
if (!isRecordOutputSchema(currentSubStep)) {
return;
@@ -91,8 +76,6 @@ export const WorkflowVariablesDropdownObjectItems = ({
}
};
- const headerLabel = currentPath.length === 0 ? step.name : currentPath.at(-1);
-
const displayedSubStepObject = getDisplayedSubStepObject();
const shouldDisplaySubStepObject = searchInputValue
@@ -116,7 +99,9 @@ export const WorkflowVariablesDropdownObjectItems = ({
return (
<>
-
+
{
+ it('should return the current sub step from the path', () => {
+ const path = ['company', 'name'];
+ expect(getCurrentSubStepFromPath(mockStep, path)).toBe('Twenty');
+ });
+
+ it('should return undefined when the path is not valid', () => {
+ const path = ['company', 'unknown'];
+ expect(getCurrentSubStepFromPath(mockStep, path)).toBe(undefined);
+ });
+});
diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getStepHeaderLabel.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getStepHeaderLabel.test.ts
new file mode 100644
index 000000000..28a4da3e6
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getStepHeaderLabel.test.ts
@@ -0,0 +1,54 @@
+import { StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
+import { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel';
+
+const mockStep = {
+ id: 'step-1',
+ name: 'Step 1',
+ outputSchema: {
+ company: {
+ isLeaf: false,
+ icon: 'company',
+ label: 'Company',
+ value: {
+ object: {
+ nameSingular: 'company',
+ fieldIdName: 'id',
+ label: 'Company',
+ value: 'John',
+ isLeaf: true,
+ },
+ fields: {
+ name: { label: 'Name', value: 'Twenty', isLeaf: true },
+ address: {
+ label: 'Address',
+ value: {
+ street: { label: 'Street', value: '123 Main St', isLeaf: true },
+ city: { label: 'City', value: 'New York', isLeaf: true },
+ state: { label: 'State', value: 'NY', isLeaf: true },
+ zip: { label: 'Zip', value: '10001', isLeaf: true },
+ },
+ isLeaf: false,
+ },
+ },
+ _outputSchemaType: 'RECORD',
+ },
+ },
+ },
+} satisfies StepOutputSchema;
+
+describe('getStepHeaderLabel', () => {
+ it('should return the step name when the path is empty', () => {
+ const currentPath: string[] = [];
+ expect(getStepHeaderLabel(mockStep, currentPath)).toBe('Step 1');
+ });
+
+ it('should return the field label when the path is not empty', () => {
+ const currentPath: string[] = ['company'];
+ expect(getStepHeaderLabel(mockStep, currentPath)).toBe('Company');
+ });
+
+ it('should return the nested field label when the path is not empty', () => {
+ const currentPath: string[] = ['company', 'address'];
+ expect(getStepHeaderLabel(mockStep, currentPath)).toBe('Address');
+ });
+});
diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getCurrentSubStepFromPath.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getCurrentSubStepFromPath.ts
new file mode 100644
index 000000000..3ccbb4182
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getCurrentSubStepFromPath.ts
@@ -0,0 +1,23 @@
+import {
+ OutputSchema,
+ StepOutputSchema,
+} from '@/workflow/workflow-variables/types/StepOutputSchema';
+import { isBaseOutputSchema } from '@/workflow/workflow-variables/utils/isBaseOutputSchema';
+import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema';
+
+export const getCurrentSubStepFromPath = (
+ step: StepOutputSchema,
+ path: string[],
+): OutputSchema => {
+ let currentSubStep = step.outputSchema;
+
+ for (const key of path) {
+ if (isRecordOutputSchema(currentSubStep)) {
+ currentSubStep = currentSubStep.fields[key]?.value;
+ } else if (isBaseOutputSchema(currentSubStep)) {
+ currentSubStep = currentSubStep[key]?.value;
+ }
+ }
+
+ return currentSubStep;
+};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getStepHeaderLabel.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getStepHeaderLabel.ts
new file mode 100644
index 000000000..3b734eaab
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getStepHeaderLabel.ts
@@ -0,0 +1,40 @@
+import { StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
+import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath';
+import { isBaseOutputSchema } from '@/workflow/workflow-variables/utils/isBaseOutputSchema';
+import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema';
+import { isDefined } from '~/utils/isDefined';
+
+export const getStepHeaderLabel = (
+ step: StepOutputSchema,
+ currentPath: string[],
+) => {
+ if (currentPath.length === 0) {
+ return step.name;
+ }
+
+ const subStepName = currentPath.at(-1);
+ const previousSubStep = getCurrentSubStepFromPath(
+ step,
+ currentPath.slice(0, -1),
+ );
+
+ if (!subStepName) {
+ return step.name;
+ }
+
+ if (
+ isRecordOutputSchema(previousSubStep) &&
+ isDefined(previousSubStep.fields[subStepName]?.label)
+ ) {
+ return previousSubStep.fields[subStepName].label;
+ }
+
+ if (
+ isBaseOutputSchema(previousSubStep) &&
+ isDefined(previousSubStep[subStepName]?.label)
+ ) {
+ return previousSubStep[subStepName].label;
+ }
+
+ return subStepName;
+};
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event.ts
index be7a3f86e..a18c4d098 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event.ts
@@ -46,11 +46,9 @@ export const generateFakeObjectRecordEvent = (
if (action === DatabaseEventAction.CREATED) {
return {
...baseResult,
- properties: {
+ 'properties.after': {
isLeaf: false,
- value: {
- after: { isLeaf: false, value: after, label: 'After Creation' },
- },
+ value: after,
label: 'Record Fields',
},
};
@@ -75,11 +73,9 @@ export const generateFakeObjectRecordEvent = (
if (action === DatabaseEventAction.DELETED) {
return {
...baseResult,
- properties: {
+ 'properties.before': {
isLeaf: false,
- value: {
- before: { isLeaf: false, value: before, label: 'Before Deletion' },
- },
+ value: before,
label: 'Record Fields',
},
};
@@ -88,15 +84,9 @@ export const generateFakeObjectRecordEvent = (
if (action === DatabaseEventAction.DESTROYED) {
return {
...baseResult,
- properties: {
+ 'properties.before': {
isLeaf: false,
- value: {
- before: {
- isLeaf: false,
- value: before,
- label: 'Before Permanent Deletion',
- },
- },
+ value: before,
label: 'Record Fields',
},
};