Show app name on workflow logic function nodes (#21675)
Workflow logic-function nodes from installed integration apps now show the owning app's name as the node label (e.g. "People Data Labs") instead of the generic "Action". The descriptive title below it (e.g. "Enrich Person") is unchanged. This only applies to installed integration apps — plain custom-code functions and standard functions keep showing "Action". A new `useWorkflowNodeLabel` hook resolves the logic function's `applicationId` and returns the app name for integration apps, falling back to the node type otherwise. It's used by the editable, read-only, and run node renderers. ## Before <img width="572" height="766" alt="CleanShot 2026-06-16 at 16 26 42@2x" src="https://github.com/user-attachments/assets/4ca9cf2f-5469-437d-a4dc-084057730589" /> ## After <img width="460" height="706" alt="CleanShot 2026-06-16 at 16 25 46@2x" src="https://github.com/user-attachments/assets/504ed2ea-49a6-49c6-830e-2fdd214b8c61" /> <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21675?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
+5
-2
@@ -17,13 +17,14 @@ import { WorkflowNodeRightPart } from '@/workflow/workflow-diagram/workflow-node
|
||||
import { WorkflowNodeTitle } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle';
|
||||
import { WORKFLOW_DIAGRAM_NODE_DEFAULT_SOURCE_HANDLE_ID } from '@/workflow/workflow-diagram/workflow-nodes/constants/WorkflowDiagramNodeDefaultSourceHandleId';
|
||||
import { useConnectionState } from '@/workflow/workflow-diagram/workflow-nodes/hooks/useConnectionState';
|
||||
import { useWorkflowNodeLabel } from '@/workflow/workflow-diagram/workflow-nodes/hooks/useWorkflowNodeLabel';
|
||||
import { isNodeTitleHighlighted } from '@/workflow/workflow-diagram/workflow-nodes/utils/isNodeTitleHighlighted';
|
||||
import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { Position } from '@xyflow/react';
|
||||
import { useState } from 'react';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledAddStepButtonContainer = styled.div<{
|
||||
shouldDisplay: boolean;
|
||||
@@ -75,6 +76,8 @@ export const WorkflowDiagramStepNodeEditableContent = ({
|
||||
|
||||
const isNodeConnectable = isConnectable({ nodeId: id });
|
||||
|
||||
const nodeLabel = useWorkflowNodeLabel(data);
|
||||
|
||||
const handleAddStepButtonContainerClick = (
|
||||
event: React.MouseEvent<HTMLDivElement>,
|
||||
) => {
|
||||
@@ -110,7 +113,7 @@ export const WorkflowDiagramStepNodeEditableContent = ({
|
||||
<WorkflowNodeRightPart>
|
||||
<WorkflowNodeLabelWithCounterPart>
|
||||
<WorkflowNodeLabel selected={selected}>
|
||||
{capitalize(data.nodeType)}
|
||||
{nodeLabel}
|
||||
</WorkflowNodeLabel>
|
||||
</WorkflowNodeLabelWithCounterPart>
|
||||
|
||||
|
||||
+5
-2
@@ -20,10 +20,11 @@ import { WorkflowNodeLabelWithCounterPart } from '@/workflow/workflow-diagram/wo
|
||||
import { WorkflowNodeRightPart } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeRightPart';
|
||||
import { WorkflowNodeTitle } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle';
|
||||
import { WORKFLOW_DIAGRAM_NODE_DEFAULT_SOURCE_HANDLE_ID } from '@/workflow/workflow-diagram/workflow-nodes/constants/WorkflowDiagramNodeDefaultSourceHandleId';
|
||||
import { useWorkflowNodeLabel } from '@/workflow/workflow-diagram/workflow-nodes/hooks/useWorkflowNodeLabel';
|
||||
import { isNodeTitleHighlighted } from '@/workflow/workflow-diagram/workflow-nodes/utils/isNodeTitleHighlighted';
|
||||
import { Position } from '@xyflow/react';
|
||||
import { useContext } from 'react';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui-deprecated/display';
|
||||
|
||||
export const WorkflowDiagramStepNodeReadonly = ({
|
||||
@@ -85,6 +86,8 @@ export const WorkflowDiagramStepNodeReadonly = ({
|
||||
actionType: data.nodeType === 'action' ? data.actionType : undefined,
|
||||
});
|
||||
|
||||
const nodeLabel = useWorkflowNodeLabel(data);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WorkflowNodeContainer
|
||||
@@ -100,7 +103,7 @@ export const WorkflowDiagramStepNodeReadonly = ({
|
||||
<WorkflowNodeRightPart>
|
||||
<WorkflowNodeLabelWithCounterPart>
|
||||
<WorkflowNodeLabel selected={selected}>
|
||||
{capitalize(data.nodeType)}
|
||||
{nodeLabel}
|
||||
</WorkflowNodeLabel>
|
||||
</WorkflowNodeLabelWithCounterPart>
|
||||
|
||||
|
||||
+5
-2
@@ -22,11 +22,12 @@ import { WorkflowNodeLabel } from '@/workflow/workflow-diagram/workflow-nodes/co
|
||||
import { WorkflowNodeRightPart } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeRightPart';
|
||||
import { WorkflowNodeTitle } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle';
|
||||
import { WORKFLOW_DIAGRAM_NODE_DEFAULT_SOURCE_HANDLE_ID } from '@/workflow/workflow-diagram/workflow-nodes/constants/WorkflowDiagramNodeDefaultSourceHandleId';
|
||||
import { useWorkflowNodeLabel } from '@/workflow/workflow-diagram/workflow-nodes/hooks/useWorkflowNodeLabel';
|
||||
import { getNodeIterationCount } from '@/workflow/workflow-diagram/workflow-nodes/utils/getNodeIterationCount';
|
||||
import { styled } from '@linaria/react';
|
||||
import { Position } from '@xyflow/react';
|
||||
import { useContext } from 'react';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { StepStatus } from 'twenty-shared/workflow';
|
||||
import { IconCheck, IconX, useIcons } from 'twenty-ui-deprecated/display';
|
||||
import { Loader } from 'twenty-ui-deprecated/feedback';
|
||||
@@ -116,6 +117,8 @@ export const WorkflowRunDiagramStepNode = ({
|
||||
? getNodeIterationCount({ stepInfo })
|
||||
: 0;
|
||||
|
||||
const nodeLabel = useWorkflowNodeLabel(data);
|
||||
|
||||
const handleClick = () => {
|
||||
if (!isDefined(workflowVisualizerWorkflowId)) {
|
||||
throw new Error('Workflow ID must be defined');
|
||||
@@ -153,7 +156,7 @@ export const WorkflowRunDiagramStepNode = ({
|
||||
<WorkflowNodeRightPart>
|
||||
<StyledNodeLabelWithCounterPart>
|
||||
<WorkflowNodeLabel runStatus={data.runStatus} selected={selected}>
|
||||
{capitalize(data.nodeType)}
|
||||
{nodeLabel}
|
||||
</WorkflowNodeLabel>
|
||||
|
||||
<StyledRightPartContainer>
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { useApplicationChipData } from '@/applications/hooks/useApplicationChipData';
|
||||
import { useIsThirdPartyApplication } from '@/applications/hooks/useIsThirdPartyApplication';
|
||||
import { logicFunctionsSelector } from '@/logic-functions/states/logicFunctionsSelector';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { type WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useWorkflowNodeLabel = (
|
||||
data: WorkflowDiagramStepNodeData,
|
||||
): string => {
|
||||
const logicFunctions = useAtomStateValue(logicFunctionsSelector);
|
||||
|
||||
const logicFunctionId =
|
||||
data.nodeType === 'action' ? data.logicFunctionId : undefined;
|
||||
|
||||
const applicationId = isDefined(logicFunctionId)
|
||||
? logicFunctions.find(
|
||||
(logicFunction) => logicFunction.id === logicFunctionId,
|
||||
)?.applicationId
|
||||
: undefined;
|
||||
|
||||
const isThirdPartyApplication = useIsThirdPartyApplication(applicationId);
|
||||
|
||||
const { applicationChipData } = useApplicationChipData({ applicationId });
|
||||
|
||||
if (isThirdPartyApplication) {
|
||||
return applicationChipData.name;
|
||||
}
|
||||
|
||||
return capitalize(data.nodeType);
|
||||
};
|
||||
Reference in New Issue
Block a user