From 8369168abebce1a4ccbb15fcedf1021eb218040c Mon Sep 17 00:00:00 2001 From: DipakHalkude <128908555+DipakHalkude@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:50:49 +0530 Subject: [PATCH] feat: display specific action types in workflow side panel tooltips (#15013) ## What I've Implemented I've added tooltips that show the original action type when you hover over workflow step icons in the side panel. Now even if you rename an action to something custom, you can still see what type of action it actually is by hovering over the icon. ## The Problem This Solves Before this change, once you renamed a workflow action (like changing "Create Record" to "Add New Customer"), there was no way to tell what the original action type was. This made it really confusing when collaborating with others or when coming back to your own workflows after some time - you couldn't tell if an action was a "Create Record" or "Update Record" or something else. ## What Changed Updated action type labels: Instead of showing just "Action" for all record operations, the system now shows specific types like "Create Record", "Update Record", "Delete Record", etc. Added hover tooltips: When you hover over the action icons in the workflow side panel, a tooltip appears showing the original action type, even if you've renamed the step title. ## Before vs After Before: All actions showed "Action" in the side panel, making it impossible to distinguish between different types after renaming. After: Each action shows its specific type in tooltips, so you always know what you're working with. This should make workflow management much clearer, especially when multiple people are collaborating on the same workflow! Resolves #14878 ## Demo Video See it in action: https://www.loom.com/share/c0e0ec24e4524d0685b841e9ceb011d0?t=65&sid=dc05e58a-8869-4969-aa67-9772242fe697 --------- Co-authored-by: Thomas Trompette --- .../components/SidePanelHeader.tsx | 15 +++++- .../components/WorkflowActionMenuItems.tsx | 8 ++- .../components/WorkflowEditActionAiAgent.tsx | 4 +- .../WorkflowEditActionServerlessFunction.tsx | 4 +- .../WorkflowEditActionCreateRecord.tsx | 4 +- .../WorkflowEditActionDeleteRecord.tsx | 4 +- .../WorkflowEditActionSendEmail.tsx | 4 +- .../WorkflowEditActionUpdateRecord.tsx | 4 +- .../WorkflowEditActionUpsertRecord.tsx | 4 +- .../workflow-actions/constants/AiActions.ts | 11 ++-- .../workflow-actions/constants/CoreActions.ts | 23 ++------ .../workflow-actions/constants/FlowActions.ts | 23 ++------ .../constants/HumanInputActions.ts | 11 ++-- .../constants/RecordActions.ts | 37 ++++--------- .../constants/actions/AiAgentAction.ts | 11 ++++ .../constants/actions/CodeAction.ts | 11 ++++ .../constants/actions/CreateRecordAction.ts | 11 ++++ .../constants/actions/DelayAction.ts | 11 ++++ .../constants/actions/DeleteRecordAction.ts | 11 ++++ .../constants/actions/FilterAction.ts | 11 ++++ .../constants/actions/FindRecordsAction.ts | 11 ++++ .../constants/actions/FormAction.ts | 11 ++++ .../constants/actions/HttpRequestAction.ts | 11 ++++ .../constants/actions/IteratorAction.ts | 11 ++++ .../constants/actions/SendEmailAction.ts | 11 ++++ .../constants/actions/UpdateRecordAction.ts | 11 ++++ .../constants/actions/UpsertRecordAction.ts | 11 ++++ .../components/WorkflowEditActionDelay.tsx | 4 +- .../components/WorkflowEditActionFilter.tsx | 4 +- .../WorkflowEditActionFindRecords.tsx | 4 +- .../WorkflowEditActionFormBuilder.tsx | 15 +++--- .../WorkflowEditActionFormFiller.tsx | 18 ++++--- .../WorkflowEditActionHttpRequest.tsx | 4 +- .../components/WorkflowEditActionIterator.tsx | 4 +- .../getActionHeaderTypeOrThrow.test.ts | 29 +++++----- .../utils/getActionHeaderTypeOrThrow.ts | 53 +++++++++---------- .../WorkflowEditTriggerCronForm.tsx | 1 + .../WorkflowEditTriggerDatabaseEventForm.tsx | 1 + .../components/WorkflowEditTriggerManual.tsx | 1 + .../WorkflowEditTriggerWebhookForm.tsx | 1 + .../constants/DatabaseTriggerTypes.ts | 34 ++++-------- .../constants/OtherTriggerTypes.ts | 21 ++------ .../constants/triggers/CronTrigger.ts | 11 ++++ .../constants/triggers/ManualTrigger.ts | 11 ++++ .../triggers/RecordIsCreatedTrigger.ts | 14 +++++ .../triggers/RecordIsDeletedTrigger.ts | 14 +++++ .../triggers/RecordIsUpdatedTrigger.ts | 14 +++++ .../triggers/RecordUpsertedTrigger.ts | 14 +++++ .../constants/triggers/WebhookTrigger.ts | 11 ++++ .../__tests__/getTriggerHeaderType.test.ts | 8 +-- .../utils/getTriggerHeaderType.ts | 2 +- 51 files changed, 392 insertions(+), 200 deletions(-) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/AiAgentAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/CodeAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/CreateRecordAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/DelayAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/DeleteRecordAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/FilterAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/FindRecordsAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/FormAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/HttpRequestAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/IteratorAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/SendEmailAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/UpdateRecordAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/actions/UpsertRecordAction.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/CronTrigger.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/ManualTrigger.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsCreatedTrigger.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsDeletedTrigger.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsUpdatedTrigger.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordUpsertedTrigger.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/WebhookTrigger.ts diff --git a/packages/twenty-front/src/modules/command-menu/components/SidePanelHeader.tsx b/packages/twenty-front/src/modules/command-menu/components/SidePanelHeader.tsx index 5772f2f5c2d..b8c347c2855 100644 --- a/packages/twenty-front/src/modules/command-menu/components/SidePanelHeader.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/SidePanelHeader.tsx @@ -3,7 +3,7 @@ import { TitleInput } from '@/ui/input/components/TitleInput'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useState } from 'react'; -import { type IconComponent } from 'twenty-ui/display'; +import { AppTooltip, type IconComponent } from 'twenty-ui/display'; const StyledHeader = styled.div` background-color: ${({ theme }) => theme.background.secondary}; @@ -52,6 +52,7 @@ type SidePanelHeaderProps = { iconColor: string; initialTitle: string; headerType: string; + iconTooltip?: string; } & ( | { disabled: true; @@ -70,6 +71,7 @@ export const SidePanelHeader = ({ headerType, disabled, onTitleChange, + iconTooltip, }: SidePanelHeaderProps) => { const theme = useTheme(); @@ -89,15 +91,24 @@ export const SidePanelHeader = ({ }); }; + const tooltipId = `side-panel-icon-tooltip-${headerType.replace(/\s+/g, '-')}`; + return ( - + + {iconTooltip && ( + + )} )} - text={action.label} + text={action.defaultLabel} onClick={() => onClick(action.type)} /> ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx index 8adc346106f..3ab3eff9665 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx @@ -6,6 +6,7 @@ import { Select } from '@/ui/input/components/Select'; import { type WorkflowAiAgentAction } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter'; +import { AI_AGENT_ACTION } from '@/workflow/workflow-steps/workflow-actions/constants/actions/AiAgentAction'; import { useWorkflowActionHeader } from '@/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { type AiAgentOutputSchema } from '@/workflow/workflow-variables/types/AiAgentOutputSchema'; @@ -44,7 +45,7 @@ export const WorkflowEditActionAiAgent = ({ const { headerTitle, headerIcon, headerIconColor, headerType } = useWorkflowActionHeader({ action, - defaultTitle: 'AI Agent', + defaultTitle: AI_AGENT_ACTION.defaultLabel, }); const { handleOutputSchemaChange, outputFields } = useAiAgentOutputSchema( @@ -111,6 +112,7 @@ export const WorkflowEditActionAiAgent = ({ initialTitle={headerTitle} headerType={headerType} disabled={actionOptions.readonly} + iconTooltip={AI_AGENT_ACTION.defaultLabel} />
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx index d37bc841989..a8f7d4b24dd 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx @@ -40,6 +40,7 @@ import { useLingui } from '@lingui/react/macro'; import { SOURCE_FOLDER_NAME } from '@/serverless-functions/constants/SourceFolderName'; import { computeNewSources } from '@/serverless-functions/utils/computeNewSources'; import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter'; +import { CODE_ACTION } from '@/workflow/workflow-steps/workflow-actions/constants/actions/CodeAction'; import { type Monaco } from '@monaco-editor/react'; import { type editor } from 'monaco-editor'; import { AutoTypings } from 'monaco-editor-auto-typings'; @@ -326,7 +327,7 @@ export const WorkflowEditActionServerlessFunction = ({ const headerTitle = isDefined(action.name) ? action.name - : 'Code - Serverless Function'; + : CODE_ACTION.defaultLabel; const headerIcon = getActionIcon(action.type); const headerIconColor = useActionIconColorOrThrow(action.type); const headerType = useActionHeaderTypeOrThrow(action.type); @@ -431,6 +432,7 @@ export const WorkflowEditActionServerlessFunction = ({ initialTitle={headerTitle} headerType={headerType} disabled={actionOptions.readonly} + iconTooltip={CODE_ACTION.defaultLabel} /> {activeTabId === WorkflowServerlessFunctionTabId.CODE && ( diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx index 2d8e8eadac3..b750869bd54 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx @@ -10,6 +10,7 @@ import { useViewOrDefaultViewFromPrefetchedViews } from '@/views/hooks/useViewOr import { type WorkflowCreateRecordAction } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter'; +import { CREATE_RECORD_ACTION } from '@/workflow/workflow-steps/workflow-actions/constants/actions/CreateRecordAction'; import { useWorkflowActionHeader } from '@/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader'; import { shouldDisplayFormField } from '@/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; @@ -198,7 +199,7 @@ export const WorkflowEditActionCreateRecord = ({ const { headerTitle, headerIcon, headerIconColor, headerType } = useWorkflowActionHeader({ action, - defaultTitle: 'Create Record', + defaultTitle: CREATE_RECORD_ACTION.defaultLabel, }); return ( @@ -219,6 +220,7 @@ export const WorkflowEditActionCreateRecord = ({ initialTitle={headerTitle} headerType={headerType} disabled={isFormDisabled} + iconTooltip={CREATE_RECORD_ACTION.defaultLabel} /> (action.settings.input); - const headerTitle = isDefined(action.name) ? action.name : `Form`; - const headerIcon = getActionIcon(action.type); - const headerIconColor = useActionIconColorOrThrow(action.type); - const headerType = useActionHeaderTypeOrThrow(action.type); + const { headerTitle, headerIcon, headerIconColor, headerType } = + useWorkflowActionHeader({ + action, + defaultTitle: FORM_ACTION.defaultLabel, + }); const [selectedField, setSelectedField] = useState(null); const [hoveredField, setHoveredField] = useState(null); @@ -239,6 +239,7 @@ export const WorkflowEditActionFormBuilder = ({ initialTitle={headerTitle} headerType={headerType} disabled={actionOptions.readonly} + iconTooltip={FORM_ACTION.defaultLabel} /> {formData.length === 0 && ( diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx index 590061e221c..a55ec8ae94e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx @@ -10,11 +10,11 @@ import { type WorkflowFormAction } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { useUpdateWorkflowRunStep } from '@/workflow/workflow-steps/hooks/useUpdateWorkflowRunStep'; import { WorkflowFormFieldInput } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowFormFieldInput'; +import { FORM_ACTION } from '@/workflow/workflow-steps/workflow-actions/constants/actions/FormAction'; import { useSubmitFormStep } from '@/workflow/workflow-steps/workflow-actions/form-action/hooks/useSubmitFormStep'; import { type WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField'; import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings'; -import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; -import { useTheme } from '@emotion/react'; +import { useWorkflowActionHeader } from '@/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader'; import { useEffect, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; @@ -33,7 +33,6 @@ export const WorkflowEditActionFormFiller = ({ action, actionOptions, }: WorkflowEditActionFormFillerProps) => { - const theme = useTheme(); const { getIcon } = useIcons(); const { submitFormStep } = useSubmitFormStep(); const [formData, setFormData] = useState(action.settings.input); @@ -43,8 +42,12 @@ export const WorkflowEditActionFormFiller = ({ const [error, setError] = useState(undefined); const canSubmit = !actionOptions.readonly && !isDefined(error); - const headerTitle = isDefined(action.name) ? action.name : `Form`; - const headerIcon = getActionIcon(action.type); + + const { headerTitle, headerIcon, headerIconColor, headerType } = + useWorkflowActionHeader({ + action, + defaultTitle: FORM_ACTION.defaultLabel, + }); const onFieldUpdate = ({ fieldId, @@ -108,10 +111,11 @@ export const WorkflowEditActionFormFiller = ({ <> {formData.map((field) => { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/WorkflowEditActionHttpRequest.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/WorkflowEditActionHttpRequest.tsx index 0e10d59a2e4..be2b8fbd698 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/WorkflowEditActionHttpRequest.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/WorkflowEditActionHttpRequest.tsx @@ -4,6 +4,7 @@ import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/c import { Select } from '@/ui/input/components/Select'; import { type WorkflowHttpRequestAction } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; +import { HTTP_REQUEST_ACTION } from '@/workflow/workflow-steps/workflow-actions/constants/actions/HttpRequestAction'; import { useWorkflowActionHeader } from '@/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader'; import { CmdEnterActionButton } from '@/action-menu/components/CmdEnterActionButton'; @@ -98,7 +99,7 @@ export const WorkflowEditActionHttpRequest = ({ const { headerTitle, headerIcon, headerIconColor, headerType } = useWorkflowActionHeader({ action, - defaultTitle: 'HTTP Request', + defaultTitle: HTTP_REQUEST_ACTION.defaultLabel, }); const { formData, handleFieldChange, saveAction } = useHttpRequestForm({ @@ -152,6 +153,7 @@ export const WorkflowEditActionHttpRequest = ({ initialTitle={headerTitle} headerType={headerType} disabled={actionOptions.readonly} + iconTooltip={HTTP_REQUEST_ACTION.defaultLabel} /> {activeTabId === WorkflowHttpRequestTabId.CONFIGURATION && ( diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/iterator-action/components/WorkflowEditActionIterator.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/iterator-action/components/WorkflowEditActionIterator.tsx index 6cec7230605..dd0864ebdf5 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/iterator-action/components/WorkflowEditActionIterator.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/iterator-action/components/WorkflowEditActionIterator.tsx @@ -5,6 +5,7 @@ import { type WorkflowIteratorAction } from '@/workflow/types/Workflow'; import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter'; +import { ITERATOR_ACTION } from '@/workflow/workflow-steps/workflow-actions/constants/actions/IteratorAction'; import { useWorkflowActionHeader } from '@/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { useLingui } from '@lingui/react/macro'; @@ -46,7 +47,7 @@ export const WorkflowEditActionIterator = ({ const { headerTitle, headerIcon, headerIconColor, headerType, getIcon } = useWorkflowActionHeader({ action, - defaultTitle: 'Iterator', + defaultTitle: ITERATOR_ACTION.defaultLabel, }); const { t } = useLingui(); @@ -119,6 +120,7 @@ export const WorkflowEditActionIterator = ({ initialTitle={headerTitle} headerType={headerType} disabled={actionOptions.readonly} + iconTooltip={ITERATOR_ACTION.defaultLabel} /> { - it('should return "Code" for CODE action type', () => { - expect(getActionHeaderTypeOrThrow('CODE').message).toBe('Code'); + it('should return "Core" for CODE action type', () => { + expect(getActionHeaderTypeOrThrow('CODE').message).toBe('Core'); }); - it('should return "Action" for record-related action types', () => { + it('should return "Record" for record-related action types', () => { const recordActionTypes = [ 'CREATE_RECORD', 'UPDATE_RECORD', @@ -14,28 +14,23 @@ describe('getActionHeaderTypeOrThrow', () => { ] as const; recordActionTypes.forEach((type) => { - expect(getActionHeaderTypeOrThrow(type).message).toBe('Action'); + expect(getActionHeaderTypeOrThrow(type).message).toBe('Record'); }); }); - it('should return "Action" for FORM action type', () => { - expect(getActionHeaderTypeOrThrow('FORM').message).toBe('Action'); + it('should return "Human Input" for FORM action type', () => { + expect(getActionHeaderTypeOrThrow('FORM').message).toBe('Human Input'); }); - it('should return "Action" for SEND_EMAIL action type', () => { - expect(getActionHeaderTypeOrThrow('SEND_EMAIL').message).toBe('Action'); + it('should return "Core" for SEND_EMAIL action type', () => { + expect(getActionHeaderTypeOrThrow('SEND_EMAIL').message).toBe('Core'); }); - it('should return "HTTP Request" for HTTP_REQUEST action type', () => { - expect(getActionHeaderTypeOrThrow('HTTP_REQUEST').message).toBe( - 'HTTP Request', - ); + it('should return "Core" for HTTP_REQUEST action type', () => { + expect(getActionHeaderTypeOrThrow('HTTP_REQUEST').message).toBe('Core'); }); - it('should throw error for unknown action type', () => { - // @ts-expect-error Testing invalid action type - expect(() => getActionHeaderTypeOrThrow('UNKNOWN_ACTION')).toThrow( - 'Unsupported action type: UNKNOWN_ACTION', - ); + it('should return "AI" for AI_AGENT action type', () => { + expect(getActionHeaderTypeOrThrow('AI_AGENT').message).toBe('AI'); }); }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionHeaderTypeOrThrow.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionHeaderTypeOrThrow.ts index 91ea50a3c0c..28b9d38b19b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionHeaderTypeOrThrow.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionHeaderTypeOrThrow.ts @@ -1,34 +1,31 @@ import { type WorkflowActionType } from '@/workflow/types/Workflow'; +import { AI_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/AiActions'; +import { CORE_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/CoreActions'; +import { FLOW_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/FlowActions'; +import { HUMAN_INPUT_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/HumanInputActions'; +import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions'; import { msg } from '@lingui/core/macro'; -import { assertUnreachable } from 'twenty-shared/utils'; export const getActionHeaderTypeOrThrow = (actionType: WorkflowActionType) => { - switch (actionType) { - case 'CODE': - return msg`Code`; - case 'CREATE_RECORD': - case 'UPDATE_RECORD': - case 'DELETE_RECORD': - case 'UPSERT_RECORD': - case 'FIND_RECORDS': - case 'FORM': - case 'SEND_EMAIL': - case 'DELAY': - return msg`Action`; - case 'HTTP_REQUEST': - return msg`HTTP Request`; - case 'AI_AGENT': - return msg`AI Agent`; - case 'FILTER': { - return msg`Filter`; - } - case 'ITERATOR': { - return msg`Iterator`; - } - case 'EMPTY': { - return msg`Add an Action`; - } - default: - assertUnreachable(actionType, `Unsupported action type: ${actionType}`); + if (FLOW_ACTIONS.some((action) => action.type === actionType)) { + return msg`Flow`; } + + if (CORE_ACTIONS.some((action) => action.type === actionType)) { + return msg`Core`; + } + + if (HUMAN_INPUT_ACTIONS.some((action) => action.type === actionType)) { + return msg`Human Input`; + } + + if (RECORD_ACTIONS.some((action) => action.type === actionType)) { + return msg`Record`; + } + + if (AI_ACTIONS.some((action) => action.type === actionType)) { + return msg`AI`; + } + + return msg`Action`; }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx index 03e43a71a2d..97a479251bb 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx @@ -82,6 +82,7 @@ export const WorkflowEditTriggerCronForm = ({ initialTitle={headerTitle} headerType={headerType} disabled={triggerOptions.readonly} + iconTooltip={getTriggerDefaultLabel(trigger)} /> = [ - { - defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED, - type: 'DATABASE_EVENT', - icon: 'IconPlaylistAdd', - event: 'created', - }, - { - defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_UPDATED, - type: 'DATABASE_EVENT', - icon: 'IconRefreshDot', - event: 'updated', - }, - { - defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_DELETED, - type: 'DATABASE_EVENT', - icon: 'IconTrash', - event: 'deleted', - }, - { - defaultLabel: DatabaseTriggerDefaultLabel.RECORD_UPSERTED, - type: 'DATABASE_EVENT', - icon: 'IconPencilPlus', - event: 'upserted', - }, + RECORD_IS_CREATED_TRIGGER, + RECORD_IS_UPDATED_TRIGGER, + RECORD_IS_DELETED_TRIGGER, + RECORD_UPSERTED_TRIGGER, ]; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/OtherTriggerTypes.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/OtherTriggerTypes.ts index 0e1e9ad3950..b3e50b39333 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/OtherTriggerTypes.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/OtherTriggerTypes.ts @@ -1,23 +1,10 @@ import { type WorkflowTriggerType } from '@/workflow/types/Workflow'; +import { CRON_TRIGGER } from '@/workflow/workflow-trigger/constants/triggers/CronTrigger'; +import { MANUAL_TRIGGER } from '@/workflow/workflow-trigger/constants/triggers/ManualTrigger'; +import { WEBHOOK_TRIGGER } from '@/workflow/workflow-trigger/constants/triggers/WebhookTrigger'; export const OTHER_TRIGGER_TYPES: Array<{ defaultLabel: string; type: WorkflowTriggerType; icon: string; -}> = [ - { - defaultLabel: 'Launch manually', - type: 'MANUAL', - icon: 'IconHandMove', - }, - { - defaultLabel: 'On a schedule', - type: 'CRON', - icon: 'IconClock', - }, - { - defaultLabel: 'Webhook', - type: 'WEBHOOK', - icon: 'IconWebhook', - }, -]; +}> = [MANUAL_TRIGGER, CRON_TRIGGER, WEBHOOK_TRIGGER]; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/CronTrigger.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/CronTrigger.ts new file mode 100644 index 00000000000..eec33f65f4d --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/CronTrigger.ts @@ -0,0 +1,11 @@ +import { type WorkflowTriggerType } from '@/workflow/types/Workflow'; + +export const CRON_TRIGGER: { + defaultLabel: string; + type: WorkflowTriggerType; + icon: string; +} = { + defaultLabel: 'On a schedule', + type: 'CRON', + icon: 'IconClock', +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/ManualTrigger.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/ManualTrigger.ts new file mode 100644 index 00000000000..75fd4db7880 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/ManualTrigger.ts @@ -0,0 +1,11 @@ +import { type WorkflowTriggerType } from '@/workflow/types/Workflow'; + +export const MANUAL_TRIGGER: { + defaultLabel: string; + type: WorkflowTriggerType; + icon: string; +} = { + defaultLabel: 'Launch manually', + type: 'MANUAL', + icon: 'IconHandMove', +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsCreatedTrigger.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsCreatedTrigger.ts new file mode 100644 index 00000000000..3680580620f --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsCreatedTrigger.ts @@ -0,0 +1,14 @@ +import { type WorkflowTriggerType } from '@/workflow/types/Workflow'; +import { DatabaseTriggerDefaultLabel } from '@/workflow/workflow-trigger/constants/DatabaseTriggerDefaultLabel'; + +export const RECORD_IS_CREATED_TRIGGER: { + defaultLabel: DatabaseTriggerDefaultLabel; + type: WorkflowTriggerType; + icon: string; + event: string; +} = { + defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED, + type: 'DATABASE_EVENT', + icon: 'IconPlaylistAdd', + event: 'created', +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsDeletedTrigger.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsDeletedTrigger.ts new file mode 100644 index 00000000000..d0696a76ef4 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsDeletedTrigger.ts @@ -0,0 +1,14 @@ +import { type WorkflowTriggerType } from '@/workflow/types/Workflow'; +import { DatabaseTriggerDefaultLabel } from '@/workflow/workflow-trigger/constants/DatabaseTriggerDefaultLabel'; + +export const RECORD_IS_DELETED_TRIGGER: { + defaultLabel: DatabaseTriggerDefaultLabel; + type: WorkflowTriggerType; + icon: string; + event: string; +} = { + defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_DELETED, + type: 'DATABASE_EVENT', + icon: 'IconTrash', + event: 'deleted', +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsUpdatedTrigger.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsUpdatedTrigger.ts new file mode 100644 index 00000000000..c2563675a52 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordIsUpdatedTrigger.ts @@ -0,0 +1,14 @@ +import { type WorkflowTriggerType } from '@/workflow/types/Workflow'; +import { DatabaseTriggerDefaultLabel } from '@/workflow/workflow-trigger/constants/DatabaseTriggerDefaultLabel'; + +export const RECORD_IS_UPDATED_TRIGGER: { + defaultLabel: DatabaseTriggerDefaultLabel; + type: WorkflowTriggerType; + icon: string; + event: string; +} = { + defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_UPDATED, + type: 'DATABASE_EVENT', + icon: 'IconRefreshDot', + event: 'updated', +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordUpsertedTrigger.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordUpsertedTrigger.ts new file mode 100644 index 00000000000..757f30b240f --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/RecordUpsertedTrigger.ts @@ -0,0 +1,14 @@ +import { type WorkflowTriggerType } from '@/workflow/types/Workflow'; +import { DatabaseTriggerDefaultLabel } from '@/workflow/workflow-trigger/constants/DatabaseTriggerDefaultLabel'; + +export const RECORD_UPSERTED_TRIGGER: { + defaultLabel: DatabaseTriggerDefaultLabel; + type: WorkflowTriggerType; + icon: string; + event: string; +} = { + defaultLabel: DatabaseTriggerDefaultLabel.RECORD_UPSERTED, + type: 'DATABASE_EVENT', + icon: 'IconPencilPlus', + event: 'upserted', +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/WebhookTrigger.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/WebhookTrigger.ts new file mode 100644 index 00000000000..82af499c782 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/constants/triggers/WebhookTrigger.ts @@ -0,0 +1,11 @@ +import { type WorkflowTriggerType } from '@/workflow/types/Workflow'; + +export const WEBHOOK_TRIGGER: { + defaultLabel: string; + type: WorkflowTriggerType; + icon: string; +} = { + defaultLabel: 'Webhook', + type: 'WEBHOOK', + icon: 'IconWebhook', +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/__tests__/getTriggerHeaderType.test.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/__tests__/getTriggerHeaderType.test.ts index ddeb7025078..25b3b8f778e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/__tests__/getTriggerHeaderType.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/__tests__/getTriggerHeaderType.test.ts @@ -115,7 +115,7 @@ describe('getTriggerHeaderType', () => { const result = getTriggerHeaderType(trigger); - expect(result).toBe('Trigger'); + expect(result).toBe('Trigger · Cron'); }); it('returns "Trigger" for cron trigger with HOURS schedule', () => { @@ -134,7 +134,7 @@ describe('getTriggerHeaderType', () => { const result = getTriggerHeaderType(trigger); - expect(result).toBe('Trigger'); + expect(result).toBe('Trigger · Cron'); }); it('returns "Trigger" for cron trigger with MINUTES schedule', () => { @@ -152,7 +152,7 @@ describe('getTriggerHeaderType', () => { const result = getTriggerHeaderType(trigger); - expect(result).toBe('Trigger'); + expect(result).toBe('Trigger · Cron'); }); it('returns "Trigger" for cron trigger with CUSTOM schedule', () => { @@ -168,7 +168,7 @@ describe('getTriggerHeaderType', () => { const result = getTriggerHeaderType(trigger); - expect(result).toBe('Trigger'); + expect(result).toBe('Trigger · Cron'); }); }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/getTriggerHeaderType.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/getTriggerHeaderType.ts index 02cec7b04c0..ff4d53267cc 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/getTriggerHeaderType.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/getTriggerHeaderType.ts @@ -5,7 +5,7 @@ import { assertUnreachable } from 'twenty-shared/utils'; export const getTriggerHeaderType = (trigger: WorkflowTrigger) => { switch (trigger.type) { case 'CRON': { - return 'Trigger'; + return 'Trigger · Cron'; } case 'WEBHOOK': { return 'Trigger · Webhook';