Show app logo on workflow logic function nodes (#21482)

Workflow `LOGIC_FUNCTION` action nodes (functions provided by an
installed app) previously rendered a generic ƒ icon in the diagram. They
now display the owning app's logo instead.

The node's logic function id is resolved to its `applicationId` and
rendered via the existing `AppChip`. When no app can be resolved (e.g.
the logic function isn't loaded yet, or has no application), it falls
back to the original ƒ icon, so nodes never look broken. Inline `CODE`
actions are unchanged.

## Before
<img width="692" height="670" alt="CleanShot 2026-06-12 at 15 27 22@2x"
src="https://github.com/user-attachments/assets/4d7c17ce-dbe3-45e6-9d44-41e363b2e4a5"
/>

## After

<img width="592" height="628" alt="CleanShot 2026-06-12 at 15 26 22@2x"
src="https://github.com/user-attachments/assets/d55c308f-3cde-4153-8d5d-ec948bebc823"
/>


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21482?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:
Raphaël Bosi
2026-06-12 15:36:30 +02:00
committed by GitHub
parent c4453923f0
commit bd4161a905
4 changed files with 50 additions and 0 deletions

View File

@@ -65,6 +65,7 @@ export type WorkflowDiagramStepNodeData =
nodeType: 'action';
actionType: WorkflowActionType;
name: string;
logicFunctionId?: string;
runStatus?: WorkflowRunStepStatus;
hasNextStepIds: boolean;
stepId: string;

View File

@@ -47,6 +47,9 @@ export const generateNodesAndEdgesForDefaultNode = ({
nodeType: 'action',
actionType: step.type,
name: step.name,
...(step.type === 'LOGIC_FUNCTION'
? { logicFunctionId: step.settings.input.logicFunctionId }
: {}),
hasNextStepIds:
isDefined(step.nextStepIds) && step.nextStepIds.length > 0,
stepId: step.id,

View File

@@ -1,5 +1,6 @@
import { type WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
import { WorkflowDiagramStepNodeLogicFunctionIcon } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeLogicFunctionIcon';
import { assertUnreachable } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui-deprecated/display';
import { ThemeContext } from 'twenty-ui-deprecated/theme-constants';
@@ -43,6 +44,13 @@ export const WorkflowDiagramStepNodeIcon = ({
/>
);
}
case 'LOGIC_FUNCTION': {
return (
<WorkflowDiagramStepNodeLogicFunctionIcon
logicFunctionId={data.logicFunctionId}
/>
);
}
case 'FORM': {
return <Icon size={theme.icon.size.md} color={theme.color.orange} />;
}

View File

@@ -0,0 +1,38 @@
import { AppChip } from '@/applications/components/AppChip';
import { logicFunctionsSelector } from '@/logic-functions/states/logicFunctionsSelector';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui-deprecated/display';
import { ThemeContext } from 'twenty-ui-deprecated/theme-constants';
export const WorkflowDiagramStepNodeLogicFunctionIcon = ({
logicFunctionId,
}: {
logicFunctionId?: string;
}) => {
const { theme } = useContext(ThemeContext);
const { getIcon } = useIcons();
const logicFunctions = useAtomStateValue(logicFunctionsSelector);
const applicationId = isDefined(logicFunctionId)
? logicFunctions.find(
(logicFunction) => logicFunction.id === logicFunctionId,
)?.applicationId
: undefined;
if (isDefined(applicationId)) {
return <AppChip applicationId={applicationId} size="md" chipOnly />;
}
const FallbackIcon = getIcon(getActionIcon('LOGIC_FUNCTION'));
return (
<FallbackIcon
size={theme.icon.size.md}
color={theme.font.color.tertiary}
stroke={theme.icon.stroke.sm}
/>
);
};