diff --git a/packages/twenty-front/src/modules/ai/hooks/useAiAgentOutputSchema.ts b/packages/twenty-front/src/modules/ai/hooks/useAiAgentOutputSchema.ts index a4a5cf646f..4aecfbd6fe 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useAiAgentOutputSchema.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useAiAgentOutputSchema.ts @@ -1,14 +1,13 @@ -import { type WorkflowAiAgentAction } from '@/workflow/types/Workflow'; import { type OutputSchemaField } from '@/ai/constants/OutputFieldTypeOptions'; -import { type BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type WorkflowAiAgentAction } from '@/workflow/types/Workflow'; +import { type BaseOutputSchemaDeprecated } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; import { useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { useDebouncedCallback } from 'use-debounce'; import { v4 } from 'uuid'; -import { getFieldIcon } from '../utils/getFieldIcon'; export const useAiAgentOutputSchema = ( - outputSchema?: BaseOutputSchema, + outputSchema?: BaseOutputSchemaDeprecated, onActionUpdate?: (action: WorkflowAiAgentAction) => void, action?: WorkflowAiAgentAction, readonly?: boolean, @@ -18,7 +17,6 @@ export const useAiAgentOutputSchema = ( id: v4(), name, type: field.type, - description: field.description, })), ); @@ -28,16 +26,14 @@ export const useAiAgentOutputSchema = ( return; } - const newOutputSchema = fields.reduce( + const newOutputSchema = fields.reduce( (schema, field) => { if (isDefined(field.name)) { schema[field.name] = { isLeaf: true, type: field.type, value: null, - icon: getFieldIcon(field.type), label: field.name, - description: field.description, }; } return schema; diff --git a/packages/twenty-front/src/modules/serverless-functions/utils/__tests__/getFunctionOutputSchema.test.ts b/packages/twenty-front/src/modules/serverless-functions/utils/__tests__/getFunctionOutputSchema.test.ts index 9f425ffd1b..a3a9e13898 100644 --- a/packages/twenty-front/src/modules/serverless-functions/utils/__tests__/getFunctionOutputSchema.test.ts +++ b/packages/twenty-front/src/modules/serverless-functions/utils/__tests__/getFunctionOutputSchema.test.ts @@ -14,25 +14,23 @@ describe('getFunctionOutputSchema', () => { isLeaf: true, type: 'unknown', value: null, - icon: 'IconVariable', label: 'a', }, b: { isLeaf: true, type: 'string', value: 'b', - icon: 'IconVariable', label: 'b', }, c: { isLeaf: false, - icon: 'IconVariable', + type: 'object', + label: 'c', value: { cc: { isLeaf: true, type: 'number', value: 1, - icon: 'IconVariable', label: 'cc', }, }, @@ -41,14 +39,12 @@ describe('getFunctionOutputSchema', () => { isLeaf: true, type: 'boolean', value: true, - icon: 'IconVariable', label: 'd', }, e: { isLeaf: true, type: 'array', value: [1, 2, 3], - icon: 'IconVariable', label: 'e', }, }; diff --git a/packages/twenty-front/src/modules/serverless-functions/utils/getFunctionOutputSchema.ts b/packages/twenty-front/src/modules/serverless-functions/utils/getFunctionOutputSchema.ts index 5a34c50f6b..37a726e5fc 100644 --- a/packages/twenty-front/src/modules/serverless-functions/utils/getFunctionOutputSchema.ts +++ b/packages/twenty-front/src/modules/serverless-functions/utils/getFunctionOutputSchema.ts @@ -1,9 +1,11 @@ -import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema'; -import { type BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { + type BaseOutputSchemaV2, + type LeafType, +} from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; import { isObject } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; -const getValueType = (value: any): InputSchemaPropertyType => { +const getValueType = (value: any): LeafType => { if (!isDefined(value) || value === null) { return 'unknown'; } @@ -19,20 +21,18 @@ const getValueType = (value: any): InputSchemaPropertyType => { if (Array.isArray(value)) { return 'array'; } - if (isObject(value)) { - return 'object'; - } return 'unknown'; }; export const getFunctionOutputSchema = (testResult: object) => { return testResult ? Object.entries(testResult).reduce( - (acc: BaseOutputSchema, [key, value]) => { + (acc: BaseOutputSchemaV2, [key, value]) => { if (isObject(value) && !Array.isArray(value)) { acc[key] = { isLeaf: false, - icon: 'IconVariable', + type: 'object', + label: key, value: getFunctionOutputSchema(value), }; } else { @@ -40,7 +40,6 @@ export const getFunctionOutputSchema = (testResult: object) => { isLeaf: true, value, type: getValueType(value), - icon: 'IconVariable', label: key, }; } diff --git a/packages/twenty-front/src/modules/workflow/hooks/useStepsOutputSchema.ts b/packages/twenty-front/src/modules/workflow/hooks/useStepsOutputSchema.ts index c01de8dfe3..5f8ec90939 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useStepsOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useStepsOutputSchema.ts @@ -5,9 +5,9 @@ import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/ import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerDefaultLabel'; import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon'; import { - type OutputSchema, - type StepOutputSchema, -} from '@/workflow/workflow-variables/types/StepOutputSchema'; + type OutputSchemaV2, + type StepOutputSchemaV2, +} from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { TRIGGER_STEP_ID } from 'twenty-shared/workflow'; @@ -17,11 +17,12 @@ export const useStepsOutputSchema = () => { ({ set }) => (workflowVersion: WorkflowVersion) => { workflowVersion.steps?.forEach((step) => { - const stepOutputSchema: StepOutputSchema = { + const stepOutputSchema: StepOutputSchemaV2 = { id: step.id, name: step.name, + type: step.type, icon: getActionIcon(step.type), - outputSchema: step.settings?.outputSchema as OutputSchema, + outputSchema: step.settings?.outputSchema as OutputSchemaV2, }; set( @@ -37,13 +38,14 @@ export const useStepsOutputSchema = () => { if (isDefined(trigger)) { const triggerIconKey = getTriggerIcon(trigger); - const triggerOutputSchema: StepOutputSchema = { + const triggerOutputSchema: StepOutputSchemaV2 = { id: TRIGGER_STEP_ID, name: isDefined(trigger.name) ? trigger.name : getTriggerDefaultLabel(trigger), + type: trigger.type, icon: triggerIconKey, - outputSchema: trigger.settings?.outputSchema as OutputSchema, + outputSchema: trigger.settings?.outputSchema as OutputSchemaV2, }; set( diff --git a/packages/twenty-front/src/modules/workflow/states/selectors/stepsOutputSchemaFamilySelector.ts b/packages/twenty-front/src/modules/workflow/states/selectors/stepsOutputSchemaFamilySelector.ts index 14e2a29d29..8606525e2b 100644 --- a/packages/twenty-front/src/modules/workflow/states/selectors/stepsOutputSchemaFamilySelector.ts +++ b/packages/twenty-front/src/modules/workflow/states/selectors/stepsOutputSchemaFamilySelector.ts @@ -1,11 +1,11 @@ import { stepsOutputSchemaFamilyState } from '@/workflow/states/stepsOutputSchemaFamilyState'; import { getStepOutputSchemaFamilyStateKey } from '@/workflow/utils/getStepOutputSchemaFamilyStateKey'; -import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; import { selectorFamily } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; export const stepsOutputSchemaFamilySelector = selectorFamily< - StepOutputSchema[], + StepOutputSchemaV2[], { workflowVersionId: string; stepIds: string[] } >({ key: 'stepsOutputSchemaFamilySelector', diff --git a/packages/twenty-front/src/modules/workflow/states/stepsOutputSchemaFamilyState.ts b/packages/twenty-front/src/modules/workflow/states/stepsOutputSchemaFamilyState.ts index 82f7f58fda..9599b6c657 100644 --- a/packages/twenty-front/src/modules/workflow/states/stepsOutputSchemaFamilyState.ts +++ b/packages/twenty-front/src/modules/workflow/states/stepsOutputSchemaFamilyState.ts @@ -1,8 +1,8 @@ import { createFamilyState } from '@/ui/utilities/state/utils/createFamilyState'; -import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; export const stepsOutputSchemaFamilyState = createFamilyState< - StepOutputSchema | null, + StepOutputSchemaV2 | null, string | undefined >({ key: 'stepsOutputSchemaFamilyState', diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx index 8d05d31371..e0c9b8fa36 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx @@ -7,7 +7,7 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { useWorkflowActionHeader } from '@/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; -import { type BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type BaseOutputSchemaDeprecated } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; import { useRecoilValue } from 'recoil'; @@ -47,7 +47,7 @@ export const WorkflowEditActionAiAgent = ({ }); const { handleOutputSchemaChange, outputFields } = useAiAgentOutputSchema( - action.settings.outputSchema as BaseOutputSchema, + action.settings.outputSchema as BaseOutputSchemaDeprecated, actionOptions.readonly === true ? undefined : actionOptions.onActionUpdate, action, actionOptions.readonly, diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowOutputFieldTypeSelector.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowOutputFieldTypeSelector.tsx index 24224ec877..a67f41b496 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowOutputFieldTypeSelector.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowOutputFieldTypeSelector.tsx @@ -1,7 +1,7 @@ +import { OUTPUT_FIELD_TYPE_OPTIONS } from '@/ai/constants/OutputFieldTypeOptions'; import { Select } from '@/ui/input/components/Select'; import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema'; import { t } from '@lingui/core/macro'; -import { OUTPUT_FIELD_TYPE_OPTIONS } from '@/ai/constants/OutputFieldTypeOptions'; type WorkflowOutputFieldTypeSelectorProps = { value?: InputSchemaPropertyType; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowDropdownStepOutputItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowDropdownStepOutputItems.tsx index 6b6130ff06..967c2c5ac7 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowDropdownStepOutputItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowDropdownStepOutputItems.tsx @@ -2,9 +2,9 @@ import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenu 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 { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; 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'; @@ -13,12 +13,13 @@ import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/ste 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 { isRecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; 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 { searchVariableThroughOutputSchemaV2 } from '@/workflow/workflow-variables/utils/searchVariableThroughOutputSchemaV2'; import { useLingui } from '@lingui/react/macro'; import { useRecoilCallback } from 'recoil'; import { type StepFilter } from 'twenty-shared/types'; @@ -32,7 +33,7 @@ import { MenuItemSelect } from 'twenty-ui/navigation'; type WorkflowDropdownStepOutputItemsProps = { stepFilter: StepFilter; - step: StepOutputSchema; + step: StepOutputSchemaV2; onSelect: () => void; onBack: () => void; }; @@ -51,6 +52,7 @@ export const WorkflowDropdownStepOutputItems = ({ useGetFieldMetadataItemByIdOrThrow(); const workflowVersionId = useWorkflowVersionIdOrThrow(); + const { objectMetadataItems } = useObjectMetadataItems(); const updateStepFilter = useRecoilCallback( ({ snapshot }) => @@ -75,8 +77,9 @@ export const WorkflowDropdownStepOutputItems = ({ .getValue(); const { variableType, fieldMetadataId, compositeFieldSubFieldName } = - searchVariableThroughOutputSchema({ + searchVariableThroughOutputSchemaV2({ stepOutputSchema: currentStepOutputSchema, + stepType: step.type, rawVariableName, isFullRecord: false, }); @@ -112,6 +115,7 @@ export const WorkflowDropdownStepOutputItems = ({ }, [ workflowVersionId, + step.type, getFieldMetadataItemByIdOrThrow, upsertStepFilterSettings, stepFilter, @@ -142,7 +146,7 @@ export const WorkflowDropdownStepOutputItems = ({ const getDisplayedSubStepObject = () => { const currentSubStep = getCurrentSubStepFromPath(step, currentPath); - if (!isRecordOutputSchema(currentSubStep)) { + if (!isRecordOutputSchemaV2(currentSubStep)) { return; } @@ -152,14 +156,14 @@ export const WorkflowDropdownStepOutputItems = ({ const handleSelectObject = () => { const currentSubStep = getCurrentSubStepFromPath(step, currentPath); - if (!isRecordOutputSchema(currentSubStep)) { + if (!isRecordOutputSchemaV2(currentSubStep)) { return; } updateStepFilter({ rawVariableName: getVariableTemplateFromPath({ stepId: step.id, - path: [...currentPath, currentSubStep.object.fieldIdName], + path: [...currentPath, 'id'], }), isFullRecord: true, }); @@ -168,16 +172,22 @@ export const WorkflowDropdownStepOutputItems = ({ const displayedSubStepObject = getDisplayedSubStepObject(); + const subStepObjectMetadataItem = isDefined( + displayedSubStepObject?.objectMetadataId, + ) + ? objectMetadataItems.find( + (item) => item.id === displayedSubStepObject?.objectMetadataId, + ) + : undefined; + const shouldDisplaySubStepObject = searchInputValue - ? displayedSubStepObject?.label && - displayedSubStepObject.label + ? isDefined(subStepObjectMetadataItem) && + subStepObjectMetadataItem.labelSingular .toLowerCase() .includes(searchInputValue.toLowerCase()) : true; - const shouldDisplayObject = - shouldDisplaySubStepObject && displayedSubStepObject?.label; - const nameSingular = displayedSubStepObject?.nameSingular; + const objectLabel = subStepObjectMetadataItem?.labelSingular; return ( @@ -200,22 +210,22 @@ export const WorkflowDropdownStepOutputItems = ({ /> - {shouldDisplayObject && ( + {shouldDisplaySubStepObject && ( )} - {filteredOptions.length > 0 && shouldDisplayObject && ( + {filteredOptions.length > 0 && shouldDisplaySubStepObject && ( )} {filteredOptions.map(([key, subStep]) => ( diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx index 251d4e25fc..a4d5ec813c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx @@ -4,11 +4,11 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; 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 { WorkflowVariablesDropdownWorkflowStepItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems'; +import { WorkflowVariablesDropdownSteps } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownSteps'; import { useAvailableVariablesInWorkflowStep } from '@/workflow/workflow-variables/hooks/useAvailableVariablesInWorkflowStep'; import { useSearchVariable } from '@/workflow/workflow-variables/hooks/useSearchVariable'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; -import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; import { extractRawVariableNamePart } from '@/workflow/workflow-variables/utils/extractRawVariableNamePart'; import { useTheme } from '@emotion/react'; import { useLingui } from '@lingui/react/macro'; @@ -51,7 +51,7 @@ export const WorkflowStepFilterFieldSelect = ({ : undefined; const [selectedStep, setSelectedStep] = useState< - StepOutputSchema | undefined + StepOutputSchemaV2 | undefined >(initialStep); const stepId = extractRawVariableNamePart({ @@ -137,7 +137,7 @@ export const WorkflowStepFilterFieldSelect = ({ } dropdownComponents={ !isDefined(selectedStep) ? ( - { it('should convert simple object schema to JSON', () => { - const schema: BaseOutputSchema = { + const schema: BaseOutputSchemaV2 = { name: { isLeaf: true, type: 'string', label: 'name', value: 'John', - icon: 'IconText', }, age: { isLeaf: true, type: 'number', label: 'age', value: 25, - icon: 'IconNumber', }, isActive: { isLeaf: true, type: 'boolean', label: 'isActive', value: true, - icon: 'IconCheckbox', }, }; const expected = { @@ -35,27 +32,24 @@ describe('convertOutputSchemaToJson', () => { }); it('should convert array schema to JSON array', () => { - const schema: BaseOutputSchema = { + const schema: BaseOutputSchemaV2 = { '0': { isLeaf: true, type: 'string', label: '0', value: 'first', - icon: 'IconText', }, '1': { isLeaf: true, type: 'string', label: '1', value: 'second', - icon: 'IconText', }, '2': { isLeaf: true, type: 'string', label: '2', value: 'third', - icon: 'IconText', }, }; const expected = ['first', 'second', 'third']; @@ -63,9 +57,10 @@ describe('convertOutputSchemaToJson', () => { }); it('should convert nested object schema to JSON', () => { - const schema: BaseOutputSchema = { + const schema: BaseOutputSchemaV2 = { user: { isLeaf: false, + type: 'object', label: 'user', value: { name: { @@ -73,17 +68,16 @@ describe('convertOutputSchemaToJson', () => { type: 'string', label: 'name', value: 'John', - icon: 'IconText', }, age: { isLeaf: true, type: 'number', label: 'age', value: 25, - icon: 'IconNumber', }, address: { isLeaf: false, + type: 'object', label: 'address', value: { city: { @@ -91,20 +85,16 @@ describe('convertOutputSchemaToJson', () => { type: 'string', label: 'city', value: 'New York', - icon: 'IconText', }, country: { isLeaf: true, type: 'string', label: 'country', value: 'USA', - icon: 'IconText', }, }, - icon: 'IconBox', }, }, - icon: 'IconBox', }, }; const expected = { @@ -121,9 +111,10 @@ describe('convertOutputSchemaToJson', () => { }); it('should convert nested array schema to JSON', () => { - const schema: BaseOutputSchema = { + const schema: BaseOutputSchemaV2 = { '0': { isLeaf: false, + type: 'object', label: '0', value: { '0': { @@ -131,20 +122,18 @@ describe('convertOutputSchemaToJson', () => { type: 'number', label: '0', value: 1, - icon: 'IconNumber', }, '1': { isLeaf: true, type: 'number', label: '1', value: 2, - icon: 'IconNumber', }, }, - icon: 'IconBox', }, '1': { isLeaf: false, + type: 'object', label: '1', value: { '0': { @@ -152,17 +141,14 @@ describe('convertOutputSchemaToJson', () => { type: 'number', label: '0', value: 3, - icon: 'IconNumber', }, '1': { isLeaf: true, type: 'number', label: '1', value: 4, - icon: 'IconNumber', }, }, - icon: 'IconBox', }, }; const expected = [ @@ -173,13 +159,15 @@ describe('convertOutputSchemaToJson', () => { }); it('should handle mixed array and object schema', () => { - const schema: BaseOutputSchema = { + const schema: BaseOutputSchemaV2 = { users: { isLeaf: false, + type: 'object', label: 'users', value: { '0': { isLeaf: false, + type: 'object', label: '0', value: { name: { @@ -187,20 +175,18 @@ describe('convertOutputSchemaToJson', () => { type: 'string', label: 'name', value: 'John', - icon: 'IconText', }, age: { isLeaf: true, type: 'number', label: 'age', value: 25, - icon: 'IconNumber', }, }, - icon: 'IconBox', }, '1': { isLeaf: false, + type: 'object', label: '1', value: { name: { @@ -208,23 +194,20 @@ describe('convertOutputSchemaToJson', () => { type: 'string', label: 'name', value: 'Jane', - icon: 'IconText', }, age: { isLeaf: true, type: 'number', label: 'age', value: 30, - icon: 'IconNumber', }, }, - icon: 'IconBox', }, }, - icon: 'IconBox', }, metadata: { isLeaf: false, + type: 'object', label: 'metadata', value: { count: { @@ -232,17 +215,14 @@ describe('convertOutputSchemaToJson', () => { type: 'number', label: 'count', value: 2, - icon: 'IconNumber', }, active: { isLeaf: true, type: 'boolean', label: 'active', value: true, - icon: 'IconCheckbox', }, }, - icon: 'IconBox', }, }; const expected = { @@ -265,20 +245,18 @@ describe('convertOutputSchemaToJson', () => { }); it('should handle null values', () => { - const schema: BaseOutputSchema = { + const schema: BaseOutputSchemaV2 = { name: { isLeaf: true, type: 'unknown', label: 'name', value: null, - icon: 'IconQuestionMark', }, age: { isLeaf: true, type: 'unknown', label: 'age', value: null, - icon: 'IconQuestionMark', }, }; const expected = { @@ -289,23 +267,26 @@ describe('convertOutputSchemaToJson', () => { }); it('should handle empty object schema', () => { - const schema: BaseOutputSchema = {}; + const schema: BaseOutputSchemaV2 = {}; const expected = {}; expect(convertOutputSchemaToJson(schema)).toEqual(expected); }); it('should handle complex nested structure', () => { - const schema: BaseOutputSchema = { + const schema: BaseOutputSchemaV2 = { data: { isLeaf: false, + type: 'object', label: 'data', value: { users: { isLeaf: false, + type: 'object', label: 'users', value: { '0': { isLeaf: false, + type: 'object', label: '0', value: { id: { @@ -313,17 +294,16 @@ describe('convertOutputSchemaToJson', () => { type: 'number', label: 'id', value: 1, - icon: 'IconNumber', }, name: { isLeaf: true, type: 'string', label: 'name', value: 'John', - icon: 'IconText', }, roles: { isLeaf: false, + type: 'object', label: 'roles', value: { '0': { @@ -331,20 +311,18 @@ describe('convertOutputSchemaToJson', () => { type: 'string', label: '0', value: 'admin', - icon: 'IconText', }, '1': { isLeaf: true, type: 'string', label: '1', value: 'user', - icon: 'IconText', }, }, - icon: 'IconBox', }, settings: { isLeaf: false, + type: 'object', label: 'settings', value: { theme: { @@ -352,26 +330,22 @@ describe('convertOutputSchemaToJson', () => { type: 'string', label: 'theme', value: 'dark', - icon: 'IconText', }, notifications: { isLeaf: true, type: 'boolean', label: 'notifications', value: true, - icon: 'IconCheckbox', }, }, - icon: 'IconBox', }, }, - icon: 'IconBox', }, }, - icon: 'IconBox', }, metadata: { isLeaf: false, + type: 'object', label: 'metadata', value: { total: { @@ -379,20 +353,16 @@ describe('convertOutputSchemaToJson', () => { type: 'number', label: 'total', value: 1, - icon: 'IconNumber', }, page: { isLeaf: true, type: 'number', label: 'page', value: 1, - icon: 'IconNumber', }, }, - icon: 'IconBox', }, }, - icon: 'IconBox', }, }; const expected = { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/__tests__/getHttpRequestOutputSchema.test.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/__tests__/getHttpRequestOutputSchema.test.ts index 056a48ca06..ee53898119 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/__tests__/getHttpRequestOutputSchema.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/__tests__/getHttpRequestOutputSchema.test.ts @@ -23,14 +23,12 @@ describe('getHttpRequestOutputSchema', () => { type: 'string', label: 'name', value: 'John', - icon: 'IconAbc', }, email: { isLeaf: true, type: 'string', label: 'email', value: 'john@example.com', - icon: 'IconAbc', }, }; expect(getHttpRequestOutputSchema(input)).toEqual(expected); @@ -44,14 +42,12 @@ describe('getHttpRequestOutputSchema', () => { type: 'number', label: 'age', value: 25, - icon: 'IconText', }, score: { isLeaf: true, type: 'number', label: 'score', value: 98.5, - icon: 'IconText', }, }; expect(getHttpRequestOutputSchema(input)).toEqual(expected); @@ -65,14 +61,12 @@ describe('getHttpRequestOutputSchema', () => { type: 'boolean', label: 'isActive', value: true, - icon: 'IconCheckbox', }, isVerified: { isLeaf: true, type: 'boolean', label: 'isVerified', value: false, - icon: 'IconCheckbox', }, }; expect(getHttpRequestOutputSchema(input)).toEqual(expected); @@ -93,44 +87,40 @@ describe('getHttpRequestOutputSchema', () => { user: { isLeaf: false, label: 'user', + type: 'object', value: { name: { isLeaf: true, type: 'string', label: 'name', value: 'John', - icon: 'IconAbc', }, age: { isLeaf: true, type: 'number', label: 'age', value: 25, - icon: 'IconText', }, address: { isLeaf: false, label: 'address', + type: 'object', value: { city: { isLeaf: true, type: 'string', label: 'city', value: 'New York', - icon: 'IconAbc', }, country: { isLeaf: true, type: 'string', label: 'country', value: 'USA', - icon: 'IconAbc', }, }, - icon: 'IconBox', }, }, - icon: 'IconBox', }, }; expect(getHttpRequestOutputSchema(input)).toEqual(expected); @@ -144,74 +134,22 @@ describe('getHttpRequestOutputSchema', () => { }; const expected = { tags: { - isLeaf: false, + isLeaf: true, label: 'tags', - value: { - '0': { - isLeaf: true, - type: 'string', - label: '0', - value: 'tag1', - icon: 'IconAbc', - }, - '1': { - isLeaf: true, - type: 'string', - label: '1', - value: 'tag2', - icon: 'IconAbc', - }, - }, - icon: 'IconBox', + type: 'array', + value: ['tag1', 'tag2'], }, scores: { - isLeaf: false, + isLeaf: true, label: 'scores', - value: { - '0': { - isLeaf: true, - type: 'number', - label: '0', - value: 1, - icon: 'IconText', - }, - '1': { - isLeaf: true, - type: 'number', - label: '1', - value: 2, - icon: 'IconText', - }, - '2': { - isLeaf: true, - type: 'number', - label: '2', - value: 3, - icon: 'IconText', - }, - }, - icon: 'IconBox', + type: 'array', + value: [1, 2, 3], }, flags: { - isLeaf: false, + isLeaf: true, label: 'flags', - value: { - '0': { - isLeaf: true, - type: 'boolean', - label: '0', - value: true, - icon: 'IconCheckbox', - }, - '1': { - isLeaf: true, - type: 'boolean', - label: '1', - value: false, - icon: 'IconCheckbox', - }, - }, - icon: 'IconBox', + type: 'array', + value: [true, false], }, }; expect(getHttpRequestOutputSchema(input)).toEqual(expected); @@ -225,14 +163,12 @@ describe('getHttpRequestOutputSchema', () => { type: 'unknown', label: 'name', value: null, - icon: 'IconQuestionMark', }, age: { isLeaf: true, type: 'unknown', label: 'age', value: null, - icon: 'IconQuestionMark', }, }; expect(getHttpRequestOutputSchema(input)).toEqual(expected); @@ -256,70 +192,49 @@ describe('getHttpRequestOutputSchema', () => { type: 'string', label: 'name', value: 'John', - icon: 'IconAbc', }, age: { isLeaf: true, type: 'number', label: 'age', value: 25, - icon: 'IconText', }, isActive: { isLeaf: true, type: 'boolean', label: 'isActive', value: true, - icon: 'IconCheckbox', }, tags: { - isLeaf: false, + isLeaf: true, label: 'tags', - value: { - '0': { - isLeaf: true, - type: 'string', - label: '0', - value: 'tag1', - icon: 'IconAbc', - }, - '1': { - isLeaf: true, - type: 'string', - label: '1', - value: 'tag2', - icon: 'IconAbc', - }, - }, - icon: 'IconBox', + type: 'array', + value: ['tag1', 'tag2'], }, address: { isLeaf: false, label: 'address', + type: 'object', value: { city: { isLeaf: true, type: 'string', label: 'city', value: 'New York', - icon: 'IconAbc', }, zip: { isLeaf: true, type: 'number', label: 'zip', value: 10001, - icon: 'IconText', }, }, - icon: 'IconBox', }, metadata: { isLeaf: true, type: 'unknown', label: 'metadata', value: null, - icon: 'IconQuestionMark', }, }; expect(getHttpRequestOutputSchema(input)).toEqual(expected); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/convertOutputSchemaToJson.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/convertOutputSchemaToJson.ts index 0cc891954b..6d8ea293a4 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/convertOutputSchemaToJson.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/convertOutputSchemaToJson.ts @@ -1,7 +1,7 @@ -import { type BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; export const convertOutputSchemaToJson = ( - schema: BaseOutputSchema, + schema: BaseOutputSchemaV2, ): Record | unknown[] => { const keys = Object.keys(schema); @@ -17,7 +17,7 @@ export const convertOutputSchemaToJson = ( if (entry.isLeaf) { return entry.value; } - return convertOutputSchemaToJson(entry.value as BaseOutputSchema); + return convertOutputSchemaToJson(entry.value as BaseOutputSchemaV2); }); } @@ -27,7 +27,9 @@ export const convertOutputSchemaToJson = ( if (entry.isLeaf) { result[key] = entry.value; } else { - result[key] = convertOutputSchemaToJson(entry.value as BaseOutputSchema); + result[key] = convertOutputSchemaToJson( + entry.value as BaseOutputSchemaV2, + ); } }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/getHttpRequestOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/getHttpRequestOutputSchema.ts index 51fcb042f8..d24e4a6fda 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/getHttpRequestOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/getHttpRequestOutputSchema.ts @@ -1,5 +1,5 @@ import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema'; -import { type BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; const getValueType = (value: unknown): InputSchemaPropertyType => { if (value === null || value === undefined) { @@ -25,12 +25,12 @@ const getValueType = (value: unknown): InputSchemaPropertyType => { export const getHttpRequestOutputSchema = ( responseData: unknown, -): BaseOutputSchema => { +): BaseOutputSchemaV2 => { if (typeof responseData !== 'object' || responseData === null) { return {}; } - const schema: BaseOutputSchema = {}; + const schema: BaseOutputSchemaV2 = {}; Object.entries(responseData).forEach(([key, value]) => { const type = getValueType(value); @@ -41,7 +41,6 @@ export const getHttpRequestOutputSchema = ( type: 'string', label: key, value, - icon: 'IconAbc', }; break; case 'number': @@ -50,7 +49,6 @@ export const getHttpRequestOutputSchema = ( type: 'number', label: key, value, - icon: 'IconText', }; break; case 'boolean': @@ -59,16 +57,22 @@ export const getHttpRequestOutputSchema = ( type: 'boolean', label: key, value, - icon: 'IconCheckbox', }; break; case 'array': + schema[key] = { + isLeaf: true, + label: key, + type: 'array', + value, + }; + break; case 'object': schema[key] = { isLeaf: false, label: key, value: getHttpRequestOutputSchema(value), - icon: 'IconBox', + type: 'object', }; break; case 'unknown': @@ -78,7 +82,6 @@ export const getHttpRequestOutputSchema = ( type: 'unknown', label: key, value: value === null ? null : String(value), - icon: 'IconQuestionMark', }; break; } diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdown.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdown.tsx index 3f066846b5..65dcb42042 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdown.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdown.tsx @@ -4,13 +4,12 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema'; -import { WorkflowVariablesDropdownAllItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownAllItems'; -import { WorkflowVariablesDropdownFieldItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems'; -import { WorkflowVariablesDropdownWorkflowStepItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems'; +import { WorkflowVariablesDropdownStepItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems'; +import { WorkflowVariablesDropdownSteps } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownSteps'; import { SEARCH_VARIABLES_DROPDOWN_ID } from '@/workflow/workflow-variables/constants/SearchVariablesDropdownId'; import { useAvailableVariablesInWorkflowStep } from '@/workflow/workflow-variables/hooks/useAvailableVariablesInWorkflowStep'; -import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useState } from 'react'; @@ -73,7 +72,7 @@ export const WorkflowVariablesDropdown = ({ : undefined; const [selectedStep, setSelectedStep] = useState< - StepOutputSchema | undefined + StepOutputSchemaV2 | undefined >(initialStep); const handleStepSelect = (stepId: string) => { @@ -122,22 +121,17 @@ export const WorkflowVariablesDropdown = ({ } dropdownComponents={ !isDefined(selectedStep) ? ( - - ) : shouldDisplayRecordObjects ? ( - ) : ( - ) } 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 deleted file mode 100644 index 1520df5fd0..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx +++ /dev/null @@ -1,82 +0,0 @@ -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 { 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 { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel'; -import { - IconChevronLeft, - OverflowingTextWithTooltip, - useIcons, -} from 'twenty-ui/display'; -import { MenuItemSelect } from 'twenty-ui/navigation'; -import { useVariableDropdown } from '../hooks/useVariableDropdown'; - -type WorkflowVariablesDropdownFieldItemsProps = { - step: StepOutputSchema; - onSelect: (value: string) => void; - onBack: () => void; -}; - -export const WorkflowVariablesDropdownFieldItems = ({ - step, - onSelect, - onBack, -}: WorkflowVariablesDropdownFieldItemsProps) => { - const { getIcon } = useIcons(); - const { - searchInputValue, - setSearchInputValue, - handleSelectField, - goBack, - filteredOptions, - currentPath, - } = useVariableDropdown({ - step, - onSelect, - onBack, - }); - - return ( - - - } - > - - - setSearchInputValue(event.target.value)} - /> - - - {filteredOptions.map(([key, option]) => ( - handleSelectField(key)} - text={option.label || key} - hasSubMenu={!option.isLeaf} - LeftIcon={option.icon ? getIcon(option.icon) : undefined} - contextualText={ - option.isLeaf ? option?.value?.toString() : undefined - } - /> - ))} - - - ); -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownAllItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx similarity index 64% rename from packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownAllItems.tsx rename to packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx index e0f640fc8b..0b14c17172 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownAllItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx @@ -2,16 +2,19 @@ import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenu 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 { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; 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 { isRecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath'; import { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel'; +import { getStepItemIcon } from '@/workflow/workflow-variables/utils/getStepItemIcon'; import { getVariableTemplateFromPath } from '@/workflow/workflow-variables/utils/getVariableTemplateFromPath'; -import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema'; import { useLingui } from '@lingui/react/macro'; +import { isDefined } from 'twenty-shared/utils'; import { IconChevronLeft, OverflowingTextWithTooltip, @@ -20,17 +23,19 @@ import { import { MenuItemSelect } from 'twenty-ui/navigation'; import { useVariableDropdown } from '../hooks/useVariableDropdown'; -type WorkflowVariablesDropdownAllItemsProps = { - step: StepOutputSchema; +type WorkflowVariablesDropdownStepItemsProps = { + step: StepOutputSchemaV2; onSelect: (value: string) => void; onBack: () => void; + shouldDisplayRecordObjects: boolean; }; -export const WorkflowVariablesDropdownAllItems = ({ +export const WorkflowVariablesDropdownStepItems = ({ step, onSelect, onBack, -}: WorkflowVariablesDropdownAllItemsProps) => { + shouldDisplayRecordObjects, +}: WorkflowVariablesDropdownStepItemsProps) => { const { t } = useLingui(); const { getIcon } = useIcons(); const { @@ -46,10 +51,12 @@ export const WorkflowVariablesDropdownAllItems = ({ onBack, }); + const { objectMetadataItems } = useObjectMetadataItems(); + const getDisplayedSubStepObject = () => { const currentSubStep = getCurrentSubStepFromPath(step, currentPath); - if (!isRecordOutputSchema(currentSubStep)) { + if (!isRecordOutputSchemaV2(currentSubStep)) { return; } @@ -59,30 +66,37 @@ export const WorkflowVariablesDropdownAllItems = ({ const handleSelectObject = () => { const currentSubStep = getCurrentSubStepFromPath(step, currentPath); - if (!isRecordOutputSchema(currentSubStep)) { + if (!isRecordOutputSchemaV2(currentSubStep)) { return; } onSelect( getVariableTemplateFromPath({ stepId: step.id, - path: [...currentPath, currentSubStep.object.fieldIdName], + path: [...currentPath, 'id'], }), ); }; const displayedSubStepObject = getDisplayedSubStepObject(); - const shouldDisplaySubStepObject = searchInputValue - ? displayedSubStepObject?.label && - displayedSubStepObject.label + const displayedSubStepObjectMetadata = isDefined(displayedSubStepObject) + ? objectMetadataItems.find( + (item) => item.id === displayedSubStepObject?.objectMetadataId, + ) + : undefined; + + const isObjectFoundThroughSearch = isDefined(searchInputValue) + ? isDefined(displayedSubStepObject?.label) && + displayedSubStepObject?.label .toLowerCase() .includes(searchInputValue.toLowerCase()) : true; - const shouldDisplayObject = - shouldDisplaySubStepObject && displayedSubStepObject?.label; - const nameSingular = displayedSubStepObject?.nameSingular; + const shouldDisplaySubStepObject = + shouldDisplayRecordObjects && isObjectFoundThroughSearch; + + const objectLabel = displayedSubStepObjectMetadata?.labelSingular; return ( @@ -105,22 +119,22 @@ export const WorkflowVariablesDropdownAllItems = ({ /> - {shouldDisplayObject && ( + {shouldDisplaySubStepObject && ( )} - {filteredOptions.length > 0 && shouldDisplayObject && ( + {filteredOptions.length > 0 && shouldDisplaySubStepObject && ( )} {filteredOptions.map(([key, subStep]) => ( @@ -131,7 +145,15 @@ export const WorkflowVariablesDropdownAllItems = ({ onClick={() => handleSelectField(key)} text={subStep.label || key} hasSubMenu={!subStep.isLeaf} - LeftIcon={subStep.icon ? getIcon(subStep.icon) : undefined} + LeftIcon={ + subStep.icon + ? getIcon(subStep.icon) + : getIcon( + getStepItemIcon({ + itemType: subStep.type, + }), + ) + } contextualText={ subStep.isLeaf ? subStep?.value?.toString() : undefined } diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownSteps.tsx similarity index 90% rename from packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems.tsx rename to packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownSteps.tsx index b3e02ce601..82f16fde5a 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownSteps.tsx @@ -6,22 +6,22 @@ import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/Dropdow import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; -import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; import { useState } from 'react'; import { IconX, OverflowingTextWithTooltip, useIcons } from 'twenty-ui/display'; import { MenuItem, MenuItemSelect } from 'twenty-ui/navigation'; -type WorkflowVariablesDropdownWorkflowStepItemsProps = { +type WorkflowVariablesDropdownStepsProps = { dropdownId: string; - steps: StepOutputSchema[]; + steps: StepOutputSchemaV2[]; onSelect: (value: string) => void; }; -export const WorkflowVariablesDropdownWorkflowStepItems = ({ +export const WorkflowVariablesDropdownSteps = ({ dropdownId, steps, onSelect, -}: WorkflowVariablesDropdownWorkflowStepItemsProps) => { +}: WorkflowVariablesDropdownStepsProps) => { const { getIcon } = useIcons(); const [searchInputValue, setSearchInputValue] = useState(''); diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useAvailableVariablesInWorkflowStep.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useAvailableVariablesInWorkflowStep.ts index 9304736e7c..4ff1a5c21b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useAvailableVariablesInWorkflowStep.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useAvailableVariablesInWorkflowStep.ts @@ -4,10 +4,7 @@ import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/ste import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema'; import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { getPreviousSteps } from '@/workflow/workflow-steps/utils/getWorkflowPreviousSteps'; -import { - type OutputSchema, - type StepOutputSchema, -} from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; import { filterOutputSchema } from '@/workflow/workflow-variables/utils/filterOutputSchema'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -22,7 +19,7 @@ export const useAvailableVariablesInWorkflowStep = ({ shouldDisplayRecordFields: boolean; shouldDisplayRecordObjects: boolean; fieldTypesToExclude?: InputSchemaPropertyType[]; -}): StepOutputSchema[] => { +}): StepOutputSchemaV2[] => { const workflowSelectedNode = useRecoilComponentValue( workflowSelectedNodeComponentState, ); @@ -33,7 +30,7 @@ export const useAvailableVariablesInWorkflowStep = ({ ? getPreviousSteps(steps, workflowSelectedNode).map((step) => step.id) : []; - const availableStepsOutputSchema: StepOutputSchema[] = useRecoilValue( + const availableStepsOutputSchema: StepOutputSchemaV2[] = useRecoilValue( stepsOutputSchemaFamilySelector({ workflowVersionId: flow.workflowVersionId, stepIds: [TRIGGER_STEP_ID, ...previousStepIds], @@ -47,7 +44,7 @@ export const useAvailableVariablesInWorkflowStep = ({ shouldDisplayRecordObjects, outputSchema: stepOutputSchema.outputSchema, fieldTypesToExclude, - }) as OutputSchema; + }); if (!isDefined(outputSchema) || isEmptyObject(outputSchema)) { return undefined; @@ -57,6 +54,7 @@ export const useAvailableVariablesInWorkflowStep = ({ id: stepOutputSchema.id, name: stepOutputSchema.name, icon: stepOutputSchema.icon, + type: stepOutputSchema.type, outputSchema, }; }) diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useSearchVariable.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useSearchVariable.ts index e1d1cd815d..ad1883dff1 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useSearchVariable.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useSearchVariable.ts @@ -1,18 +1,7 @@ import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow'; import { useWorkflowVersionIdOrThrow } from '@/workflow/hooks/useWorkflowVersionIdOrThrow'; import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/stepsOutputSchemaFamilySelector'; -import type { BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; -import type { CodeOutputSchema } from '@/workflow/workflow-variables/types/CodeOutputSchema'; -import type { FindRecordsOutputSchema } from '@/workflow/workflow-variables/types/FindRecordsOutputSchema'; -import type { FormOutputSchema } from '@/workflow/workflow-variables/types/FormOutputSchema'; -import { type RecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/RecordOutputSchemaV2'; -import { getOutputSchemaType } from '@/workflow/workflow-variables/utils/getOutputSchemaType'; -import { searchVariableThroughBaseOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughBaseOutputSchema'; -import { searchVariableThroughCodeOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughCodeOutputSchema'; -import { searchVariableThroughFindRecordsOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughFindRecordsOutputSchema'; -import { searchVariableThroughFormOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughFormOutputSchema'; -import { searchVariableThroughRecordEventOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughRecordEventOutputSchema'; -import { searchVariableThroughRecordOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughRecordOutputSchema'; +import { searchVariableThroughOutputSchemaV2 } from '@/workflow/workflow-variables/utils/searchVariableThroughOutputSchemaV2'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { TRIGGER_STEP_ID } from 'twenty-shared/workflow'; @@ -62,59 +51,9 @@ export const useSearchVariable = ({ }; } - const outputSchemaType = getOutputSchemaType(stepType); - - if (outputSchemaType === 'RECORD') { - return searchVariableThroughRecordOutputSchema({ - stepName: stepOutputSchema.name, - recordOutputSchema: stepOutputSchema.outputSchema as RecordOutputSchemaV2, - rawVariableName, - isFullRecord, - }); - } - - if (outputSchemaType === 'DATABASE_EVENT') { - return searchVariableThroughRecordEventOutputSchema({ - stepName: stepOutputSchema.name, - recordOutputSchema: stepOutputSchema.outputSchema as RecordOutputSchemaV2, - rawVariableName, - isFullRecord, - }); - } - - if (outputSchemaType === 'FIND_RECORDS') { - return searchVariableThroughFindRecordsOutputSchema({ - stepName: stepOutputSchema.name, - searchRecordOutputSchema: - stepOutputSchema.outputSchema as unknown as FindRecordsOutputSchema, - rawVariableName, - isFullRecord, - }); - } - - if (outputSchemaType === 'FORM') { - return searchVariableThroughFormOutputSchema({ - stepName: stepOutputSchema.name, - formOutputSchema: - stepOutputSchema.outputSchema as unknown as FormOutputSchema, - rawVariableName, - isFullRecord, - }); - } - - if (outputSchemaType === 'CODE') { - return searchVariableThroughCodeOutputSchema({ - stepName: stepOutputSchema.name, - codeOutputSchema: - stepOutputSchema.outputSchema as unknown as CodeOutputSchema, - rawVariableName, - isFullRecord, - }); - } - - return searchVariableThroughBaseOutputSchema({ - stepName: stepOutputSchema.name, - baseOutputSchema: stepOutputSchema.outputSchema as BaseOutputSchemaV2, + return searchVariableThroughOutputSchemaV2({ + stepOutputSchema, + stepType, rawVariableName, isFullRecord, }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts index 4e345ec42c..82ad1b0e03 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts @@ -6,23 +6,22 @@ import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; +import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; +import { type LinkOutputSchema } from '@/workflow/workflow-variables/types/LinkOutputSchema'; +import { type FieldOutputSchemaV2 } from '@/workflow/workflow-variables/types/RecordOutputSchemaV2'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; import { getVariableTemplateFromPath } from '@/workflow/workflow-variables/utils/getVariableTemplateFromPath'; import { useState } from 'react'; import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; -import { - type BaseOutputSchema, - type LinkOutputSchema, - type StepOutputSchema, -} from '../types/StepOutputSchema'; +import { isBaseOutputSchemaV2 } from '../types/guards/isBaseOutputSchemaV2'; +import { isLinkOutputSchema } from '../types/guards/isLinkOutputSchema'; +import { isRecordOutputSchemaV2 } from '../types/guards/isRecordOutputSchemaV2'; import { getCurrentSubStepFromPath } from '../utils/getCurrentSubStepFromPath'; -import { isBaseOutputSchema } from '../utils/isBaseOutputSchema'; -import { isLinkOutputSchema } from '../utils/isLinkOutputSchema'; -import { isRecordOutputSchema } from '../utils/isRecordOutputSchema'; type UseVariableDropdownProps = { - step: StepOutputSchema; + step: StepOutputSchemaV2; onSelect: (value: string) => void; onBack: () => void; }; @@ -72,9 +71,9 @@ export const useVariableDropdown = ({ if (isLinkOutputSchema(currentSubStep)) { return { link: currentSubStep.link }; - } else if (isRecordOutputSchema(currentSubStep)) { + } else if (isRecordOutputSchemaV2(currentSubStep)) { return currentSubStep.fields; - } else if (isBaseOutputSchema(currentSubStep)) { + } else if (isBaseOutputSchemaV2(currentSubStep)) { return currentSubStep; } }; @@ -83,7 +82,9 @@ export const useVariableDropdown = ({ const currentSubStep = getCurrentSubStepFromPath(step, currentPath); const handleSelectBaseOutputSchema = ( - baseOutputSchema: BaseOutputSchema, + baseOutputSchema: + | BaseOutputSchemaV2 + | Record, ) => { if (!baseOutputSchema[key]?.isLeaf) { setCurrentPath([...currentPath, key]); @@ -136,9 +137,9 @@ export const useVariableDropdown = ({ if (isLinkOutputSchema(currentSubStep)) { handleSelectLinkOutputSchema(currentSubStep); - } else if (isRecordOutputSchema(currentSubStep)) { + } else if (isRecordOutputSchemaV2(currentSubStep)) { handleSelectBaseOutputSchema(currentSubStep.fields); - } else if (isBaseOutputSchema(currentSubStep)) { + } else if (isBaseOutputSchemaV2(currentSubStep)) { handleSelectBaseOutputSchema(currentSubStep); } }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/BaseOutputSchemaV2.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/BaseOutputSchemaV2.ts index 0d362efba3..b62fa8f0ae 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/types/BaseOutputSchemaV2.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/BaseOutputSchemaV2.ts @@ -1,36 +1,15 @@ -type BaseLeaf = { +import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema'; + +export type LeafType = 'string' | 'number' | 'boolean' | 'array' | 'unknown'; + +export type Leaf = { isLeaf: true; + type: LeafType; label: string; + value: any; }; -type LeafString = BaseLeaf & { - type: 'string'; - value: string; -}; - -type LeafNumber = BaseLeaf & { - type: 'number'; - value: number; -}; - -type LeafBoolean = BaseLeaf & { - type: 'boolean'; - value: boolean; -}; - -type LeafArray = BaseLeaf & { - type: 'array'; - value: unknown[]; -}; - -type LeafUnknown = BaseLeaf & { - type: 'unknown'; - value: unknown; -}; - -type Leaf = LeafString | LeafNumber | LeafBoolean | LeafArray | LeafUnknown; - -type Node = { +export type Node = { isLeaf: false; type: 'object' | 'unknown'; label: string; @@ -38,3 +17,12 @@ type Node = { }; export type BaseOutputSchemaV2 = Record; + +export type LeafDeprecated = { + isLeaf: true; + type: InputSchemaPropertyType | undefined; + label: string; + value: any; +}; + +export type BaseOutputSchemaDeprecated = Record; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/CodeOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/CodeOutputSchema.ts index 0727ea7668..349ac579db 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/types/CodeOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/CodeOutputSchema.ts @@ -1,14 +1,4 @@ import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; - -type Link = { - isLeaf: true; - tab?: string; - label?: string; -}; - -export type LinkOutputSchema = { - link: Link; - _outputSchemaType: 'LINK'; -}; +import { type LinkOutputSchema } from '@/workflow/workflow-variables/types/LinkOutputSchema'; export type CodeOutputSchema = LinkOutputSchema | BaseOutputSchemaV2; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/DatabaseEventTriggerOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/DatabaseEventTriggerOutputSchema.ts new file mode 100644 index 0000000000..1a140c5eb6 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/DatabaseEventTriggerOutputSchema.ts @@ -0,0 +1,3 @@ +import { type RecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/RecordOutputSchemaV2'; + +export type DatabaseEventTriggerOutputSchema = RecordOutputSchemaV2; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/LinkOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/LinkOutputSchema.ts new file mode 100644 index 0000000000..0e065fe4f4 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/LinkOutputSchema.ts @@ -0,0 +1,10 @@ +type Link = { + isLeaf: true; + tab?: string; + label?: string; +}; + +export type LinkOutputSchema = { + link: Link; + _outputSchemaType: 'LINK'; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/OpenStepOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/OpenStepOutputSchema.ts new file mode 100644 index 0000000000..3769545d04 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/OpenStepOutputSchema.ts @@ -0,0 +1,3 @@ +import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; + +export type OpenStepOutputSchema = BaseOutputSchemaV2; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/RecordActionOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/RecordActionOutputSchema.ts new file mode 100644 index 0000000000..dffb7d0832 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/RecordActionOutputSchema.ts @@ -0,0 +1,3 @@ +import { type RecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/RecordOutputSchemaV2'; + +export type RecordActionOutputSchema = RecordOutputSchemaV2; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts deleted file mode 100644 index 48cc49fe11..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema'; - -type Leaf = { - isLeaf: true; - type?: InputSchemaPropertyType; - icon?: string; - label?: string; - description?: string; - value: any; - fieldMetadataId?: string; - isCompositeSubField?: boolean; -}; - -type Node = { - isLeaf: false; - type?: InputSchemaPropertyType; - icon?: string; - label?: string; - value: OutputSchema; - description?: string; - fieldMetadataId?: string; - isCompositeSubField?: boolean; -}; - -type Link = { - isLeaf: true; - tab?: string; - icon?: string; - label?: string; -}; - -export type BaseOutputSchema = Record; - -export type FieldOutputSchema = (Leaf | Node) & { - fieldMetadataId: string; -}; - -export type RecordOutputSchema = { - object: { - nameSingular: string; - fieldIdName: string; - objectMetadataId: string; - isRelationField?: boolean; - } & Leaf; - fields: Record; - _outputSchemaType: 'RECORD'; -}; - -export type LinkOutputSchema = { - link: Link; - _outputSchemaType: 'LINK'; -}; - -export type OutputSchema = - | BaseOutputSchema - | RecordOutputSchema - | LinkOutputSchema; - -export type StepOutputSchema = { - id: string; - name: string; - icon?: string; - outputSchema: OutputSchema; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchemaV2.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchemaV2.ts new file mode 100644 index 0000000000..a19e8cb4e5 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchemaV2.ts @@ -0,0 +1,24 @@ +import { + type WorkflowActionType, + type WorkflowTriggerType, +} from '@/workflow/types/Workflow'; +import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; +import { type CodeOutputSchema } from '@/workflow/workflow-variables/types/CodeOutputSchema'; +import { type FindRecordsOutputSchema } from '@/workflow/workflow-variables/types/FindRecordsOutputSchema'; +import { type FormOutputSchema } from '@/workflow/workflow-variables/types/FormOutputSchema'; +import { type RecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/RecordOutputSchemaV2'; + +export type OutputSchemaV2 = + | BaseOutputSchemaV2 + | CodeOutputSchema + | FindRecordsOutputSchema + | FormOutputSchema + | RecordOutputSchemaV2; + +export type StepOutputSchemaV2 = { + id: string; + name: string; + type: WorkflowTriggerType | WorkflowActionType; + icon?: string; + outputSchema: OutputSchemaV2; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isBaseOutputSchemaV2.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isBaseOutputSchemaV2.ts new file mode 100644 index 0000000000..dd5b412875 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isBaseOutputSchemaV2.ts @@ -0,0 +1,9 @@ +import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; +import { type OutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; +import { isObject } from '@sniptt/guards'; + +export const isBaseOutputSchemaV2 = ( + outputSchema: OutputSchemaV2, +): outputSchema is BaseOutputSchemaV2 => { + return !(isObject(outputSchema) && '_outputSchemaType' in outputSchema); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isCodeOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isCodeOutputSchema.ts new file mode 100644 index 0000000000..4fb31df929 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isCodeOutputSchema.ts @@ -0,0 +1,13 @@ +import { + type WorkflowActionType, + type WorkflowTriggerType, +} from '@/workflow/types/Workflow'; +import { type CodeOutputSchema } from '@/workflow/workflow-variables/types/CodeOutputSchema'; +import { type OutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; + +export const isCodeOutputSchema = ( + stepType: WorkflowActionType | WorkflowTriggerType, + schema: OutputSchemaV2, +): schema is CodeOutputSchema => { + return stepType === 'CODE'; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isDatabaseEventTriggerOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isDatabaseEventTriggerOutputSchema.ts new file mode 100644 index 0000000000..95c547186f --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isDatabaseEventTriggerOutputSchema.ts @@ -0,0 +1,13 @@ +import { + type WorkflowActionType, + type WorkflowTriggerType, +} from '@/workflow/types/Workflow'; +import { type DatabaseEventTriggerOutputSchema } from '@/workflow/workflow-variables/types/DatabaseEventTriggerOutputSchema'; +import { type OutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; + +export const isDatabaseEventTriggerOutputSchema = ( + stepType: WorkflowActionType | WorkflowTriggerType, + schema: OutputSchemaV2, +): schema is DatabaseEventTriggerOutputSchema => { + return stepType === 'DATABASE_EVENT'; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isFindRecordsOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isFindRecordsOutputSchema.ts new file mode 100644 index 0000000000..892e86de58 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isFindRecordsOutputSchema.ts @@ -0,0 +1,13 @@ +import { + type WorkflowActionType, + type WorkflowTriggerType, +} from '@/workflow/types/Workflow'; +import { type FindRecordsOutputSchema } from '@/workflow/workflow-variables/types/FindRecordsOutputSchema'; +import { type OutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; + +export const isFindRecordsOutputSchema = ( + stepType: WorkflowActionType | WorkflowTriggerType, + schema: OutputSchemaV2, +): schema is FindRecordsOutputSchema => { + return stepType === 'FIND_RECORDS'; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isFormOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isFormOutputSchema.ts new file mode 100644 index 0000000000..0fc7117b84 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isFormOutputSchema.ts @@ -0,0 +1,13 @@ +import { + type WorkflowActionType, + type WorkflowTriggerType, +} from '@/workflow/types/Workflow'; +import { type FormOutputSchema } from '@/workflow/workflow-variables/types/FormOutputSchema'; +import { type OutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; + +export const isFormOutputSchema = ( + stepType: WorkflowActionType | WorkflowTriggerType, + schema: OutputSchemaV2, +): schema is FormOutputSchema => { + return stepType === 'FORM'; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isLinkOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isLinkOutputSchema.ts new file mode 100644 index 0000000000..573b2fa39d --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isLinkOutputSchema.ts @@ -0,0 +1,13 @@ +import { type LinkOutputSchema } from '@/workflow/workflow-variables/types/LinkOutputSchema'; +import { type OutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; +import { isObject } from '@sniptt/guards'; + +export const isLinkOutputSchema = ( + outputSchema: OutputSchemaV2, +): outputSchema is LinkOutputSchema => { + return ( + isObject(outputSchema) && + '_outputSchemaType' in outputSchema && + outputSchema._outputSchemaType === 'LINK' + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isOpenStepOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isOpenStepOutputSchema.ts new file mode 100644 index 0000000000..7afb0ccd06 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isOpenStepOutputSchema.ts @@ -0,0 +1,25 @@ +import { + type WorkflowActionType, + type WorkflowTriggerType, +} from '@/workflow/types/Workflow'; +import { type OpenStepOutputSchema } from '@/workflow/workflow-variables/types/OpenStepOutputSchema'; +import { type OutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; + +import { isCodeOutputSchema } from './isCodeOutputSchema'; +import { isDatabaseEventTriggerOutputSchema } from './isDatabaseEventTriggerOutputSchema'; +import { isFindRecordsOutputSchema } from './isFindRecordsOutputSchema'; +import { isFormOutputSchema } from './isFormOutputSchema'; +import { isRecordActionOutputSchema } from './isRecordActionOutputSchema'; + +export const isOpenStepOutputSchema = ( + stepType: WorkflowActionType | WorkflowTriggerType, + schema: OutputSchemaV2, +): schema is OpenStepOutputSchema => { + return ( + !isRecordActionOutputSchema(stepType, schema) && + !isDatabaseEventTriggerOutputSchema(stepType, schema) && + !isFindRecordsOutputSchema(stepType, schema) && + !isFormOutputSchema(stepType, schema) && + !isCodeOutputSchema(stepType, schema) + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isRecordActionOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isRecordActionOutputSchema.ts new file mode 100644 index 0000000000..66aed4af17 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isRecordActionOutputSchema.ts @@ -0,0 +1,15 @@ +import { + type WorkflowActionType, + type WorkflowTriggerType, +} from '@/workflow/types/Workflow'; +import { type RecordActionOutputSchema } from '@/workflow/workflow-variables/types/RecordActionOutputSchema'; +import { type OutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; + +const RECORD_ACTION_TYPES = ['CREATE_RECORD', 'UPDATE_RECORD', 'DELETE_RECORD']; + +export const isRecordActionOutputSchema = ( + stepType: WorkflowActionType | WorkflowTriggerType, + schema: OutputSchemaV2, +): schema is RecordActionOutputSchema => { + return RECORD_ACTION_TYPES.includes(stepType); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2.ts new file mode 100644 index 0000000000..22f90fe1cd --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2.ts @@ -0,0 +1,13 @@ +import { type RecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/RecordOutputSchemaV2'; +import { type OutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; +import { isObject } from '@sniptt/guards'; + +export const isRecordOutputSchemaV2 = ( + outputSchema: OutputSchemaV2, +): outputSchema is RecordOutputSchemaV2 => { + return ( + isObject(outputSchema) && + '_outputSchemaType' in outputSchema && + outputSchema._outputSchemaType === 'RECORD' + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/filterOutputSchema.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/filterOutputSchema.test.ts index 4b5665f1a2..25e7dc70d0 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/filterOutputSchema.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/filterOutputSchema.test.ts @@ -1,4 +1,5 @@ -import { type OutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; +import { type RecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/RecordOutputSchemaV2'; import { FieldMetadataType } from 'twenty-shared/types'; import { filterOutputSchema } from '../filterOutputSchema'; @@ -6,19 +7,17 @@ describe('filterOutputSchema', () => { const createRecordSchema = ( nameSingular: string, fields = {}, - ): OutputSchema => ({ + ): RecordOutputSchemaV2 => ({ _outputSchemaType: 'RECORD', object: { - nameSingular, - fieldIdName: 'id', - isLeaf: true, - value: 'Fake value', + label: nameSingular, objectMetadataId: '123', + isRelationField: false, }, fields, }); - const createBaseSchema = (fields = {}): OutputSchema => ({ + const createBaseSchema = (fields = {}): BaseOutputSchemaV2 => ({ ...fields, }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getCurrentSubStepFromPath.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getCurrentSubStepFromPath.test.ts index 44e6b1fcec..a6d3b685a0 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getCurrentSubStepFromPath.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getCurrentSubStepFromPath.test.ts @@ -1,22 +1,20 @@ -import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath'; +import { FieldMetadataType } from 'twenty-shared/types'; const mockStep = { id: 'step-1', name: 'Step 1', + type: 'CREATE_RECORD', outputSchema: { company: { isLeaf: false, - icon: 'company', label: 'Company', value: { object: { - nameSingular: 'company', - fieldIdName: 'id', label: 'Company', - value: 'John', - isLeaf: true, objectMetadataId: '123', + isRelationField: false, }, fields: { name: { @@ -24,13 +22,15 @@ const mockStep = { value: 'Twenty', isLeaf: true, fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + type: FieldMetadataType.TEXT, + isCompositeSubField: false, }, }, _outputSchemaType: 'RECORD', }, }, }, -} satisfies StepOutputSchema; +} satisfies StepOutputSchemaV2; describe('getCurrentSubStepFromPath', () => { it('should return the current sub step from the path', () => { 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 index c8d56dfe38..6c5ac2efa3 100644 --- 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 @@ -1,22 +1,20 @@ -import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; import { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel'; +import { FieldMetadataType } from 'twenty-shared/types'; const mockStep = { id: 'step-1', name: 'Step 1', + type: 'CREATE_RECORD', outputSchema: { company: { isLeaf: false, - icon: 'company', label: 'Company', value: { object: { - nameSingular: 'company', - fieldIdName: 'id', label: 'Company', - value: 'John', - isLeaf: true, objectMetadataId: '123', + isRelationField: false, }, fields: { name: { @@ -24,35 +22,46 @@ const mockStep = { value: 'Twenty', isLeaf: true, fieldMetadataId: '123e4567-e89b-12d3-a456-426614174001', + type: FieldMetadataType.TEXT, + isCompositeSubField: false, }, address: { isLeaf: false, label: 'Address', fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + type: FieldMetadataType.ADDRESS, value: { street: { label: 'Street', value: '123 Main St', isLeaf: true, fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + type: FieldMetadataType.TEXT, + isCompositeSubField: true, }, city: { label: 'City', value: 'New York', isLeaf: true, fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + type: FieldMetadataType.TEXT, + isCompositeSubField: true, }, state: { label: 'State', value: 'NY', isLeaf: true, fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + type: FieldMetadataType.TEXT, + isCompositeSubField: true, }, zip: { label: 'Zip', value: '10001', isLeaf: true, fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + type: FieldMetadataType.TEXT, + isCompositeSubField: true, }, }, }, @@ -61,7 +70,7 @@ const mockStep = { }, }, }, -} satisfies StepOutputSchema; +} satisfies StepOutputSchemaV2; describe('getStepHeaderLabel', () => { it('should return the step name when the path is empty', () => { diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/isBaseOutputSchema.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/isBaseOutputSchema.test.ts deleted file mode 100644 index e3d5672f6b..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/isBaseOutputSchema.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { isBaseOutputSchema } from '@/workflow/workflow-variables/utils/isBaseOutputSchema'; - -describe('isBaseOutputSchema', () => { - // This looks weird, but that is the way this method was built - it('should return false for base output schema', () => { - expect( - isBaseOutputSchema({ _outputSchemaType: 'LINK', link: { isLeaf: true } }), - ).toBe(false); - }); - - it('should return true in other cases', () => { - expect(isBaseOutputSchema({})).toBe(true); - }); -}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/searchVariableThroughOutputSchema.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/searchVariableThroughOutputSchema.test.ts deleted file mode 100644 index 590776f251..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/searchVariableThroughOutputSchema.test.ts +++ /dev/null @@ -1,373 +0,0 @@ -import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; -import { searchVariableThroughOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughOutputSchema'; -import { FieldMetadataType } from '~/generated-metadata/graphql'; - -describe('searchVariableThroughOutputSchema', () => { - describe('step tests', () => { - 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, - objectMetadataId: '123', - }, - fields: { - name: { - label: 'Name', - value: 'Twenty', - isLeaf: true, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - }, - address: { - label: 'Address', - value: '123 Main St', - isLeaf: true, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - }, - }, - _outputSchemaType: 'RECORD', - }, - }, - person: { - isLeaf: false, - icon: 'person', - label: 'Person', - value: { - object: { - nameSingular: 'person', - fieldIdName: 'id', - label: 'Person', - value: 'Jane', - isLeaf: true, - objectMetadataId: '123', - }, - fields: { - firstName: { - label: 'First Name', - value: 'Jane', - isLeaf: true, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - }, - lastName: { - label: 'Last Name', - value: 'Doe', - isLeaf: true, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - }, - email: { - label: 'Email', - value: 'jane@example.com', - isLeaf: true, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - }, - }, - _outputSchemaType: 'RECORD', - }, - }, - simpleData: { - isLeaf: true, - label: 'Simple Data', - value: 'Simple value', - }, - nestedData: { - isLeaf: false, - label: 'Nested Data', - value: { - field1: { label: 'Field 1', value: 'Value 1', isLeaf: true }, - field2: { label: 'Field 2', value: 'Value 2', isLeaf: true }, - }, - }, - }, - } satisfies StepOutputSchema; - it('should not break with wrong path', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockStep, - rawVariableName: '{{step-1.wrong.wrong.wrong}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - variableLabel: undefined, - variablePathLabel: 'Step 1 > undefined', - variableType: 'unknown', - }); - }); - - it('should find a company field variable', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockStep, - rawVariableName: '{{step-1.company.name}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - compositeFieldSubFieldName: undefined, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - variableLabel: 'Name', - variablePathLabel: 'Step 1 > Company > Name', - variableType: 'unknown', - }); - }); - - it('should find a person field variable', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockStep, - rawVariableName: '{{step-1.person.email}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - compositeFieldSubFieldName: undefined, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - variableLabel: 'Email', - variablePathLabel: 'Step 1 > Person > Email', - variableType: 'unknown', - }); - }); - - it('should find a company object variable', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockStep, - rawVariableName: '{{step-1.company.id}}', - isFullRecord: true, - }); - - expect(result).toEqual({ - variableLabel: 'Company', - variablePathLabel: 'Step 1 > Company > Company', - variableType: 'unknown', - }); - }); - - it('should find a person object variable', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockStep, - rawVariableName: '{{step-1.person.id}}', - isFullRecord: true, - }); - - expect(result).toEqual({ - variableLabel: 'Person', - variablePathLabel: 'Step 1 > Person > Person', - variableType: 'unknown', - }); - }); - - it('should handle simple data fields', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockStep, - rawVariableName: '{{step-1.simpleData}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - variableLabel: 'Simple Data', - variablePathLabel: 'Step 1 > Simple Data', - variableType: 'unknown', - }); - }); - - it('should handle nested data fields', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockStep, - rawVariableName: '{{step-1.nestedData.field1}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - variableLabel: 'Field 1', - variablePathLabel: 'Step 1 > Nested Data > Field 1', - variableType: 'unknown', - }); - }); - - it('should handle invalid variable names', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockStep, - rawVariableName: '{{invalid}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - variableLabel: undefined, - variablePathLabel: 'Step 1 > undefined', - variableType: 'unknown', - }); - }); - - it('should handle non-existent paths', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockStep, - rawVariableName: '{{step-1.nonExistent.field}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - variableLabel: undefined, - variablePathLabel: 'Step 1 > undefined', - variableType: 'unknown', - }); - }); - - it('should handle the case where the path has dots in field names', () => { - const mockStepWithDotInField = { - id: 'step-1', - name: 'Step 1', - outputSchema: { - 'complex.field': { - isLeaf: false, - label: 'Complex Field', - value: { - field1: { label: 'Field 1', value: 'Value 1', isLeaf: true }, - field2: { label: 'Field 2', value: 'Value 2', isLeaf: true }, - }, - }, - }, - } satisfies StepOutputSchema; - - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockStepWithDotInField, - rawVariableName: '{{step-1.complex.field.field1}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - variableLabel: 'Field 1', - variablePathLabel: 'Step 1 > Complex Field > Field 1', - variableType: 'unknown', - }); - }); - }); - - describe('trigger tests', () => { - const mockTrigger = { - id: 'trigger', - name: 'Record is Created', - icon: 'IconPlaylistAdd', - outputSchema: { - fields: { - 'properties.after.id': { - icon: 'Icon123', - type: FieldMetadataType.UUID, - label: 'Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - }, - 'properties.after.name': { - icon: 'IconBuildingSkyscraper', - type: FieldMetadataType.TEXT, - label: 'Name', - value: 'My text', - isLeaf: true, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - }, - 'properties.after.annualRecurringRevenue': { - icon: 'IconMoneybag', - label: 'ARR', - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - value: { - amountMicros: { - type: FieldMetadataType.NUMERIC, - label: ' Amount Micros', - value: null, - isLeaf: true, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - isCompositeSubField: true, - }, - currencyCode: { - type: FieldMetadataType.TEXT, - label: ' Currency Code', - value: 'My text', - isLeaf: true, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - isCompositeSubField: true, - }, - }, - isLeaf: false, - }, - }, - object: { - icon: 'IconBuildingSkyscraper', - label: 'Company', - value: 'A company', - isLeaf: true, - fieldIdName: 'properties.after.id', - nameSingular: 'company', - objectMetadataId: '123', - }, - _outputSchemaType: 'RECORD', - }, - } satisfies StepOutputSchema; - it('should find a simple field from trigger', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockTrigger, - rawVariableName: '{{trigger.properties.after.name}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - compositeFieldSubFieldName: undefined, - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - variableLabel: 'Name', - variablePathLabel: 'Record is Created > Name', - variableType: FieldMetadataType.TEXT, - }); - }); - - it('should find a nested field from trigger', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockTrigger, - rawVariableName: - '{{trigger.properties.after.annualRecurringRevenue.amountMicros}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - compositeFieldSubFieldName: 'amountMicros', - fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', - variableLabel: ' Amount Micros', - variablePathLabel: 'Record is Created > ARR > Amount Micros', - variableType: FieldMetadataType.NUMERIC, - }); - }); - - it('should find the object field from trigger', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockTrigger, - rawVariableName: '{{trigger.object}}', - isFullRecord: true, - }); - - expect(result).toEqual({ - variableLabel: 'Company', - variablePathLabel: 'Record is Created > Company', - variableType: 'unknown', - }); - }); - - it('should handle invalid trigger field path', () => { - const result = searchVariableThroughOutputSchema({ - stepOutputSchema: mockTrigger, - rawVariableName: '{{trigger.nonExistent}}', - isFullRecord: false, - }); - - expect(result).toEqual({ - variableLabel: undefined, - variablePathLabel: 'Record is Created > undefined', - variableType: 'unknown', - }); - }); - }); -}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts index 8570bdaed9..4510724cca 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts @@ -1,14 +1,17 @@ import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema'; import { - type BaseOutputSchema, - type FieldOutputSchema, - type OutputSchema, - type RecordOutputSchema, -} from '@/workflow/workflow-variables/types/StepOutputSchema'; -import { isBaseOutputSchema } from '@/workflow/workflow-variables/utils/isBaseOutputSchema'; + type BaseOutputSchemaV2, + type Node, +} from '@/workflow/workflow-variables/types/BaseOutputSchemaV2'; +import { + type FieldOutputSchemaV2, + type RecordOutputSchemaV2, +} from '@/workflow/workflow-variables/types/RecordOutputSchemaV2'; +import { type OutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; +import { isBaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isBaseOutputSchemaV2'; +import { isLinkOutputSchema } from '@/workflow/workflow-variables/types/guards/isLinkOutputSchema'; +import { isRecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2'; import { isFieldTypeCompatibleWithRecordId } from '@/workflow/workflow-variables/utils/isFieldTypeCompatibleWithRecordId'; -import { isLinkOutputSchema } from '@/workflow/workflow-variables/utils/isLinkOutputSchema'; -import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema'; import { isDefined } from 'twenty-shared/utils'; const isValidRecordOutputSchema = ({ @@ -18,7 +21,7 @@ const isValidRecordOutputSchema = ({ }: { shouldDisplayRecordFields: boolean; shouldDisplayRecordObjects: boolean; - outputSchema: RecordOutputSchema; + outputSchema: RecordOutputSchemaV2; }): boolean => { if (shouldDisplayRecordObjects && !shouldDisplayRecordFields) { return isDefined(outputSchema.object); @@ -32,11 +35,11 @@ const filterRecordOutputSchema = ({ shouldDisplayRecordFields, shouldDisplayRecordObjects, }: { - outputSchema: RecordOutputSchema; + outputSchema: RecordOutputSchemaV2; shouldDisplayRecordFields: boolean; shouldDisplayRecordObjects: boolean; -}): RecordOutputSchema | undefined => { - const filteredFields: Record = {}; +}): RecordOutputSchemaV2 | undefined => { + const filteredFields: Record = {}; let hasValidFields = false; for (const key in outputSchema.fields) { @@ -60,7 +63,7 @@ const filterRecordOutputSchema = ({ filteredFields[key] = { ...field, value: validSubSchema, - }; + } as FieldOutputSchemaV2; hasValidFields = true; } } @@ -80,7 +83,7 @@ const filterRecordOutputSchema = ({ return { _outputSchemaType: 'RECORD', fields: filteredFields, - } as RecordOutputSchema; + } as RecordOutputSchemaV2; } return undefined; @@ -91,11 +94,11 @@ const filterBaseOutputSchema = ({ shouldDisplayRecordFields, shouldDisplayRecordObjects, }: { - outputSchema: BaseOutputSchema; + outputSchema: BaseOutputSchemaV2; shouldDisplayRecordFields: boolean; shouldDisplayRecordObjects: boolean; -}): BaseOutputSchema | undefined => { - const filteredSchema: BaseOutputSchema = {}; +}): BaseOutputSchemaV2 | undefined => { + const filteredSchema: BaseOutputSchemaV2 = {}; let hasValidFields = false; for (const key in outputSchema) { @@ -118,7 +121,7 @@ const filterBaseOutputSchema = ({ filteredSchema[key] = { ...field, value: validSubSchema, - }; + } as Node; hasValidFields = true; } } @@ -134,10 +137,10 @@ const filterRecordOutputSchemaFieldsByType = ({ outputSchema, fieldTypesToExclude, }: { - outputSchema: RecordOutputSchema; + outputSchema: RecordOutputSchemaV2; fieldTypesToExclude: InputSchemaPropertyType[]; -}): RecordOutputSchema => { - const filteredFields: Record = {}; +}): RecordOutputSchemaV2 => { + const filteredFields: Record = {}; for (const key in outputSchema.fields) { const field = outputSchema.fields[key]; @@ -164,15 +167,18 @@ export const filterOutputSchema = ({ }: { shouldDisplayRecordFields: boolean; shouldDisplayRecordObjects: boolean; - outputSchema?: OutputSchema; + outputSchema?: OutputSchemaV2; fieldTypesToExclude?: InputSchemaPropertyType[]; -}): OutputSchema | undefined => { +}): OutputSchemaV2 | undefined => { if (!isDefined(outputSchema)) { return undefined; } if (!shouldDisplayRecordObjects || shouldDisplayRecordFields) { - if (isRecordOutputSchema(outputSchema) && isDefined(fieldTypesToExclude)) { + if ( + isRecordOutputSchemaV2(outputSchema) && + isDefined(fieldTypesToExclude) + ) { return filterRecordOutputSchemaFieldsByType({ outputSchema, fieldTypesToExclude, @@ -184,13 +190,13 @@ export const filterOutputSchema = ({ if (isLinkOutputSchema(outputSchema)) { return outputSchema; - } else if (isRecordOutputSchema(outputSchema)) { + } else if (isRecordOutputSchemaV2(outputSchema)) { return filterRecordOutputSchema({ outputSchema, shouldDisplayRecordFields, shouldDisplayRecordObjects, }); - } else if (isBaseOutputSchema(outputSchema)) { + } else if (isBaseOutputSchemaV2(outputSchema)) { return filterBaseOutputSchema({ outputSchema, shouldDisplayRecordFields, 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 index 9331a26e9e..dbb3d20f30 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getCurrentSubStepFromPath.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getCurrentSubStepFromPath.ts @@ -1,20 +1,20 @@ import { - type OutputSchema, - type StepOutputSchema, -} from '@/workflow/workflow-variables/types/StepOutputSchema'; -import { isBaseOutputSchema } from '@/workflow/workflow-variables/utils/isBaseOutputSchema'; -import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema'; + type OutputSchemaV2, + type StepOutputSchemaV2, +} from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; +import { isBaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isBaseOutputSchemaV2'; +import { isRecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2'; export const getCurrentSubStepFromPath = ( - step: StepOutputSchema, + step: StepOutputSchemaV2, path: string[], -): OutputSchema => { +): OutputSchemaV2 => { let currentSubStep = step.outputSchema; for (const key of path) { - if (isRecordOutputSchema(currentSubStep)) { + if (isRecordOutputSchemaV2(currentSubStep)) { currentSubStep = currentSubStep.fields[key]?.value; - } else if (isBaseOutputSchema(currentSubStep)) { + } else if (isBaseOutputSchemaV2(currentSubStep)) { currentSubStep = currentSubStep[key]?.value; } } diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getOutputSchemaType.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getOutputSchemaType.ts deleted file mode 100644 index c25b226830..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getOutputSchemaType.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { - type WorkflowActionType, - type WorkflowTriggerType, -} from '@/workflow/types/Workflow'; - -export const getOutputSchemaType = ( - stepType: WorkflowActionType | WorkflowTriggerType, -): 'RECORD' | 'DATABASE_EVENT' | 'FIND_RECORDS' | 'FORM' | 'CODE' | 'BASE' => { - switch (stepType) { - case 'CREATE_RECORD': - case 'UPDATE_RECORD': - case 'DELETE_RECORD': - case 'MANUAL': - return 'RECORD'; - case 'DATABASE_EVENT': - return 'DATABASE_EVENT'; - case 'FIND_RECORDS': - return 'FIND_RECORDS'; - case 'FORM': - return 'FORM'; - case 'CODE': - return 'CODE'; - default: - return 'BASE'; - } -}; 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 index d03af20ea5..b74e47102f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getStepHeaderLabel.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getStepHeaderLabel.ts @@ -1,11 +1,11 @@ -import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { isBaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isBaseOutputSchemaV2'; +import { isRecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; 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 'twenty-shared/utils'; export const getStepHeaderLabel = ( - step: StepOutputSchema, + step: StepOutputSchemaV2, currentPath: string[], ) => { if (currentPath.length === 0) { @@ -23,14 +23,14 @@ export const getStepHeaderLabel = ( } if ( - isRecordOutputSchema(previousSubStep) && + isRecordOutputSchemaV2(previousSubStep) && isDefined(previousSubStep.fields[subStepName]?.label) ) { return previousSubStep.fields[subStepName].label; } if ( - isBaseOutputSchema(previousSubStep) && + isBaseOutputSchemaV2(previousSubStep) && isDefined(previousSubStep[subStepName]?.label) ) { return previousSubStep[subStepName].label; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getStepItemIcon.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getStepItemIcon.ts new file mode 100644 index 0000000000..d5be1e397f --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getStepItemIcon.ts @@ -0,0 +1,38 @@ +import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema'; +import { FieldMetadataType } from 'twenty-shared/types'; + +export const getStepItemIcon = ({ + itemType, +}: { + itemType: InputSchemaPropertyType; +}) => { + if (itemType === 'string' || itemType === FieldMetadataType.TEXT) { + return 'IconAbc'; + } + + if ( + itemType === 'number' || + itemType === FieldMetadataType.NUMERIC || + itemType === FieldMetadataType.NUMBER + ) { + return 'Icon123'; + } + + if (itemType === 'boolean' || itemType === FieldMetadataType.BOOLEAN) { + return 'IconCheckbox'; + } + + if (itemType === 'array' || itemType === FieldMetadataType.ARRAY) { + return 'IconBrackets'; + } + + if (itemType === 'object') { + return 'IconBraces'; + } + + if (itemType === 'unknown') { + return 'IconQuestionMark'; + } + + return undefined; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isBaseOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isBaseOutputSchema.ts deleted file mode 100644 index 135c82d860..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isBaseOutputSchema.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { - type BaseOutputSchema, - type OutputSchema, -} from '@/workflow/workflow-variables/types/StepOutputSchema'; - -export const isBaseOutputSchema = ( - outputSchema: OutputSchema, -): outputSchema is BaseOutputSchema => { - return !outputSchema._outputSchemaType; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isLinkOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isLinkOutputSchema.ts deleted file mode 100644 index 9ac62fb112..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isLinkOutputSchema.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { - type LinkOutputSchema, - type OutputSchema, -} from '@/workflow/workflow-variables/types/StepOutputSchema'; - -export const isLinkOutputSchema = ( - outputSchema: OutputSchema, -): outputSchema is LinkOutputSchema => { - return outputSchema._outputSchemaType === 'LINK'; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isRecordOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isRecordOutputSchema.ts deleted file mode 100644 index 46fdfbce58..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isRecordOutputSchema.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { - type OutputSchema, - type RecordOutputSchema, -} from '@/workflow/workflow-variables/types/StepOutputSchema'; - -export const isRecordOutputSchema = ( - outputSchema: OutputSchema, -): outputSchema is RecordOutputSchema => { - return outputSchema._outputSchemaType === 'RECORD'; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isRecordOutputSchemaV2.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isRecordOutputSchemaV2.ts deleted file mode 100644 index 54b3ab9533..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isRecordOutputSchemaV2.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { - type RecordFieldLeaf, - type RecordOutputSchemaV2, -} from '@/workflow/workflow-variables/types/RecordOutputSchemaV2'; - -export const isRecordOutputSchemaV2 = ( - outputSchema: RecordOutputSchemaV2 | Record, -): outputSchema is RecordOutputSchemaV2 => { - return outputSchema._outputSchemaType === 'RECORD'; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughOutputSchema.ts deleted file mode 100644 index 5df8d9c021..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughOutputSchema.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { CAPTURE_ALL_VARIABLE_TAG_INNER_REGEX } from '@/workflow/workflow-variables/constants/CaptureAllVariableTagInnerRegex'; -import { type VariableSearchResult } from '@/workflow/workflow-variables/hooks/useSearchVariable'; -import { - type OutputSchema, - type StepOutputSchema, -} from '@/workflow/workflow-variables/types/StepOutputSchema'; -import { isBaseOutputSchema } from '@/workflow/workflow-variables/utils/isBaseOutputSchema'; -import { isLinkOutputSchema } from '@/workflow/workflow-variables/utils/isLinkOutputSchema'; -import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema'; -import { isDefined } from 'twenty-shared/utils'; - -const getDisplayedSubStepObjectLabel = (outputSchema: OutputSchema) => { - if (!isRecordOutputSchema(outputSchema)) { - return; - } - - return outputSchema.object.label; -}; - -const getDisplayedSubStepFieldLabel = ( - key: string, - outputSchema: OutputSchema, -) => { - if (isBaseOutputSchema(outputSchema)) { - return outputSchema[key]?.label; - } - - if (isRecordOutputSchema(outputSchema)) { - return outputSchema.fields[key]?.label; - } - - return; -}; - -const getVariableType = (key: string, outputSchema: OutputSchema): string => { - if (isRecordOutputSchema(outputSchema)) { - return outputSchema.fields[key]?.type ?? 'unknown'; - } - - if (isLinkOutputSchema(outputSchema)) { - return 'unknown'; - } - - return outputSchema[key]?.type ?? 'unknown'; -}; - -const getFieldMetadataId = ( - key: string, - outputSchema: OutputSchema, -): string | undefined => { - if (isRecordOutputSchema(outputSchema)) { - return outputSchema.fields[key]?.fieldMetadataId; - } - - return undefined; -}; - -const isCompositeSubField = ( - key: string, - outputSchema: OutputSchema, -): boolean => { - if (isBaseOutputSchema(outputSchema) && outputSchema[key]?.isLeaf) { - return outputSchema[key]?.isCompositeSubField ?? false; - } - - return false; -}; - -const searchCurrentStepOutputSchema = ({ - stepOutputSchema, - path, - isFullRecord, - selectedField, -}: { - stepOutputSchema: StepOutputSchema; - path: string[]; - isFullRecord: boolean; - selectedField: string; -}): VariableSearchResult => { - let currentSubStep = stepOutputSchema.outputSchema; - let nextKeyIndex = 0; - let nextKey = path[nextKeyIndex]; - let variablePathLabel = stepOutputSchema.name; - let isSelectedFieldInNextKey = false; - let parentFieldMetadataId: string | undefined; - - const handleFieldNotFound = () => { - if (nextKeyIndex + 1 < path.length) { - // If the key is not found in the step, we handle the case where the path has been wrongly split - // For example, if there is a dot in the field name - nextKey = `${nextKey}.${path[nextKeyIndex + 1]}`; - } else { - // If we already reached the end of the path, we add the selected field to the next key - nextKey = `${nextKey}.${selectedField}`; - isSelectedFieldInNextKey = true; - } - }; - - while (nextKeyIndex < path.length) { - if (!isDefined(currentSubStep)) { - break; - } - - if (isRecordOutputSchema(currentSubStep)) { - const currentField = currentSubStep.fields[nextKey]; - if (isDefined(currentField)) { - currentSubStep = currentField.value; - nextKey = path[nextKeyIndex + 1]; - variablePathLabel = `${variablePathLabel} > ${currentField.label}`; - parentFieldMetadataId = currentField.fieldMetadataId; - } else { - handleFieldNotFound(); - } - } else if (isBaseOutputSchema(currentSubStep)) { - if (isDefined(currentSubStep[nextKey])) { - const currentField = currentSubStep[nextKey]; - currentSubStep = currentField.value; - nextKey = path[nextKeyIndex + 1]; - variablePathLabel = `${variablePathLabel} > ${currentField.label}`; - parentFieldMetadataId = currentField.fieldMetadataId; - } else { - handleFieldNotFound(); - } - } - nextKeyIndex++; - } - - if (!isDefined(currentSubStep)) { - return { - variableLabel: undefined, - variablePathLabel: undefined, - variableType: undefined, - }; - } - - const variableName = isSelectedFieldInNextKey ? nextKey : selectedField; - const variableLabel = - isFullRecord && isRecordOutputSchema(currentSubStep) - ? getDisplayedSubStepObjectLabel(currentSubStep) - : getDisplayedSubStepFieldLabel(variableName, currentSubStep); - - return { - variableLabel, - variablePathLabel: `${variablePathLabel} > ${variableLabel}`, - variableType: getVariableType(variableName, currentSubStep), - fieldMetadataId: - getFieldMetadataId(variableName, currentSubStep) ?? parentFieldMetadataId, - compositeFieldSubFieldName: isCompositeSubField( - variableName, - currentSubStep, - ) - ? variableName - : undefined, - }; -}; - -export const searchVariableThroughOutputSchema = ({ - stepOutputSchema, - rawVariableName, - isFullRecord = false, -}: { - stepOutputSchema: StepOutputSchema; - rawVariableName: string; - isFullRecord?: boolean; -}): VariableSearchResult => { - if (!isDefined(stepOutputSchema)) { - return { - variableLabel: undefined, - variablePathLabel: undefined, - }; - } - - const variableWithoutBrackets = rawVariableName.replace( - CAPTURE_ALL_VARIABLE_TAG_INNER_REGEX, - (_, variableName) => { - return variableName; - }, - ); - - const parts = variableWithoutBrackets.split('.'); - - const stepId = parts.at(0); - const selectedField = parts.at(-1); - // path is the remaining parts of the variable name - const path = parts.slice(1, -1); - - if (!isDefined(stepId) || !isDefined(selectedField)) { - return { - variableLabel: undefined, - variablePathLabel: undefined, - }; - } - - return searchCurrentStepOutputSchema({ - stepOutputSchema, - path, - isFullRecord, - selectedField, - }); -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughOutputSchemaV2.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughOutputSchemaV2.ts new file mode 100644 index 0000000000..2eca82a301 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughOutputSchemaV2.ts @@ -0,0 +1,83 @@ +import { + type WorkflowActionType, + type WorkflowTriggerType, +} from '@/workflow/types/Workflow'; +import { isCodeOutputSchema } from '@/workflow/workflow-variables/types/guards/isCodeOutputSchema'; +import { isDatabaseEventTriggerOutputSchema } from '@/workflow/workflow-variables/types/guards/isDatabaseEventTriggerOutputSchema'; +import { isFindRecordsOutputSchema } from '@/workflow/workflow-variables/types/guards/isFindRecordsOutputSchema'; +import { isFormOutputSchema } from '@/workflow/workflow-variables/types/guards/isFormOutputSchema'; +import { isRecordActionOutputSchema } from '@/workflow/workflow-variables/types/guards/isRecordActionOutputSchema'; +import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; + +import { searchVariableThroughBaseOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughBaseOutputSchema'; +import { searchVariableThroughCodeOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughCodeOutputSchema'; +import { searchVariableThroughFindRecordsOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughFindRecordsOutputSchema'; +import { searchVariableThroughFormOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughFormOutputSchema'; +import { searchVariableThroughRecordEventOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughRecordEventOutputSchema'; +import { searchVariableThroughRecordOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughRecordOutputSchema'; + +export const searchVariableThroughOutputSchemaV2 = ({ + stepOutputSchema, + stepType, + rawVariableName, + isFullRecord, +}: { + stepOutputSchema: StepOutputSchemaV2; + stepType: WorkflowTriggerType | WorkflowActionType; + rawVariableName: string; + isFullRecord: boolean; +}) => { + if (isRecordActionOutputSchema(stepType, stepOutputSchema.outputSchema)) { + return searchVariableThroughRecordOutputSchema({ + stepName: stepOutputSchema.name, + recordOutputSchema: stepOutputSchema.outputSchema, + rawVariableName, + isFullRecord, + }); + } + + if ( + isDatabaseEventTriggerOutputSchema(stepType, stepOutputSchema.outputSchema) + ) { + return searchVariableThroughRecordEventOutputSchema({ + stepName: stepOutputSchema.name, + recordOutputSchema: stepOutputSchema.outputSchema, + rawVariableName, + isFullRecord, + }); + } + + if (isFindRecordsOutputSchema(stepType, stepOutputSchema.outputSchema)) { + return searchVariableThroughFindRecordsOutputSchema({ + stepName: stepOutputSchema.name, + searchRecordOutputSchema: stepOutputSchema.outputSchema, + rawVariableName, + isFullRecord, + }); + } + + if (isFormOutputSchema(stepType, stepOutputSchema.outputSchema)) { + return searchVariableThroughFormOutputSchema({ + stepName: stepOutputSchema.name, + formOutputSchema: stepOutputSchema.outputSchema, + rawVariableName, + isFullRecord, + }); + } + + if (isCodeOutputSchema(stepType, stepOutputSchema.outputSchema)) { + return searchVariableThroughCodeOutputSchema({ + stepName: stepOutputSchema.name, + codeOutputSchema: stepOutputSchema.outputSchema, + rawVariableName, + isFullRecord, + }); + } + + return searchVariableThroughBaseOutputSchema({ + stepName: stepOutputSchema.name, + baseOutputSchema: stepOutputSchema.outputSchema, + rawVariableName, + isFullRecord, + }); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughRecordOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughRecordOutputSchema.ts index 0a0c98f809..d053f43715 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughRecordOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughRecordOutputSchema.ts @@ -5,7 +5,7 @@ import { type RecordFieldNodeValue, type RecordOutputSchemaV2, } from '@/workflow/workflow-variables/types/RecordOutputSchemaV2'; -import { isRecordOutputSchemaV2 } from '@/workflow/workflow-variables/utils/isRecordOutputSchemaV2'; +import { isRecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2'; import { isDefined } from 'twenty-shared/utils'; const getRecordObjectLabel = ( diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value.ts index dc311eee54..e297f093b6 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value.ts @@ -6,13 +6,15 @@ const isManyToOneRelationField = (field: FieldMetadataEntity) => (field as FieldMetadataEntity).settings ?.relationType === 'MANY_TO_ONE'; +const EXCLUDED_SYSTEM_FIELDS = ['searchVector', 'position']; + // TODO refactor export const shouldGenerateFieldFakeValue = ( field: FieldMetadataEntity, ) => { return ( field.isActive && - field.name !== 'searchVector' && + !(EXCLUDED_SYSTEM_FIELDS.includes(field.name) && field.isSystem) && (field.type !== FieldMetadataType.RELATION || isManyToOneRelationField(field as unknown as FieldMetadataEntity)) );