diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/hooks/__tests__/useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInformation.test.tsx b/packages/twenty-front/src/modules/command-menu-item/engine-command/hooks/__tests__/useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInformation.test.tsx index c6ea616601..b5df929571 100644 --- a/packages/twenty-front/src/modules/command-menu-item/engine-command/hooks/__tests__/useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInformation.test.tsx +++ b/packages/twenty-front/src/modules/command-menu-item/engine-command/hooks/__tests__/useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInformation.test.tsx @@ -37,6 +37,14 @@ const buildBaseContextApi = ( ...overrides, }); +jest.mock('@/object-metadata/hooks/useApolloCoreClient', () => ({ + useApolloCoreClient: () => ({ query: jest.fn() }), +})); + +jest.mock('@/workspace/hooks/useIsFeatureEnabled', () => ({ + useIsFeatureEnabled: () => false, +})); + describe('useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInformation', () => { beforeEach(() => { jest.clearAllMocks(); diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/hooks/useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInformation.ts b/packages/twenty-front/src/modules/command-menu-item/engine-command/hooks/useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInformation.ts index 44c26a6e54..65d415c243 100644 --- a/packages/twenty-front/src/modules/command-menu-item/engine-command/hooks/useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInformation.ts +++ b/packages/twenty-front/src/modules/command-menu-item/engine-command/hooks/useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInformation.ts @@ -4,11 +4,18 @@ import { type HeadlessCommandContextApi, type HeadlessEngineCommandContextApi, } from '@/command-menu-item/engine-command/types/HeadlessCommandContextApi'; +import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { useLazyFindOneRecord } from '@/object-record/hooks/useLazyFindOneRecord'; import { type WorkflowVersion } from '@/workflow/types/Workflow'; +import { GET_WORKFLOW_VERSION_CONTENT } from '@/workflow/workflow-version/graphql/queries/getWorkflowVersionContent'; +import { type WorkflowVersionContent } from '@/workflow/workflow-version/hooks/useWorkflowVersionContent'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { CoreObjectNameSingular } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -import { type CommandMenuItemAvailabilityType } from '~/generated-metadata/graphql'; +import { + type CommandMenuItemAvailabilityType, + FeatureFlagKey, +} from '~/generated-metadata/graphql'; type WorkflowVersionRecord = Pick< WorkflowVersion, @@ -24,12 +31,31 @@ type EnrichParams = { export const useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInformation = () => { + const apolloCoreClient = useApolloCoreClient(); + const isWorkflowVersionInCoreEnabled = useIsFeatureEnabled( + FeatureFlagKey.IS_WORKFLOW_VERSION_IN_CORE_ENABLED, + ); + const { findOneRecord: findOneWorkflowVersion } = useLazyFindOneRecord({ objectNameSingular: CoreObjectNameSingular.WorkflowVersion, recordGqlFields: { id: true, workflowId: true, trigger: true }, }); + const fetchTriggerFromCore = useCallback( + async (versionId: string) => { + const { data } = await apolloCoreClient.query<{ + workflowVersionContent: WorkflowVersionContent; + }>({ + query: GET_WORKFLOW_VERSION_CONTENT, + variables: { workflowVersionId: versionId }, + }); + + return data?.workflowVersionContent.trigger ?? null; + }, + [apolloCoreClient], + ); + const fetchWorkflowVersion = useCallback( async (versionId: string): Promise => { let record: WorkflowVersionRecord | undefined; @@ -60,16 +86,24 @@ export const useEnrichHeadlessCommandContextApiWithWorkflowVersionTriggerInforma return undefined; } + const trigger = isWorkflowVersionInCoreEnabled + ? await fetchTriggerFromCore(workflowVersionId) + : workflowVersion.trigger; + return { ...headlessEngineCommandContextApi, workflowId: workflowVersion.workflowId, workflowVersionId: workflowVersion.id, - trigger: workflowVersion.trigger, + trigger, availabilityType, availabilityObjectMetadataId, }; }, - [fetchWorkflowVersion], + [ + fetchWorkflowVersion, + fetchTriggerFromCore, + isWorkflowVersionInCoreEnabled, + ], ); return { diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/workflow/components/TestWorkflowSingleRecordCommand.tsx b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/workflow/components/TestWorkflowSingleRecordCommand.tsx index ee2bb611e6..40ce048f2f 100644 --- a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/workflow/components/TestWorkflowSingleRecordCommand.tsx +++ b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/workflow/components/TestWorkflowSingleRecordCommand.tsx @@ -1,6 +1,7 @@ import { HeadlessEngineCommandWrapperEffect } from '@/command-menu-item/engine-command/components/HeadlessEngineCommandWrapperEffect'; import { useHeadlessCommandContextApi } from '@/command-menu-item/engine-command/hooks/useHeadlessCommandContextApi'; import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion'; +import { useWorkflowVersionContent } from '@/workflow/workflow-version/hooks/useWorkflowVersionContent'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { getTestPayloadFromTrigger } from '@/workflow/workflow-trigger/utils/getTestPayloadFromTrigger'; import { isDefined } from 'twenty-shared/utils'; @@ -13,6 +14,9 @@ export const TestWorkflowSingleRecordCommand = () => { const workflowWithCurrentVersion = useWorkflowWithCurrentVersion( recordId ?? '', ); + const { content } = useWorkflowVersionContent( + workflowWithCurrentVersion?.currentVersion.id, + ); if (!isDefined(recordId)) { throw new Error('Record ID is required to test workflow'); @@ -25,21 +29,21 @@ export const TestWorkflowSingleRecordCommand = () => { const { currentVersion } = workflowWithCurrentVersion; - if (!isDefined(currentVersion.trigger)) { + if (!isDefined(content?.trigger)) { return; } runWorkflowVersion({ workflowVersionId: currentVersion.id, workflowId: workflowWithCurrentVersion.id, - payload: getTestPayloadFromTrigger(currentVersion.trigger), + payload: getTestPayloadFromTrigger(content.trigger), }); }; return ( ); }; diff --git a/packages/twenty-front/src/modules/side-panel/components/SidePanelWorkflowStepInfo.tsx b/packages/twenty-front/src/modules/side-panel/components/SidePanelWorkflowStepInfo.tsx index 685b1a0c19..2f5e9a4acb 100644 --- a/packages/twenty-front/src/modules/side-panel/components/SidePanelWorkflowStepInfo.tsx +++ b/packages/twenty-front/src/modules/side-panel/components/SidePanelWorkflowStepInfo.tsx @@ -8,6 +8,7 @@ import { TitleInput } from '@/ui/input/components/TitleInput'; import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { getAgentIdFromStep } from '@/workflow/utils/getAgentIdFromStep'; import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow'; @@ -15,6 +16,7 @@ import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWo import { useUpdateAgentLabel } from '@/workflow/workflow-steps/hooks/useUpdateAgentLabel'; import { useUpdateWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/useUpdateWorkflowVersionStep'; import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger'; +import { useWorkflowVersionContent } from '@/workflow/workflow-version/hooks/useWorkflowVersionContent'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow'; import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon'; @@ -63,15 +65,13 @@ export const SidePanelWorkflowStepInfo = ({ useUpdateWorkflowVersionStep(instanceId); const { updateTrigger } = useUpdateWorkflowVersionTrigger(instanceId); - const { - trigger, - steps, - id: workflowVersionId, - } = workflowWithCurrentVersion?.currentVersion ?? { - trigger: null, - steps: null, - id: undefined, - }; + const workflowVersionId = workflowWithCurrentVersion?.currentVersion?.id; + + const flow = useAtomComponentStateValue(flowComponentState, instanceId); + const { content } = useWorkflowVersionContent(workflowVersionId); + + const trigger = flow?.trigger ?? content?.trigger ?? null; + const steps = flow?.steps ?? content?.steps ?? null; const isTriggerStep = sidePanelWorkflowStepId === TRIGGER_STEP_ID; diff --git a/packages/twenty-front/src/modules/side-panel/pages/workflow/step/create/components/SidePanelWorkflowCreateStepContent.tsx b/packages/twenty-front/src/modules/side-panel/pages/workflow/step/create/components/SidePanelWorkflowCreateStepContent.tsx index c33d5b013a..f633d6d4be 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/workflow/step/create/components/SidePanelWorkflowCreateStepContent.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/workflow/step/create/components/SidePanelWorkflowCreateStepContent.tsx @@ -6,7 +6,7 @@ import { import { sidePanelNavigationStackState } from '@/side-panel/states/sidePanelNavigationStackState'; import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState'; import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; -import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { type WorkflowActionType, @@ -30,9 +30,7 @@ export const SidePanelWorkflowCreateStepContent = () => { const { createStep } = useCreateStep(); const { updateStep } = useUpdateStep(); - const workflowWithCurrentVersion = useWorkflowWithCurrentVersion( - workflowVisualizerWorkflowId, - ); + const flow = useAtomComponentStateValue(flowComponentState); const { openWorkflowEditStepInSidePanel } = useSidePanelWorkflowNavigation(); const { closeRightClickMenu } = useCloseRightClickMenu(); @@ -81,7 +79,7 @@ export const SidePanelWorkflowCreateStepContent = () => { return; } - const steps = workflowWithCurrentVersion?.currentVersion?.steps; + const steps = flow?.steps; const parentStep = isDefined(parentStepId) && isDefined(steps) && isDefined(position) ? steps.find((step) => step.id === parentStepId) diff --git a/packages/twenty-front/src/modules/workflow/hooks/useWorkflowVersion.ts b/packages/twenty-front/src/modules/workflow/hooks/useWorkflowVersion.ts index d4edf4d7f7..67fbaae097 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useWorkflowVersion.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useWorkflowVersion.ts @@ -14,8 +14,6 @@ export const useWorkflowVersion = (workflowVersionId?: string) => { createdAt: true, updatedAt: true, workflowId: true, - trigger: true, - steps: true, status: true, workflow: { id: true, diff --git a/packages/twenty-front/src/modules/workflow/hooks/useWorkflowWithCurrentVersion.ts b/packages/twenty-front/src/modules/workflow/hooks/useWorkflowWithCurrentVersion.ts index 7f62e1d714..7ad05219b5 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useWorkflowWithCurrentVersion.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useWorkflowWithCurrentVersion.ts @@ -55,6 +55,14 @@ export const useWorkflowWithCurrentVersion = ( { objectNameSingular: CoreObjectNameSingular.WorkflowVersion, objectRecordId: currentVersionId, + recordGqlFields: { + id: true, + name: true, + status: true, + workflowId: true, + createdAt: true, + updatedAt: true, + }, skip: !isDefined(currentVersionId), }, ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx index a1c2bbf867..5df2f56c7e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx @@ -1,5 +1,6 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase'; @@ -62,8 +63,10 @@ export const WorkflowDiagramCanvasEditable = () => { const { startNodeCreation } = useStartNodeCreation(); + const flow = useAtomComponentStateValue(flowComponentState); + const onConnect = async (edgeConnect: WorkflowConnection) => { - const steps = workflowWithCurrentVersion?.currentVersion?.steps; + const steps = flow?.steps; const sourceStep = isDefined(steps) ? steps.find((step) => step.id === edgeConnect.source) : undefined; @@ -135,10 +138,7 @@ export const WorkflowDiagramCanvasEditable = () => { }; const onNodeDragStop: OnNodeDrag = async (_, node) => { - const stepToUpdate = - workflowWithCurrentVersion?.currentVersion?.steps?.find( - (step) => step.id === node.id, - ); + const stepToUpdate = flow?.steps?.find((step) => step.id === node.id); if (isDefined(stepToUpdate)) { await updateStep({ @@ -149,7 +149,7 @@ export const WorkflowDiagramCanvasEditable = () => { return; } - const triggerToUpdate = workflowWithCurrentVersion?.currentVersion?.trigger; + const triggerToUpdate = flow?.trigger; if (isDefined(triggerToUpdate)) { await updateTrigger({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect.tsx index 2fa2bf0ade..b5d5fcf7cf 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect.tsx @@ -246,12 +246,16 @@ export const WorkflowRunVisualizerEffect = ({ ]); useEffect(() => { - if (!isDefined(workflowVersion)) { + if (!isDefined(workflowVersion) || !isDefined(workflowRun?.state)) { return; } - populateStepsOutputSchema(workflowVersion); - }, [populateStepsOutputSchema, workflowVersion]); + populateStepsOutputSchema({ + ...workflowVersion, + trigger: workflowRun.state.flow.trigger, + steps: workflowRun.state.flow.steps, + }); + }, [populateStepsOutputSchema, workflowRun?.state, workflowVersion]); return null; }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts index 72cd89779d..9fa85ae41e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts @@ -1,7 +1,7 @@ import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu'; import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; -import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { useDeleteWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/useDeleteWorkflowVersionStep'; import { useResetWorkflowAiAgentPermissionsStateOnSidePanelClose } from '@/workflow/workflow-steps/workflow-actions/ai-agent-action/hooks/useResetWorkflowAiAgentPermissionsStateOnSidePanelClose'; @@ -20,12 +20,12 @@ export const useDeleteStep = () => { const workflowVisualizerWorkflowId = useAtomComponentStateValue( workflowVisualizerWorkflowIdComponentState, ); - const workflow = useWorkflowWithCurrentVersion(workflowVisualizerWorkflowId); + const flow = useAtomComponentStateValue(flowComponentState); const deleteStep = async (stepId: string) => { const workflowVersionId = await getUpdatableWorkflowVersion(); - const steps = workflow?.currentVersion?.steps; + const steps = flow?.steps; const stepToDelete = isDefined(steps) ? steps.find((step) => step.id === stepId) : undefined; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/if-else-action/components/WorkflowEditActionIfElseBody.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/if-else-action/components/WorkflowEditActionIfElseBody.tsx index d5423b6101..d4f0ddd4f1 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/if-else-action/components/WorkflowEditActionIfElseBody.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/if-else-action/components/WorkflowEditActionIfElseBody.tsx @@ -2,8 +2,7 @@ import { InputLabel } from '@/ui/input/components/InputLabel'; import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState'; import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; -import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; import { type WorkflowIfElseAction, type WorkflowStep, @@ -66,10 +65,7 @@ export const WorkflowEditActionIfElseBody = ({ const { updateWorkflowVersionPosition } = useTidyUpWorkflowVersion(); const { deleteWorkflowVersionStep } = useDeleteWorkflowVersionStep(); const { deleteStepsOutputSchema } = useStepsOutputSchema(); - const workflowVisualizerWorkflowId = useAtomComponentStateValue( - workflowVisualizerWorkflowIdComponentState, - ); - const workflow = useWorkflowWithCurrentVersion(workflowVisualizerWorkflowId); + const flow = useAtomComponentStateValue(flowComponentState); const currentStepFilters = useAtomComponentStateValue( currentStepFiltersComponentState, @@ -146,7 +142,7 @@ export const WorkflowEditActionIfElseBody = ({ await cleanupEmptyChildStepsFromDeletedBranches({ branchesToDelete, - allSteps: workflow?.currentVersion?.steps ?? undefined, + allSteps: flow?.steps ?? undefined, }); setCurrentStepFilterGroups(updatedStepFilterGroups);