Make workflow step name editable (#8677)
- Use TextInput in header title - add onTitleChange prop - rename field name instead of label To fix : - padding right on title comes from current TextInput component. It needs to be refactored https://github.com/user-attachments/assets/535cd6d3-866b-4a61-9c5d-cdbe7710396a
This commit is contained in:
@ -39,7 +39,7 @@ export const RightDrawerWorkflowSelectTriggerTypeContent = ({
|
||||
<MenuItem
|
||||
key={action.type}
|
||||
LeftIcon={action.icon}
|
||||
text={action.label}
|
||||
text={action.name}
|
||||
onClick={async () => {
|
||||
await updateTrigger(
|
||||
getTriggerDefaultDefinition({
|
||||
|
||||
@ -2,7 +2,7 @@ import { WorkflowDiagramStepNodeData } from '@/workflow/types/WorkflowDiagram';
|
||||
import styled from '@emotion/styled';
|
||||
import { Handle, Position } from '@xyflow/react';
|
||||
import React from 'react';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
import { isDefined, OverflowingTextWithTooltip } from 'twenty-ui';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
type Variant = 'placeholder';
|
||||
@ -68,6 +68,7 @@ const StyledStepNodeLabel = styled.div<{ variant?: Variant }>`
|
||||
variant === 'placeholder'
|
||||
? theme.font.color.extraLight
|
||||
: theme.font.color.primary};
|
||||
max-width: 200px;
|
||||
`;
|
||||
|
||||
const StyledSourceHandle = styled(Handle)`
|
||||
@ -90,13 +91,13 @@ const StyledRightFloatingElementContainer = styled.div`
|
||||
|
||||
export const WorkflowDiagramBaseStepNode = ({
|
||||
nodeType,
|
||||
label,
|
||||
name,
|
||||
variant,
|
||||
Icon,
|
||||
RightFloatingElement,
|
||||
}: {
|
||||
nodeType: WorkflowDiagramStepNodeData['nodeType'];
|
||||
label: string;
|
||||
name: string;
|
||||
variant?: Variant;
|
||||
Icon?: React.ReactNode;
|
||||
RightFloatingElement?: React.ReactNode;
|
||||
@ -113,7 +114,7 @@ export const WorkflowDiagramBaseStepNode = ({
|
||||
<StyledStepNodeLabel variant={variant}>
|
||||
{Icon}
|
||||
|
||||
{label}
|
||||
<OverflowingTextWithTooltip text={name} />
|
||||
</StyledStepNodeLabel>
|
||||
|
||||
{isDefined(RightFloatingElement) ? (
|
||||
|
||||
@ -17,7 +17,7 @@ export const WorkflowDiagramEmptyTrigger = () => {
|
||||
|
||||
return (
|
||||
<WorkflowDiagramBaseStepNode
|
||||
label="Add a Trigger"
|
||||
name="Add a Trigger"
|
||||
nodeType="trigger"
|
||||
variant="placeholder"
|
||||
Icon={
|
||||
|
||||
@ -100,8 +100,8 @@ export const WorkflowDiagramStepNodeBase = ({
|
||||
|
||||
return (
|
||||
<WorkflowDiagramBaseStepNode
|
||||
name={data.name}
|
||||
nodeType={data.nodeType}
|
||||
label={data.label}
|
||||
Icon={renderStepIcon()}
|
||||
RightFloatingElement={RightFloatingElement}
|
||||
/>
|
||||
|
||||
@ -119,15 +119,27 @@ export const WorkflowEditActionFormRecordCreate = ({
|
||||
};
|
||||
}, [saveAction]);
|
||||
|
||||
const headerTitle = isDefined(action.name) ? action.name : `Create Record`;
|
||||
|
||||
return (
|
||||
<WorkflowEditGenericFormBase
|
||||
onTitleChange={(newName: string) => {
|
||||
if (actionOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
actionOptions.onActionUpdate({
|
||||
...action,
|
||||
name: newName,
|
||||
});
|
||||
}}
|
||||
HeaderIcon={
|
||||
<IconAddressBook
|
||||
color={theme.font.color.tertiary}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
/>
|
||||
}
|
||||
headerTitle="Record Create"
|
||||
headerTitle={headerTitle}
|
||||
headerType="Action"
|
||||
>
|
||||
<Select
|
||||
|
||||
@ -166,11 +166,23 @@ export const WorkflowEditActionFormSendEmail = ({
|
||||
}
|
||||
});
|
||||
|
||||
const headerTitle = isDefined(action.name) ? action.name : 'Send Email';
|
||||
|
||||
return (
|
||||
!loading && (
|
||||
<WorkflowEditGenericFormBase
|
||||
onTitleChange={(newName: string) => {
|
||||
if (actionOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
actionOptions.onActionUpdate({
|
||||
...action,
|
||||
name: newName,
|
||||
});
|
||||
}}
|
||||
HeaderIcon={<IconMail color={theme.color.blue} />}
|
||||
headerTitle="Send Email"
|
||||
headerTitle={headerTitle}
|
||||
headerType="Email"
|
||||
>
|
||||
<Controller
|
||||
|
||||
@ -217,10 +217,24 @@ export const WorkflowEditActionFormServerlessFunctionInner = ({
|
||||
});
|
||||
};
|
||||
|
||||
const headerTitle = isDefined(action.name)
|
||||
? action.name
|
||||
: 'Code - Serverless Function';
|
||||
|
||||
return (
|
||||
<WorkflowEditGenericFormBase
|
||||
onTitleChange={(newName: string) => {
|
||||
if (actionOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
actionOptions?.onActionUpdate({
|
||||
...action,
|
||||
name: newName,
|
||||
});
|
||||
}}
|
||||
HeaderIcon={<IconCode color={theme.color.orange} />}
|
||||
headerTitle="Code - Serverless Function"
|
||||
headerTitle={headerTitle}
|
||||
headerType="Code"
|
||||
>
|
||||
<Select
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||
import styled from '@emotion/styled';
|
||||
import React from 'react';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
background-color: ${({ theme }) => theme.background.secondary};
|
||||
@ -40,22 +42,36 @@ const StyledContentContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const WorkflowEditGenericFormBase = ({
|
||||
onTitleChange,
|
||||
HeaderIcon,
|
||||
headerTitle,
|
||||
headerType,
|
||||
children,
|
||||
}: {
|
||||
onTitleChange: (newTitle: string) => void;
|
||||
HeaderIcon: React.ReactNode;
|
||||
headerTitle: string;
|
||||
headerType: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const debouncedOnTitleChange = useDebouncedCallback(onTitleChange, 100);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledHeader>
|
||||
<StyledHeaderIconContainer>{HeaderIcon}</StyledHeaderIconContainer>
|
||||
|
||||
<StyledHeaderTitle>{headerTitle}</StyledHeaderTitle>
|
||||
<StyledHeaderTitle>
|
||||
<TextInput
|
||||
value={headerTitle}
|
||||
copyButton={false}
|
||||
hotkeyScope="workflow-step-title"
|
||||
onEnter={onTitleChange}
|
||||
onEscape={onTitleChange}
|
||||
onChange={debouncedOnTitleChange}
|
||||
shouldTrim={false}
|
||||
/>
|
||||
</StyledHeaderTitle>
|
||||
|
||||
<StyledHeaderType>{headerType}</StyledHeaderType>
|
||||
</StyledHeader>
|
||||
|
||||
@ -1,50 +1,12 @@
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { Select, SelectOption } from '@/ui/input/components/Select';
|
||||
import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
|
||||
import { OBJECT_EVENT_TRIGGERS } from '@/workflow/constants/ObjectEventTriggers';
|
||||
import { WorkflowDatabaseEventTrigger } from '@/workflow/types/Workflow';
|
||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { IconPlaylistAdd, isDefined } from 'twenty-ui';
|
||||
|
||||
const StyledTriggerHeader = styled.div`
|
||||
background-color: ${({ theme }) => theme.background.secondary};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${({ theme }) => theme.spacing(6)};
|
||||
`;
|
||||
|
||||
const StyledTriggerHeaderTitle = styled.p`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
font-size: ${({ theme }) => theme.font.size.xl};
|
||||
|
||||
margin: ${({ theme }) => theme.spacing(3)} 0;
|
||||
`;
|
||||
|
||||
const StyledTriggerHeaderType = styled.p`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const StyledTriggerHeaderIconContainer = styled.div`
|
||||
align-self: flex-start;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.background.transparent.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.xs};
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledTriggerSettings = styled.div`
|
||||
padding: ${({ theme }) => theme.spacing(6)};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
type WorkflowEditTriggerDatabaseEventFormProps = {
|
||||
trigger: WorkflowDatabaseEventTrigger;
|
||||
triggerOptions:
|
||||
@ -87,92 +49,98 @@ export const WorkflowEditTriggerDatabaseEventForm = ({
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const headerTitle = isDefined(trigger.name)
|
||||
? trigger.name
|
||||
: isDefined(recordTypeMetadata) && isDefined(selectedEvent)
|
||||
? `When a ${recordTypeMetadata.labelSingular} is ${selectedEvent.label}`
|
||||
: '-';
|
||||
|
||||
const headerType = isDefined(selectedEvent)
|
||||
? `Trigger · Record is ${selectedEvent.label}`
|
||||
: '-';
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTriggerHeader>
|
||||
<StyledTriggerHeaderIconContainer>
|
||||
<IconPlaylistAdd color={theme.font.color.tertiary} />
|
||||
</StyledTriggerHeaderIconContainer>
|
||||
<WorkflowEditGenericFormBase
|
||||
onTitleChange={(newName: string) => {
|
||||
if (triggerOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
<StyledTriggerHeaderTitle>
|
||||
{isDefined(recordTypeMetadata) && isDefined(selectedEvent)
|
||||
? `When a ${recordTypeMetadata.labelSingular} is ${selectedEvent.label}`
|
||||
: '-'}
|
||||
</StyledTriggerHeaderTitle>
|
||||
triggerOptions.onTriggerUpdate({
|
||||
...trigger,
|
||||
name: newName,
|
||||
});
|
||||
}}
|
||||
HeaderIcon={<IconPlaylistAdd color={theme.font.color.tertiary} />}
|
||||
headerTitle={headerTitle}
|
||||
headerType={headerType}
|
||||
>
|
||||
<Select
|
||||
dropdownId="workflow-edit-trigger-record-type"
|
||||
label="Record Type"
|
||||
fullWidth
|
||||
disabled={triggerOptions.readonly}
|
||||
value={triggerEvent?.objectType}
|
||||
emptyOption={{ label: 'Select an option', value: '' }}
|
||||
options={availableMetadata}
|
||||
onChange={(updatedRecordType) => {
|
||||
if (triggerOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
<StyledTriggerHeaderType>
|
||||
{isDefined(selectedEvent)
|
||||
? `Trigger · Record is ${selectedEvent.label}`
|
||||
: '-'}
|
||||
</StyledTriggerHeaderType>
|
||||
</StyledTriggerHeader>
|
||||
|
||||
<StyledTriggerSettings>
|
||||
<Select
|
||||
dropdownId="workflow-edit-trigger-record-type"
|
||||
label="Record Type"
|
||||
fullWidth
|
||||
disabled={triggerOptions.readonly}
|
||||
value={triggerEvent?.objectType}
|
||||
emptyOption={{ label: 'Select an option', value: '' }}
|
||||
options={availableMetadata}
|
||||
onChange={(updatedRecordType) => {
|
||||
if (triggerOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerOptions.onTriggerUpdate(
|
||||
isDefined(trigger) && isDefined(triggerEvent)
|
||||
? {
|
||||
...trigger,
|
||||
settings: {
|
||||
...trigger.settings,
|
||||
eventName: `${updatedRecordType}.${triggerEvent.event}`,
|
||||
},
|
||||
}
|
||||
: {
|
||||
type: 'DATABASE_EVENT',
|
||||
settings: {
|
||||
eventName: `${updatedRecordType}.${OBJECT_EVENT_TRIGGERS[0].value}`,
|
||||
outputSchema: {},
|
||||
},
|
||||
triggerOptions.onTriggerUpdate(
|
||||
isDefined(trigger) && isDefined(triggerEvent)
|
||||
? {
|
||||
...trigger,
|
||||
settings: {
|
||||
...trigger.settings,
|
||||
eventName: `${updatedRecordType}.${triggerEvent.event}`,
|
||||
},
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
dropdownId="workflow-edit-trigger-event-type"
|
||||
label="Event type"
|
||||
fullWidth
|
||||
value={triggerEvent?.event}
|
||||
emptyOption={{ label: 'Select an option', value: '' }}
|
||||
options={OBJECT_EVENT_TRIGGERS}
|
||||
disabled={triggerOptions.readonly}
|
||||
onChange={(updatedEvent) => {
|
||||
if (triggerOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerOptions.onTriggerUpdate(
|
||||
isDefined(trigger) && isDefined(triggerEvent)
|
||||
? {
|
||||
...trigger,
|
||||
settings: {
|
||||
...trigger.settings,
|
||||
eventName: `${triggerEvent.objectType}.${updatedEvent}`,
|
||||
},
|
||||
}
|
||||
: {
|
||||
type: 'DATABASE_EVENT',
|
||||
settings: {
|
||||
eventName: `${availableMetadata[0].value}.${updatedEvent}`,
|
||||
outputSchema: {},
|
||||
},
|
||||
}
|
||||
: {
|
||||
name: headerTitle,
|
||||
type: 'DATABASE_EVENT',
|
||||
settings: {
|
||||
eventName: `${updatedRecordType}.${OBJECT_EVENT_TRIGGERS[0].value}`,
|
||||
outputSchema: {},
|
||||
},
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</StyledTriggerSettings>
|
||||
</>
|
||||
},
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
dropdownId="workflow-edit-trigger-event-type"
|
||||
label="Event type"
|
||||
fullWidth
|
||||
value={triggerEvent?.event}
|
||||
emptyOption={{ label: 'Select an option', value: '' }}
|
||||
options={OBJECT_EVENT_TRIGGERS}
|
||||
disabled={triggerOptions.readonly}
|
||||
onChange={(updatedEvent) => {
|
||||
if (triggerOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerOptions.onTriggerUpdate(
|
||||
isDefined(trigger) && isDefined(triggerEvent)
|
||||
? {
|
||||
...trigger,
|
||||
settings: {
|
||||
...trigger.settings,
|
||||
eventName: `${triggerEvent.objectType}.${updatedEvent}`,
|
||||
},
|
||||
}
|
||||
: {
|
||||
name: headerTitle,
|
||||
type: 'DATABASE_EVENT',
|
||||
settings: {
|
||||
eventName: `${availableMetadata[0].value}.${updatedEvent}`,
|
||||
outputSchema: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</WorkflowEditGenericFormBase>
|
||||
);
|
||||
};
|
||||
|
||||
@ -44,10 +44,22 @@ export const WorkflowEditTriggerManualForm = ({
|
||||
? 'WHEN_RECORD_SELECTED'
|
||||
: 'EVERYWHERE';
|
||||
|
||||
const headerTitle = isDefined(trigger.name) ? trigger.name : 'Manual Trigger';
|
||||
|
||||
return (
|
||||
<WorkflowEditGenericFormBase
|
||||
onTitleChange={(newName: string) => {
|
||||
if (triggerOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerOptions.onTriggerUpdate({
|
||||
...trigger,
|
||||
name: newName,
|
||||
});
|
||||
}}
|
||||
HeaderIcon={<IconHandMove color={theme.font.color.tertiary} />}
|
||||
headerTitle="Manual Trigger"
|
||||
headerTitle={headerTitle}
|
||||
headerType="Trigger · Manual"
|
||||
>
|
||||
<Select
|
||||
|
||||
Reference in New Issue
Block a user