From 5ec6cb0e6f7bebfb154d86f2e794937b25557d89 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Fri, 22 Nov 2024 16:25:01 +0100 Subject: [PATCH] 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 --- .../ui/field/input/components/TextInput.tsx | 20 +- ...DrawerWorkflowSelectTriggerTypeContent.tsx | 2 +- .../WorkflowDiagramBaseStepNode.tsx | 9 +- .../WorkflowDiagramEmptyTrigger.tsx | 2 +- .../WorkflowDiagramStepNodeBase.tsx | 2 +- .../WorkflowEditActionFormRecordCreate.tsx | 14 +- .../WorkflowEditActionFormSendEmail.tsx | 14 +- ...wEditActionFormServerlessFunctionInner.tsx | 16 +- .../WorkflowEditGenericFormBase.tsx | 18 +- .../WorkflowEditTriggerDatabaseEventForm.tsx | 210 ++++++++---------- .../WorkflowEditTriggerManualForm.tsx | 14 +- .../workflow/constants/TriggerTypes.ts | 6 +- .../useAvailableVariablesInWorkflowStep.ts | 4 +- .../src/modules/workflow/types/Workflow.ts | 2 +- .../modules/workflow/types/WorkflowDiagram.ts | 4 +- .../__tests__/addCreateStepNodes.test.ts | 1 + .../__tests__/generateWorkflowDiagram.test.ts | 6 +- .../getWorkflowVersionDiagram.test.ts | 4 +- .../utils/__tests__/insertStep.test.ts | 4 + .../__tests__/mergeWorkflowDiagrams.test.ts | 12 +- .../utils/__tests__/removeStep.test.ts | 2 + .../utils/__tests__/replaceStep.test.ts | 2 + .../workflow/utils/generateWorkflowDiagram.ts | 4 +- .../utils/getStepDefaultDefinition.ts | 2 +- 24 files changed, 217 insertions(+), 157 deletions(-) diff --git a/packages/twenty-front/src/modules/ui/field/input/components/TextInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/TextInput.tsx index b932cbdc5f..a5afa4209f 100644 --- a/packages/twenty-front/src/modules/ui/field/input/components/TextInput.tsx +++ b/packages/twenty-front/src/modules/ui/field/input/components/TextInput.tsx @@ -19,10 +19,19 @@ type TextInputProps = { onEscape: (newText: string) => void; onTab?: (newText: string) => void; onShiftTab?: (newText: string) => void; - onClickOutside: (event: MouseEvent | TouchEvent, inputValue: string) => void; + onClickOutside?: (event: MouseEvent | TouchEvent, inputValue: string) => void; hotkeyScope: string; onChange?: (newText: string) => void; copyButton?: boolean; + shouldTrim?: boolean; +}; + +const getValue = (value: string, shouldTrim: boolean) => { + if (shouldTrim) { + return value.trim(); + } + + return value; }; export const TextInput = ({ @@ -37,6 +46,7 @@ export const TextInput = ({ onClickOutside, onChange, copyButton = true, + shouldTrim = true, }: TextInputProps) => { const [internalText, setInternalText] = useState(value); @@ -44,12 +54,12 @@ export const TextInput = ({ const copyRef = useRef(null); const handleChange = (event: ChangeEvent) => { - setInternalText(event.target.value.trim()); - onChange?.(event.target.value.trim()); + setInternalText(getValue(event.target.value, shouldTrim)); + onChange?.(getValue(event.target.value, shouldTrim)); }; useEffect(() => { - setInternalText(value.trim()); - }, [value]); + setInternalText(getValue(value, shouldTrim)); + }, [value, shouldTrim]); useRegisterInputEvents({ inputRef: wrapperRef, diff --git a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx index 2667195f23..d02ea69c7c 100644 --- a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx +++ b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx @@ -39,7 +39,7 @@ export const RightDrawerWorkflowSelectTriggerTypeContent = ({ { await updateTrigger( getTriggerDefaultDefinition({ diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramBaseStepNode.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramBaseStepNode.tsx index 0318f61a6b..caf1dc8565 100644 --- a/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramBaseStepNode.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramBaseStepNode.tsx @@ -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 = ({ {Icon} - {label} + {isDefined(RightFloatingElement) ? ( diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramEmptyTrigger.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramEmptyTrigger.tsx index a355733219..68f4c0e66c 100644 --- a/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramEmptyTrigger.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramEmptyTrigger.tsx @@ -17,7 +17,7 @@ export const WorkflowDiagramEmptyTrigger = () => { return ( diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormRecordCreate.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormRecordCreate.tsx index 647dfe240a..aa86a84a89 100644 --- a/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormRecordCreate.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormRecordCreate.tsx @@ -119,15 +119,27 @@ export const WorkflowEditActionFormRecordCreate = ({ }; }, [saveAction]); + const headerTitle = isDefined(action.name) ? action.name : `Create Record`; + return ( { + if (actionOptions.readonly === true) { + return; + } + + actionOptions.onActionUpdate({ + ...action, + name: newName, + }); + }} HeaderIcon={ } - headerTitle="Record Create" + headerTitle={headerTitle} headerType="Action" > 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 ( <> {HeaderIcon} - {headerTitle} + + + {headerType} diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowEditTriggerDatabaseEventForm.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowEditTriggerDatabaseEventForm.tsx index 507afd9151..9cce5d103c 100644 --- a/packages/twenty-front/src/modules/workflow/components/WorkflowEditTriggerDatabaseEventForm.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowEditTriggerDatabaseEventForm.tsx @@ -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 ( - <> - - - - + { + if (triggerOptions.readonly === true) { + return; + } - - {isDefined(recordTypeMetadata) && isDefined(selectedEvent) - ? `When a ${recordTypeMetadata.labelSingular} is ${selectedEvent.label}` - : '-'} - + triggerOptions.onTriggerUpdate({ + ...trigger, + name: newName, + }); + }} + HeaderIcon={} + headerTitle={headerTitle} + headerType={headerType} + > + { - 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}`, }, - ); - }} - /> - { + 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: {}, + }, + }, + ); + }} + /> + ); }; diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowEditTriggerManualForm.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowEditTriggerManualForm.tsx index 3863767653..683cef39f9 100644 --- a/packages/twenty-front/src/modules/workflow/components/WorkflowEditTriggerManualForm.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowEditTriggerManualForm.tsx @@ -44,10 +44,22 @@ export const WorkflowEditTriggerManualForm = ({ ? 'WHEN_RECORD_SELECTED' : 'EVERYWHERE'; + const headerTitle = isDefined(trigger.name) ? trigger.name : 'Manual Trigger'; + return ( { + if (triggerOptions.readonly === true) { + return; + } + + triggerOptions.onTriggerUpdate({ + ...trigger, + name: newName, + }); + }} HeaderIcon={} - headerTitle="Manual Trigger" + headerTitle={headerTitle} headerType="Trigger · Manual" >