Add Delete branch buttons (#13826)

Deleting branches containing a filter isn't yet supported by the
backend, but I set up the button in the frontend.



https://github.com/user-attachments/assets/f27234c4-ff9c-4f5c-930c-64d824981454
This commit is contained in:
Baptiste Devessier
2025-08-11 17:38:22 +02:00
committed by GitHub
parent 32cdb66802
commit 3e5b60a923
3 changed files with 47 additions and 18 deletions
@@ -13,15 +13,16 @@ import { useOpenWorkflowEditFilterInCommandMenu } from '@/workflow/workflow-diag
import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation';
import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { useCreateStep } from '@/workflow/workflow-steps/hooks/useCreateStep';
import { useDeleteEdge } from '@/workflow/workflow-steps/hooks/useDeleteEdge';
import {
EdgeLabelRenderer,
type EdgeProps,
getBezierPath,
} from '@xyflow/react';
import { useContext } from 'react';
import { type MouseEvent, useContext } from 'react';
import { useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { IconFilter, IconPlus } from 'twenty-ui/display';
import { IconFilter, IconPlus, IconTrash } from 'twenty-ui/display';
type WorkflowDiagramDefaultEdgeEditableProps = EdgeProps<WorkflowDiagramEdge>;
@@ -54,6 +55,8 @@ export const WorkflowDiagramDefaultEdgeEditable = ({
const { createStep } = useCreateStep({ workflow });
const { deleteEdge } = useDeleteEdge({ workflow });
const { startNodeCreation, isNodeCreationStarted } = useStartNodeCreation();
const nodeCreationStarted = isNodeCreationStarted({
@@ -97,6 +100,12 @@ export const WorkflowDiagramDefaultEdgeEditable = ({
});
};
const handleDeleteBranch = async (event: MouseEvent) => {
event.stopPropagation();
await deleteEdge({ source, target });
};
return (
<>
<WorkflowDiagramBaseEdge
@@ -126,6 +135,10 @@ export const WorkflowDiagramDefaultEdgeEditable = ({
Icon: IconPlus,
onClick: handleNodeButtonClick,
},
{
Icon: IconTrash,
onClick: handleDeleteBranch,
},
]}
selected={nodeCreationStarted}
/>
@@ -26,9 +26,11 @@ import {
type WorkflowDiagramEdgeData,
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors';
import { useDeleteEdge } from '@/workflow/workflow-steps/hooks/useDeleteEdge';
import { useDeleteStep } from '@/workflow/workflow-steps/hooks/useDeleteStep';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { isNonEmptyString } from '@sniptt/guards';
import {
EdgeLabelRenderer,
@@ -37,12 +39,12 @@ import {
} from '@xyflow/react';
import { useContext } from 'react';
import { useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import {
IconDotsVertical,
IconFilter,
IconFilterX,
IconPlus,
IconTrash,
} from 'twenty-ui/display';
import { IconButtonGroup } from 'twenty-ui/input';
import { MenuItem } from 'twenty-ui/navigation';
@@ -91,6 +93,8 @@ export const WorkflowDiagramFilterEdgeEditable = ({
}: WorkflowDiagramFilterEdgeEditableProps) => {
assertFilterEdgeDataOrThrow(data);
const { t } = useLingui();
const [edgePath, labelX, labelY] = getBezierPath({
sourceX,
sourceY,
@@ -102,9 +106,11 @@ export const WorkflowDiagramFilterEdgeEditable = ({
workflowVisualizerWorkflowIdComponentState,
);
const workflow = useWorkflowWithCurrentVersion(workflowVisualizerWorkflowId);
const { isInRightDrawer } = useContext(ActionMenuContext);
const { deleteStep } = useDeleteStep({ workflow });
const { deleteEdge } = useDeleteEdge({ workflow });
const { startNodeCreation, isNodeCreationStarted } = useStartNodeCreation();
const setCommandMenuNavigationStack = useSetRecoilState(
@@ -153,6 +159,12 @@ export const WorkflowDiagramFilterEdgeEditable = ({
});
};
const handleRemoveFilterButtonClick = async () => {
closeDropdown(dropdownId);
await deleteStep(data.stepId);
};
const handleAddNodeButtonClick = () => {
closeDropdown(dropdownId);
@@ -163,6 +175,12 @@ export const WorkflowDiagramFilterEdgeEditable = ({
});
};
const handleDeleteBranchClick = async () => {
closeDropdown(dropdownId);
await deleteEdge({ source, target });
};
return (
<>
<WorkflowDiagramBaseEdge
@@ -244,25 +262,20 @@ export const WorkflowDiagramFilterEdgeEditable = ({
}}
/>
<MenuItem
text="Remove Filter"
text={t`Remove filter`}
LeftIcon={IconFilterX}
onClick={() => {
closeDropdown(dropdownId);
if (!isDefined(data.stepId)) {
throw new Error(
'Step ID must be configured for the edge when rendering a filter',
);
}
return deleteStep(data.stepId);
}}
onClick={handleRemoveFilterButtonClick}
/>
<MenuItem
text="Add Node"
text={t`Add Node`}
LeftIcon={IconPlus}
onClick={handleAddNodeButtonClick}
/>
<MenuItem
text={t`Delete branch`}
LeftIcon={IconTrash}
onClick={handleDeleteBranchClick}
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
@@ -1,14 +1,15 @@
import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion';
import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
import { assertWorkflowWithCurrentVersionIsDefined } from '@/workflow/utils/assertWorkflowWithCurrentVersionIsDefined';
import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagramEdge';
import { useDeleteWorkflowVersionEdge } from '@/workflow/workflow-steps/hooks/useDeleteWorkflowVersionEdge';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagramEdge';
export const useDeleteEdge = ({
workflow,
}: {
workflow: WorkflowWithCurrentVersion;
workflow: WorkflowWithCurrentVersion | undefined;
}) => {
const [isLoading, setIsLoading] = useState(false);
@@ -17,6 +18,8 @@ export const useDeleteEdge = ({
const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion();
const deleteEdge = async ({ source, target }: WorkflowDiagramEdge) => {
assertWorkflowWithCurrentVersionIsDefined(workflow);
if (isLoading) {
return;
}