Update workflow run step (#11125)
Currently, when filling the form, values are not saved in the action settings. This is an issue because we do not see the response in the node settings, only in the output of the step. This PR: - adds a new endpoint to update a step in the run flow output - updates this flow when a step is updated https://github.com/user-attachments/assets/2e74a010-a0d2-4b87-bd1f-1c91f7ca6b60 --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
@ -0,0 +1,13 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_WORKFLOW_RUN_STEP = gql`
|
||||
mutation UpdateWorkflowRunStep($input: UpdateWorkflowRunStepInput!) {
|
||||
updateWorkflowRunStep(input: $input) {
|
||||
id
|
||||
name
|
||||
type
|
||||
settings
|
||||
valid
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,5 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Base schemas
|
||||
export const objectRecordSchema = z.record(z.any());
|
||||
@ -95,6 +95,7 @@ export const workflowFormActionSettingsSchema =
|
||||
]),
|
||||
placeholder: z.string().optional(),
|
||||
settings: z.record(z.any()).optional(),
|
||||
value: z.any().optional(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@ -171,8 +171,6 @@ export const WorkflowRunStepNodeDetail = ({
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={{
|
||||
readonly: stepExecutionStatus !== 'running',
|
||||
// TODO: Implement update worklfow run flow step
|
||||
onActionUpdate: () => {},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||
import { UPDATE_WORKFLOW_RUN_STEP } from '@/workflow/graphql/mutations/updateWorkflowRunStep';
|
||||
import { WorkflowRun } from '@/workflow/types/Workflow';
|
||||
import { useApolloClient, useMutation } from '@apollo/client';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
UpdateWorkflowRunStepInput,
|
||||
UpdateWorkflowRunStepMutation,
|
||||
UpdateWorkflowRunStepMutationVariables,
|
||||
WorkflowAction,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
export const useUpdateWorkflowRunStep = () => {
|
||||
const apolloClient = useApolloClient();
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowRun,
|
||||
});
|
||||
|
||||
const [mutate] = useMutation<
|
||||
UpdateWorkflowRunStepMutation,
|
||||
UpdateWorkflowRunStepMutationVariables
|
||||
>(UPDATE_WORKFLOW_RUN_STEP, {
|
||||
client: apolloClient,
|
||||
});
|
||||
|
||||
const getRecordFromCache = useGetRecordFromCache({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowRun,
|
||||
});
|
||||
|
||||
const updateWorkflowRunStep = async (input: UpdateWorkflowRunStepInput) => {
|
||||
const result = await mutate({
|
||||
variables: {
|
||||
input: { workflowRunId: input.workflowRunId, step: input.step },
|
||||
},
|
||||
});
|
||||
const updatedStep = result?.data?.updateWorkflowRunStep;
|
||||
if (!isDefined(updatedStep)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cachedRecord = getRecordFromCache<WorkflowRun>(input.workflowRunId);
|
||||
|
||||
if (
|
||||
!isDefined(cachedRecord) ||
|
||||
!isDefined(cachedRecord?.output?.flow?.steps)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newCachedRecord = {
|
||||
...cachedRecord,
|
||||
output: {
|
||||
...cachedRecord.output,
|
||||
flow: {
|
||||
...cachedRecord.output.flow,
|
||||
steps: cachedRecord.output.flow.steps.map((step: WorkflowAction) => {
|
||||
if (step.id === updatedStep.id) {
|
||||
return updatedStep;
|
||||
}
|
||||
return step;
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const recordGqlFields = {
|
||||
output: true,
|
||||
};
|
||||
updateRecordFromCache({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
cache: apolloClient.cache,
|
||||
record: newCachedRecord,
|
||||
recordGqlFields,
|
||||
});
|
||||
|
||||
return updatedStep;
|
||||
};
|
||||
|
||||
return { updateWorkflowRunStep };
|
||||
};
|
||||
@ -7,25 +7,21 @@ import { useWorkflowStepContextOrThrow } from '@/workflow/states/context/Workflo
|
||||
import { WorkflowFormAction } from '@/workflow/types/Workflow';
|
||||
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
|
||||
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
|
||||
import { useUpdateWorkflowRunStep } from '@/workflow/workflow-steps/hooks/useUpdateWorkflowRunStep';
|
||||
import { useSubmitFormStep } from '@/workflow/workflow-steps/workflow-actions/form-action/hooks/useSubmitFormStep';
|
||||
import { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
|
||||
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export type WorkflowEditActionFormFillerProps = {
|
||||
action: WorkflowFormAction;
|
||||
actionOptions:
|
||||
| {
|
||||
readonly: true;
|
||||
}
|
||||
| {
|
||||
readonly?: false;
|
||||
onActionUpdate: (action: WorkflowFormAction) => void;
|
||||
};
|
||||
actionOptions: {
|
||||
readonly: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
type FormData = WorkflowFormActionField[];
|
||||
@ -40,6 +36,7 @@ export const WorkflowEditActionFormFiller = ({
|
||||
const [formData, setFormData] = useState<FormData>(action.settings.input);
|
||||
const { workflowRunId } = useWorkflowStepContextOrThrow();
|
||||
const { closeCommandMenu } = useCommandMenu();
|
||||
const { updateWorkflowRunStep } = useUpdateWorkflowRunStep();
|
||||
|
||||
if (!isDefined(workflowRunId)) {
|
||||
throw new Error('Form filler action must be used in a workflow run');
|
||||
@ -73,11 +70,11 @@ export const WorkflowEditActionFormFiller = ({
|
||||
return;
|
||||
}
|
||||
|
||||
actionOptions.onActionUpdate({
|
||||
...action,
|
||||
settings: {
|
||||
...action.settings,
|
||||
input: updatedFormData,
|
||||
await updateWorkflowRunStep({
|
||||
workflowRunId,
|
||||
step: {
|
||||
...action,
|
||||
settings: { ...action.settings, input: updatedFormData },
|
||||
},
|
||||
});
|
||||
}, 1_000);
|
||||
@ -109,16 +106,6 @@ export const WorkflowEditActionFormFiller = ({
|
||||
return (
|
||||
<>
|
||||
<WorkflowStepHeader
|
||||
onTitleChange={(newName: string) => {
|
||||
if (actionOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
actionOptions.onActionUpdate({
|
||||
...action,
|
||||
name: newName,
|
||||
});
|
||||
}}
|
||||
Icon={getIcon(headerIcon)}
|
||||
iconColor={theme.font.color.tertiary}
|
||||
initialTitle={headerTitle}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { WorkflowFormAction } from '@/workflow/types/Workflow';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, within } from '@storybook/test';
|
||||
import { expect, within } from '@storybook/test';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
@ -9,7 +10,6 @@ import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorato
|
||||
import { WorkspaceDecorator } from '~/testing/decorators/WorkspaceDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { WorkflowEditActionFormFiller } from '../WorkflowEditActionFormFiller';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
const meta: Meta<typeof WorkflowEditActionFormFiller> = {
|
||||
title: 'Modules/Workflow/Actions/Form/WorkflowEditActionFormFiller',
|
||||
@ -67,7 +67,7 @@ export const Default: Story = {
|
||||
args: {
|
||||
action: mockAction,
|
||||
actionOptions: {
|
||||
onActionUpdate: fn(),
|
||||
readonly: false,
|
||||
},
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
|
||||
Reference in New Issue
Block a user