Skip to content

Commit 1115f6f

Browse files
authored
Automatically open pending form nodes in the side panel (#12332)
- Keep the side panel open when submitting a pending form node if the history isn't empty - Prevent the same pending Form step from being automatically opened several times - Prevent duplicated views of the same pending form in the side panel > [!WARNING] Switching from the side panel view to the full-screen view isn't robust, and I used a hack to make one use case work here. We'll have to improve that part later. ## Before https://github.com/user-attachments/assets/0f830e87-7681-49e9-acc8-a08178f631a2 ## After https://github.com/user-attachments/assets/bfd742d6-e38e-4981-a93b-8e895d3aa38a
1 parent b90cb3e commit 1115f6f

File tree

5 files changed

+133
-27
lines changed

5 files changed

+133
-27
lines changed

packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/wo
88
import { workflowVisualizerWorkflowRunIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowRunIdComponentState';
99
import { WorkflowRun } from '@/workflow/types/Workflow';
1010
import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId';
11+
import { workflowRunDiagramAutomaticallyOpenedStepsComponentState } from '@/workflow/workflow-diagram/states/workflowRunDiagramAutomaticallyOpenedStepsComponentState';
1112
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
1213
import { generateWorkflowRunDiagram } from '@/workflow/workflow-diagram/utils/generateWorkflowRunDiagram';
1314
import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
@@ -96,6 +97,14 @@ export const useRunWorkflowRunOpeningInCommandMenuSideEffects = () => {
9697
stepToOpenByDefault.id,
9798
);
9899

100+
set(
101+
workflowRunDiagramAutomaticallyOpenedStepsComponentState.atomFamily({
102+
instanceId: getWorkflowVisualizerComponentInstanceId({
103+
recordId,
104+
}),
105+
}),
106+
(steps) => [...steps, stepToOpenByDefault.id],
107+
);
99108
openWorkflowRunViewStepInCommandMenu({
100109
workflowId: workflowRunRecord.workflowId,
101110
workflowRunId: workflowRunRecord.id,

packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect.tsx

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
2+
import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu';
23
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
34
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
45
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
@@ -11,24 +12,33 @@ import { workflowVisualizerWorkflowRunIdComponentState } from '@/workflow/states
1112
import { WorkflowRunOutput } from '@/workflow/types/Workflow';
1213
import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState';
1314
import { workflowDiagramStatusComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusComponentState';
15+
import { workflowRunDiagramAutomaticallyOpenedStepsComponentState } from '@/workflow/workflow-diagram/states/workflowRunDiagramAutomaticallyOpenedStepsComponentState';
1416
import { workflowRunStepToOpenByDefaultComponentState } from '@/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultComponentState';
17+
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
1518
import { generateWorkflowRunDiagram } from '@/workflow/workflow-diagram/utils/generateWorkflowRunDiagram';
19+
import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
1620
import { selectWorkflowDiagramNode } from '@/workflow/workflow-diagram/utils/selectWorkflowDiagramNode';
1721
import { useContext, useEffect } from 'react';
1822
import { useRecoilCallback } from 'recoil';
1923
import { isDefined } from 'twenty-shared/utils';
24+
import { useIcons } from 'twenty-ui/display';
2025

2126
export const WorkflowRunVisualizerEffect = ({
2227
workflowRunId,
2328
}: {
2429
workflowRunId: string;
2530
}) => {
31+
const { getIcon } = useIcons();
32+
2633
const workflowRun = useWorkflowRun({ workflowRunId });
2734
const workflowVersion = useWorkflowVersion(workflowRun?.workflowVersionId);
2835

2936
const setWorkflowRunId = useSetRecoilComponentStateV2(
3037
workflowVisualizerWorkflowRunIdComponentState,
3138
);
39+
const workflowVisualizerWorkflowIdState = useRecoilComponentCallbackStateV2(
40+
workflowVisualizerWorkflowIdComponentState,
41+
);
3242
const setWorkflowVisualizerWorkflowId = useSetRecoilComponentStateV2(
3343
workflowVisualizerWorkflowIdComponentState,
3444
);
@@ -43,6 +53,15 @@ export const WorkflowRunVisualizerEffect = ({
4353
const workflowRunStepToOpenByDefaultState = useRecoilComponentCallbackStateV2(
4454
workflowRunStepToOpenByDefaultComponentState,
4555
);
56+
const workflowSelectedNodeState = useRecoilComponentCallbackStateV2(
57+
workflowSelectedNodeComponentState,
58+
);
59+
const workflowRunDiagramAutomaticallyOpenedStepsState =
60+
useRecoilComponentCallbackStateV2(
61+
workflowRunDiagramAutomaticallyOpenedStepsComponentState,
62+
);
63+
64+
const { openWorkflowRunViewStepInCommandMenu } = useWorkflowCommandMenu();
4665

4766
const { populateStepsOutputSchema } = useStepsOutputSchema();
4867

@@ -65,11 +84,11 @@ export const WorkflowRunVisualizerEffect = ({
6584
({
6685
workflowRunOutput,
6786
workflowVersionId,
68-
skipNodeSelection,
87+
isInRightDrawer,
6988
}: {
7089
workflowRunOutput: WorkflowRunOutput | undefined;
7190
workflowVersionId: string | undefined;
72-
skipNodeSelection: boolean;
91+
isInRightDrawer: boolean;
7392
}) => {
7493
if (!(isDefined(workflowRunOutput) && isDefined(workflowVersionId))) {
7594
set(flowState, undefined);
@@ -100,17 +119,60 @@ export const WorkflowRunVisualizerEffect = ({
100119
stepsOutput: workflowRunOutput.stepsOutput,
101120
});
102121

103-
if (isDefined(stepToOpenByDefault) && !skipNodeSelection) {
104-
const workflowRunDiagram = selectWorkflowDiagramNode({
105-
diagram: baseWorkflowRunDiagram,
106-
nodeIdToSelect: stepToOpenByDefault.id,
107-
});
108-
109-
set(workflowDiagramState, workflowRunDiagram);
110-
set(workflowRunStepToOpenByDefaultState, {
111-
id: stepToOpenByDefault.id,
112-
data: stepToOpenByDefault.data,
113-
});
122+
if (isDefined(stepToOpenByDefault)) {
123+
if (isInRightDrawer) {
124+
set(workflowDiagramState, baseWorkflowRunDiagram);
125+
126+
const workflowRunDiagramAutomaticallyOpenedSteps = getSnapshotValue(
127+
snapshot,
128+
workflowRunDiagramAutomaticallyOpenedStepsState,
129+
);
130+
const hasStepAlreadyBeenOpenedAutomatically =
131+
workflowRunDiagramAutomaticallyOpenedSteps.includes(
132+
stepToOpenByDefault.id,
133+
);
134+
135+
if (
136+
workflowDiagramStatus === 'done' &&
137+
!hasStepAlreadyBeenOpenedAutomatically
138+
) {
139+
set(workflowSelectedNodeState, stepToOpenByDefault.id);
140+
141+
const workflowVisualizerWorkflowId = getSnapshotValue(
142+
snapshot,
143+
workflowVisualizerWorkflowIdState,
144+
);
145+
if (!isDefined(workflowVisualizerWorkflowId)) {
146+
throw new Error(
147+
'The workflow id must be set; ensure the workflow id is always set before rendering the workflow diagram.',
148+
);
149+
}
150+
151+
set(workflowRunDiagramAutomaticallyOpenedStepsState, [
152+
...workflowRunDiagramAutomaticallyOpenedSteps,
153+
stepToOpenByDefault.id,
154+
]);
155+
openWorkflowRunViewStepInCommandMenu({
156+
workflowId: workflowVisualizerWorkflowId,
157+
workflowRunId,
158+
title: stepToOpenByDefault.data.name,
159+
icon: getIcon(getWorkflowNodeIconKey(stepToOpenByDefault.data)),
160+
workflowSelectedNode: stepToOpenByDefault.id,
161+
stepExecutionStatus: stepToOpenByDefault.data.runStatus,
162+
});
163+
}
164+
} else {
165+
const workflowRunDiagram = selectWorkflowDiagramNode({
166+
diagram: baseWorkflowRunDiagram,
167+
nodeIdToSelect: stepToOpenByDefault.id,
168+
});
169+
170+
set(workflowDiagramState, workflowRunDiagram);
171+
set(workflowRunStepToOpenByDefaultState, {
172+
id: stepToOpenByDefault.id,
173+
data: stepToOpenByDefault.data,
174+
});
175+
}
114176
} else {
115177
set(workflowDiagramState, baseWorkflowRunDiagram);
116178
}
@@ -121,17 +183,23 @@ export const WorkflowRunVisualizerEffect = ({
121183
},
122184
[
123185
flowState,
186+
getIcon,
187+
openWorkflowRunViewStepInCommandMenu,
124188
workflowDiagramState,
125189
workflowDiagramStatusState,
190+
workflowRunDiagramAutomaticallyOpenedStepsState,
191+
workflowRunId,
126192
workflowRunStepToOpenByDefaultState,
193+
workflowSelectedNodeState,
194+
workflowVisualizerWorkflowIdState,
127195
],
128196
);
129197

130198
useEffect(() => {
131199
handleWorkflowRunDiagramGeneration({
132200
workflowRunOutput: workflowRun?.output ?? undefined,
133201
workflowVersionId: workflowRun?.workflowVersionId,
134-
skipNodeSelection: isInRightDrawer,
202+
isInRightDrawer,
135203
});
136204
}, [
137205
handleWorkflowRunDiagramGeneration,

packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useHandleWorkflowRunDiagramCanvasInit.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/componen
55
import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow';
66
import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
77
import { workflowDiagramStatusComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusComponentState';
8+
import { workflowRunDiagramAutomaticallyOpenedStepsComponentState } from '@/workflow/workflow-diagram/states/workflowRunDiagramAutomaticallyOpenedStepsComponentState';
89
import { workflowRunStepToOpenByDefaultComponentState } from '@/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultComponentState';
910
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
1011
import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
@@ -33,6 +34,10 @@ export const useHandleWorkflowRunDiagramCanvasInit = () => {
3334
const workflowSelectedNodeState = useRecoilComponentCallbackStateV2(
3435
workflowSelectedNodeComponentState,
3536
);
37+
const workflowRunDiagramAutomaticallyOpenedStepsState =
38+
useRecoilComponentCallbackStateV2(
39+
workflowRunDiagramAutomaticallyOpenedStepsComponentState,
40+
);
3641

3742
const handleWorkflowRunDiagramCanvasInit = useRecoilCallback(
3843
({ snapshot, set }) =>
@@ -72,16 +77,30 @@ export const useHandleWorkflowRunDiagramCanvasInit = () => {
7277

7378
set(workflowSelectedNodeState, workflowStepToOpenByDefault.id);
7479

75-
openWorkflowRunViewStepInCommandMenu({
76-
workflowId: workflowVisualizerWorkflowId,
77-
workflowRunId,
78-
title: workflowStepToOpenByDefault.data.name,
79-
icon: getIcon(
80-
getWorkflowNodeIconKey(workflowStepToOpenByDefault.data),
81-
),
82-
workflowSelectedNode: workflowStepToOpenByDefault.id,
83-
stepExecutionStatus: workflowStepToOpenByDefault.data.runStatus,
84-
});
80+
const workflowRunDiagramAutomaticallyOpenedSteps = getSnapshotValue(
81+
snapshot,
82+
workflowRunDiagramAutomaticallyOpenedStepsState,
83+
);
84+
const hasStepAlreadyBeenOpenedAutomatically =
85+
workflowRunDiagramAutomaticallyOpenedSteps.includes(
86+
workflowStepToOpenByDefault.id,
87+
);
88+
89+
// FIXME: This is a workaround to avoid opening a workflow run step twice when going from the side panel to the fullscreen show page.
90+
// The step is opened in the `handleSelectionChange` function of `WorkflowRunDiagramCanvasEffect`. I think it shouldn't be opened there but
91+
// we should keep opening the step here, in `handleWorkflowRunDiagramCanvasInit`.
92+
if (!hasStepAlreadyBeenOpenedAutomatically) {
93+
openWorkflowRunViewStepInCommandMenu({
94+
workflowId: workflowVisualizerWorkflowId,
95+
workflowRunId,
96+
title: workflowStepToOpenByDefault.data.name,
97+
icon: getIcon(
98+
getWorkflowNodeIconKey(workflowStepToOpenByDefault.data),
99+
),
100+
workflowSelectedNode: workflowStepToOpenByDefault.id,
101+
stepExecutionStatus: workflowStepToOpenByDefault.data.runStatus,
102+
});
103+
}
85104

86105
set(workflowRunStepToOpenByDefaultState, undefined);
87106
}
@@ -92,6 +111,7 @@ export const useHandleWorkflowRunDiagramCanvasInit = () => {
92111
workflowRunStepToOpenByDefaultState,
93112
workflowVisualizerWorkflowIdState,
94113
workflowSelectedNodeState,
114+
workflowRunDiagramAutomaticallyOpenedStepsState,
95115
openWorkflowRunViewStepInCommandMenu,
96116
workflowRunId,
97117
getIcon,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
2+
import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext';
3+
4+
export const workflowRunDiagramAutomaticallyOpenedStepsComponentState =
5+
createComponentStateV2<string[]>({
6+
key: 'workflowRunDiagramAutomaticallyOpenedStepsComponentState',
7+
defaultValue: [],
8+
componentInstanceContext: WorkflowVisualizerComponentInstanceContext,
9+
});

packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CmdEnterActionButton } from '@/action-menu/components/CmdEnterActionButton';
2-
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
2+
import { useCommandMenuHistory } from '@/command-menu/hooks/useCommandMenuHistory';
33
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
44
import { FormSingleRecordPicker } from '@/object-record/record-field/form-types/components/FormSingleRecordPicker';
55
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
@@ -37,7 +37,7 @@ export const WorkflowEditActionFormFiller = ({
3737
const { submitFormStep } = useSubmitFormStep();
3838
const [formData, setFormData] = useState<FormData>(action.settings.input);
3939
const { workflowRunId } = useWorkflowStepContextOrThrow();
40-
const { closeCommandMenu } = useCommandMenu();
40+
const { goBackFromCommandMenu } = useCommandMenuHistory();
4141
const { updateWorkflowRunStep } = useUpdateWorkflowRunStep();
4242
const [error, setError] = useState<string | undefined>(undefined);
4343
const canSubmit = !actionOptions.readonly && !isDefined(error);
@@ -98,7 +98,7 @@ export const WorkflowEditActionFormFiller = ({
9898
response,
9999
});
100100

101-
closeCommandMenu();
101+
goBackFromCommandMenu();
102102
};
103103

104104
useEffect(() => {

0 commit comments

Comments
 (0)