Put delete button on step footer + add unique fields on upsert action (#15497)

<img width="491" height="823" alt="Capture d’écran 2025-10-31 à 14 54
38"
src="https://github.com/user-attachments/assets/8058a6a3-8a6c-4cdd-93d9-2af71208dc5a"
/>
This commit is contained in:
Thomas Trompette
2025-10-31 17:39:45 +01:00
committed by GitHub
parent d640b93096
commit 5f13e6fcf4
4 changed files with 65 additions and 10 deletions
@@ -11,6 +11,7 @@ import { useDuplicateStep } from '@/workflow/workflow-steps/hooks/useDuplicateSt
import { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
import { useId } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
import { IconCopyPlus, IconPencil, IconTrash } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
@@ -35,6 +36,8 @@ export const WorkflowStepFooter = ({
openWorkflowTriggerTypeInCommandMenu,
} = useWorkflowCommandMenu();
const { deleteStep } = useDeleteStep();
const shouldPinDeleteButton =
!isDefined(additionalActions) || additionalActions.length === 0;
const OptionsDropdown = (
<Dropdown
@@ -77,14 +80,16 @@ export const WorkflowStepFooter = ({
LeftIcon={IconCopyPlus}
/>
)}
<MenuItem
onClick={() => {
closeDropdown(dropdownId);
deleteStep(stepId);
}}
text={t`Delete node`}
LeftIcon={IconTrash}
/>
{!shouldPinDeleteButton && (
<MenuItem
onClick={() => {
closeDropdown(dropdownId);
deleteStep(stepId);
}}
text={t`Delete node`}
LeftIcon={IconTrash}
/>
)}
</SelectableList>
</DropdownMenuItemsContainer>
</DropdownContent>
@@ -92,9 +97,25 @@ export const WorkflowStepFooter = ({
/>
);
const deleteButton = (
<Button
title="Delete"
onClick={() => {
deleteStep(stepId);
}}
Icon={IconTrash}
accent="danger"
inverted
/>
);
return (
<RightDrawerFooter
actions={[OptionsDropdown, ...(additionalActions ?? [])]}
actions={[
OptionsDropdown,
...(additionalActions ?? []),
...(shouldPinDeleteButton ? [deleteButton] : []),
]}
/>
);
};
@@ -6,10 +6,12 @@ import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/is
import { Select } from '@/ui/input/components/Select';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { useViewOrDefaultViewFromPrefetchedViews } from '@/views/hooks/useViewOrDefaultViewFromPrefetchedViews';
import { WorkflowFieldsMultiSelect } from '@/workflow/components/WorkflowEditUpdateEventFieldsMultiSelect';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { shouldDisplayFormField } from '@/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react';
import { t } from '@lingui/core/macro';
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { canObjectBeManagedByWorkflow } from 'twenty-shared/workflow';
@@ -96,6 +98,8 @@ export const WorkflowCreateRecordBody = ({
(item) => item.nameSingular === objectNameSingular,
);
const objectLabelSingular = objectMetadataItem?.labelSingular;
const { view: indexView } = useViewOrDefaultViewFromPrefetchedViews({
objectMetadataItemId: objectMetadataItem?.id ?? '',
});
@@ -117,6 +121,10 @@ export const WorkflowCreateRecordBody = ({
})
.sort(sortByViewFieldPosition);
const uniqueFieldMetadataItems = inlineFieldMetadataItems?.filter(
(fieldMetadataItem) => fieldMetadataItem.isUnique,
);
const inlineFieldDefinitions = isDefined(objectMetadataItem)
? inlineFieldMetadataItems?.map((fieldMetadataItem) =>
formatFieldMetadataItemAsFieldDefinition({
@@ -206,6 +214,25 @@ export const WorkflowCreateRecordBody = ({
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
{actionType === 'UPSERT_RECORD' &&
isDefined(objectMetadataItem) &&
isDefined(uniqueFieldMetadataItems) &&
uniqueFieldMetadataItems.length > 0 && (
<WorkflowFieldsMultiSelect
label={t`Unique fields`}
objectMetadataItem={objectMetadataItem}
handleFieldsChange={() => {}}
defaultFields={
uniqueFieldMetadataItems?.map(
(fieldMetadataItem) => fieldMetadataItem.name,
) ?? []
}
placeholder={t`Object unique fields`}
readonly
hint={t`We match on these fields. If a ${objectLabelSingular} already exists, we update it. Otherwise, we create a new one.`}
/>
)}
<HorizontalSeparator noMargin />
{inlineFieldDefinitions?.map((fieldDefinition) => {