Separate workflow step details and run step details (#11069)

Workflows step details in workflows and versions should be different
from the node tab in run. For most cases, it was using the same
component. But for forms, it will be a different one.

This PR:
- renames form action into formBuilder. formFiller is coming
- put code into a separated folder
- creates a new component for node details
This commit is contained in:
Thomas Trompette
2025-03-20 17:22:55 +01:00
committed by GitHub
parent 24bae89ebc
commit 3876cb8250
23 changed files with 260 additions and 85 deletions
@@ -1,22 +0,0 @@
import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow';
import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext';
import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow';
import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail';
export const RightDrawerWorkflowViewStep = () => {
const flow = useFlowOrThrow();
const workflowSelectedNode = useWorkflowSelectedNodeOrThrow();
return (
<WorkflowStepContextProvider
value={{ workflowVersionId: flow.workflowVersionId }}
>
<WorkflowStepDetail
stepId={workflowSelectedNode}
trigger={flow.trigger}
steps={flow.steps}
readonly
/>
</WorkflowStepContextProvider>
);
};
@@ -0,0 +1,175 @@
import { WorkflowAction, WorkflowTrigger } from '@/workflow/types/Workflow';
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
import { WorkflowActionServerlessFunction } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowActionServerlessFunction';
import { WorkflowEditActionCreateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord';
import { WorkflowEditActionDeleteRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord';
import { WorkflowEditActionFindRecords } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords';
import { WorkflowEditActionSendEmail } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail';
import { WorkflowEditActionUpdateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord';
import { WorkflowEditTriggerCronForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm';
import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm';
import { WorkflowEditTriggerManualForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerManualForm';
import { WorkflowEditTriggerWebhookForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerWebhookForm';
import { isDefined } from 'twenty-shared';
type WorkflowRunStepNodeDetailProps = {
stepId: string;
trigger: WorkflowTrigger | null;
steps: Array<WorkflowAction> | null;
};
export const WorkflowRunStepNodeDetail = ({
stepId,
trigger,
steps,
}: WorkflowRunStepNodeDetailProps) => {
const stepDefinition = getStepDefinitionOrThrow({
stepId,
trigger,
steps,
});
if (!isDefined(stepDefinition) || !isDefined(stepDefinition.definition)) {
return null;
}
switch (stepDefinition.type) {
case 'trigger': {
switch (stepDefinition.definition.type) {
case 'DATABASE_EVENT': {
return (
<WorkflowEditTriggerDatabaseEventForm
key={stepId}
trigger={stepDefinition.definition}
triggerOptions={{
readonly: true,
}}
/>
);
}
case 'MANUAL': {
return (
<WorkflowEditTriggerManualForm
key={stepId}
trigger={stepDefinition.definition}
triggerOptions={{
readonly: true,
}}
/>
);
}
case 'WEBHOOK': {
return (
<WorkflowEditTriggerWebhookForm
key={stepId}
trigger={stepDefinition.definition}
triggerOptions={{
readonly: true,
}}
/>
);
}
case 'CRON': {
return (
<WorkflowEditTriggerCronForm
key={stepId}
trigger={stepDefinition.definition}
triggerOptions={{
readonly: true,
}}
/>
);
}
}
return assertUnreachable(
stepDefinition.definition,
`Expected the step to have an handler; ${JSON.stringify(stepDefinition)}`,
);
}
case 'action': {
switch (stepDefinition.definition.type) {
case 'CODE': {
return (
<WorkflowActionServerlessFunction
key={stepId}
action={stepDefinition.definition}
actionOptions={{
readonly: true,
}}
/>
);
}
case 'SEND_EMAIL': {
return (
<WorkflowEditActionSendEmail
key={stepId}
action={stepDefinition.definition}
actionOptions={{
readonly: true,
}}
/>
);
}
case 'CREATE_RECORD': {
return (
<WorkflowEditActionCreateRecord
key={stepId}
action={stepDefinition.definition}
actionOptions={{
readonly: true,
}}
/>
);
}
case 'UPDATE_RECORD': {
return (
<WorkflowEditActionUpdateRecord
key={stepId}
action={stepDefinition.definition}
actionOptions={{
readonly: true,
}}
/>
);
}
case 'DELETE_RECORD': {
return (
<WorkflowEditActionDeleteRecord
key={stepId}
action={stepDefinition.definition}
actionOptions={{
readonly: true,
}}
/>
);
}
case 'FIND_RECORDS': {
return (
<WorkflowEditActionFindRecords
key={stepId}
action={stepDefinition.definition}
actionOptions={{
readonly: true,
}}
/>
);
}
case 'FORM': {
// TODO: Implement form filler
return null;
}
}
}
}
return assertUnreachable(
stepDefinition,
`Expected the step to have an handler; ${JSON.stringify(stepDefinition)}`,
);
};
@@ -1,35 +1,18 @@
import { WorkflowAction, WorkflowTrigger } from '@/workflow/types/Workflow';
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
import { WorkflowActionServerlessFunction } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowActionServerlessFunction';
import { WorkflowEditActionCreateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord';
import { WorkflowEditActionDeleteRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord';
import { WorkflowEditActionFindRecords } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords';
import { WorkflowEditActionSendEmail } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail';
import { WorkflowEditActionUpdateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord';
import { WorkflowEditActionForm } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionForm';
import { WorkflowEditActionFormBuilder } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormBuilder';
import { WorkflowEditTriggerCronForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm';
import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm';
import { WorkflowEditTriggerManualForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerManualForm';
import { Suspense, lazy } from 'react';
import { isDefined } from 'twenty-shared';
import { RightDrawerSkeletonLoader } from '~/loading/components/RightDrawerSkeletonLoader';
import { WorkflowEditTriggerWebhookForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerWebhookForm';
const WorkflowEditActionServerlessFunction = lazy(() =>
import(
'@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionServerlessFunction'
).then((module) => ({
default: module.WorkflowEditActionServerlessFunction,
})),
);
const WorkflowReadonlyActionServerlessFunction = lazy(() =>
import(
'@/workflow/workflow-steps/workflow-actions/components/WorkflowReadonlyActionServerlessFunction'
).then((module) => ({
default: module.WorkflowReadonlyActionServerlessFunction,
})),
);
import { isDefined } from 'twenty-shared';
type WorkflowStepDetailProps = {
stepId: string;
@@ -115,20 +98,11 @@ export const WorkflowStepDetail = ({
switch (stepDefinition.definition.type) {
case 'CODE': {
return (
<Suspense fallback={<RightDrawerSkeletonLoader />}>
{props.readonly ? (
<WorkflowReadonlyActionServerlessFunction
key={stepId}
action={stepDefinition.definition}
/>
) : (
<WorkflowEditActionServerlessFunction
key={stepId}
action={stepDefinition.definition}
actionOptions={props}
/>
)}
</Suspense>
<WorkflowActionServerlessFunction
key={stepId}
action={stepDefinition.definition}
actionOptions={props}
/>
);
}
case 'SEND_EMAIL': {
@@ -182,7 +156,7 @@ export const WorkflowStepDetail = ({
case 'FORM': {
return (
<WorkflowEditActionForm
<WorkflowEditActionFormBuilder
key={stepId}
action={stepDefinition.definition}
actionOptions={props}