diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowMessage.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowMessage.tsx
deleted file mode 100644
index 56fbde23b0..0000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowMessage.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
-import { FormFieldInputInnerContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputInnerContainer';
-import { FormFieldInputRowContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputRowContainer';
-import styled from '@emotion/styled';
-
-const StyledMessageContainer = styled.div`
- padding-bottom: ${({ theme }) => theme.spacing(4)};
- padding-inline: ${({ theme }) => theme.spacing(7)};
- padding-top: ${({ theme }) => theme.spacing(2)};
-`;
-
-const StyledMessageContentContainer = styled.div`
- flex-direction: column;
- color: ${({ theme }) => theme.font.color.secondary};
- display: flex;
- gap: ${({ theme }) => theme.spacing(4)};
- width: 100%;
- padding: ${({ theme }) => theme.spacing(4)};
- line-height: normal;
-`;
-
-const StyledMessageTitle = styled.div`
- color: ${({ theme }) => theme.font.color.primary};
- font-weight: ${({ theme }) => theme.font.weight.medium};
- line-height: 13px;
-`;
-
-const StyledMessageDescription = styled.div`
- color: ${({ theme }) => theme.font.color.secondary};
- font-weight: ${({ theme }) => theme.font.weight.regular};
-`;
-
-const StyledFieldContainer = styled.div`
- align-items: center;
- background: transparent;
- border: none;
- display: flex;
- font-family: inherit;
- width: 100%;
-`;
-
-export const WorkflowMessage = ({
- title,
- description,
-}: {
- title: string;
- description: string;
-}) => {
- return (
-
-
-
-
-
-
-
- {title}
-
-
- {description}
-
-
-
-
-
-
-
- );
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowMessage.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowMessage.stories.tsx
deleted file mode 100644
index 8d2e5c2404..0000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowMessage.stories.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { WorkflowMessage } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowMessage';
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { expect, within } from 'storybook/test';
-
-const meta: Meta = {
- title: 'Modules/Workflow/Actions/Form/WorkflowMessage',
- component: WorkflowMessage,
- parameters: {
- layout: 'centered',
- },
- decorators: [],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- args: {},
- play: async ({ canvasElement }) => {
- const canvas = within(canvasElement);
- const messageTitleContainer = await canvas.findByTestId(
- 'workflow-message-title',
- );
- const messageDescriptionContainer = await canvas.findByTestId(
- 'workflow-message-description',
- );
-
- expect(messageTitleContainer).toBeVisible();
- expect(messageDescriptionContainer).toBeVisible();
- },
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormBuilder.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormBuilder.tsx
index c03215288d..f690785456 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormBuilder.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormBuilder.tsx
@@ -11,7 +11,6 @@ import {
} from '@/workflow/types/Workflow';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
-import { WorkflowMessage } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowMessage';
import { WorkflowEditActionFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFieldSettings';
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';
@@ -25,6 +24,7 @@ import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
Callout,
+ IconAlertTriangle,
IconChevronDown,
IconGripVertical,
IconPlus,
@@ -51,7 +51,8 @@ type FormData = WorkflowFormActionField[];
const StyledWorkflowStepBody = styled(WorkflowStepBody)`
display: block;
- padding-inline: ${({ theme }) => theme.spacing(2)};
+ padding-left: ${({ theme }) => theme.spacing(2)};
+ padding-right: ${({ theme }) => theme.spacing(2)};
`;
const StyledFormFieldContainer = styled.div`
@@ -98,7 +99,8 @@ const StyledFieldContainer = styled.div<{
border: none;
display: flex;
font-family: inherit;
- padding-inline: ${({ theme }) => theme.spacing(2)};
+ padding-left: ${({ theme }) => theme.spacing(2)};
+ padding-right: ${({ theme }) => theme.spacing(2)};
width: 100%;
cursor: ${({ readonly }) => (readonly ? 'default' : 'pointer')};
@@ -118,7 +120,8 @@ const StyledPlaceholder = styled(FormFieldPlaceholder)`
`;
const StyledAddFieldButtonContainer = styled.div`
- padding-inline: ${({ theme }) => theme.spacing(7)};
+ padding-left: ${({ theme }) => theme.spacing(7)};
+ padding-right: ${({ theme }) => theme.spacing(7)};
padding-top: ${({ theme }) => theme.spacing(2)};
`;
@@ -132,6 +135,17 @@ const StyledAddFieldButtonContentContainer = styled.div`
width: 100%;
`;
+const StyledCalloutContainer = styled.div`
+ padding-bottom: ${({ theme }) => theme.spacing(2)};
+ padding-left: ${({ theme }) => theme.spacing(7)};
+ padding-right: ${({ theme }) => theme.spacing(7)};
+ padding-top: ${({ theme }) => theme.spacing(2)};
+`;
+
+const StyledNotClosableCalloutContainer = styled(StyledCalloutContainer)`
+ padding-bottom: ${({ theme }) => theme.spacing(4)};
+`;
+
export const WorkflowEditActionFormBuilder = ({
triggerType,
action,
@@ -221,27 +235,35 @@ export const WorkflowEditActionFormBuilder = ({
<>
{triggerType && triggerType !== 'MANUAL' && isCalloutVisible && (
- setIsCalloutVisible(false)}
- action={{
- label: t`Learn more`,
- onClick: () =>
- window.open(
- 'https://docs.twenty.com/user-guide/workflows/capabilities/workflow-actions#form',
- '_blank',
- 'noopener,noreferrer',
- ),
- }}
- />
+
+ setIsCalloutVisible(false)}
+ action={{
+ label: t`Learn more`,
+ onClick: () =>
+ window.open(
+ 'https://docs.twenty.com/user-guide/workflows/capabilities/workflow-actions#form',
+ '_blank',
+ 'noopener,noreferrer',
+ ),
+ }}
+ />
+
)}
{formData.length === 0 && (
-
+
+
+
)}
{
const canvas = within(canvasElement);
- const messageContainer = await canvas.findByTestId(
- 'workflow-message-title',
- );
+ const messageContainer = await canvas.findByText('Add inputs to your form');
expect(messageContainer).toBeVisible();
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/logic-function-action/components/WorkflowEditActionLogicFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/logic-function-action/components/WorkflowEditActionLogicFunction.tsx
index 17c2183b83..5a2a268903 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/logic-function-action/components/WorkflowEditActionLogicFunction.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/logic-function-action/components/WorkflowEditActionLogicFunction.tsx
@@ -6,13 +6,13 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
import { WorkflowEditActionCodeFields } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields';
import { setNestedValue } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/setNestedValue';
-import { WorkflowMessage } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowMessage';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { isObject } from '@sniptt/guards';
import { useMemo } from 'react';
import { isDefined } from 'twenty-shared/utils';
+import { Callout } from 'twenty-ui/display';
import { useDebouncedCallback } from 'use-debounce';
const StyledContainer = styled.div`
@@ -118,7 +118,8 @@ export const WorkflowEditActionLogicFunction = ({
fullWidth
/>
) : (
-
diff --git a/packages/twenty-ui/src/display/callout/Callout.tsx b/packages/twenty-ui/src/display/callout/Callout.tsx
index 8083642491..50bac0e9f8 100644
--- a/packages/twenty-ui/src/display/callout/Callout.tsx
+++ b/packages/twenty-ui/src/display/callout/Callout.tsx
@@ -1,6 +1,8 @@
import styled from '@emotion/styled';
import { IconHelp, IconX } from '@ui/display/icon/components/TablerIcons';
-import { IconButton, LightButton } from '@ui/input';
+import { type IconComponent } from '@ui/display/icon/types/IconComponent';
+import { LightButton, LightIconButton } from '@ui/input';
+import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
export type CalloutVariant =
@@ -14,22 +16,22 @@ const StyledCalloutContainer = styled.div<{ variant: CalloutVariant }>`
align-items: flex-start;
background-color: ${({ theme, variant }) =>
variant === 'info'
- ? theme.color.blue1
+ ? theme.accent.accent1
: variant === 'warning'
- ? theme.color.yellow1
+ ? theme.color.orange1
: variant === 'success'
- ? theme.color.green1
+ ? theme.color.turquoise1
: variant === 'error'
? theme.color.red1
: theme.color.gray1};
border: 1px solid
${({ theme, variant }) =>
variant === 'info'
- ? theme.color.blue6
+ ? theme.accent.accent6
: variant === 'warning'
- ? theme.color.yellow6
+ ? theme.color.orange6
: variant === 'success'
- ? theme.color.green6
+ ? theme.color.turquoise6
: variant === 'error'
? theme.color.red6
: theme.color.gray6};
@@ -38,9 +40,11 @@ const StyledCalloutContainer = styled.div<{ variant: CalloutVariant }>`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
- margin-bottom: ${({ theme }) => theme.spacing(2)};
+ max-width: 512px;
+ overflow: hidden;
padding: ${({ theme }) =>
`${theme.spacing(3)} ${theme.spacing(3)} ${theme.spacing(2)}`};
+ width: 100%;
`;
const StyledHeader = styled.div`
@@ -49,6 +53,7 @@ const StyledHeader = styled.div`
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
+ min-height: ${({ theme }) => theme.spacing(6)};
`;
const StyledIconContainer = styled.div<{ variant: CalloutVariant }>`
@@ -56,13 +61,15 @@ const StyledIconContainer = styled.div<{ variant: CalloutVariant }>`
align-items: center;
justify-content: center;
flex-shrink: 0;
+ height: ${({ theme }) => theme.spacing(4)};
+ width: ${({ theme }) => theme.spacing(4)};
color: ${({ theme, variant }) =>
variant === 'info'
- ? theme.color.blue9
+ ? theme.accent.accent9
: variant === 'warning'
? theme.color.orange9
: variant === 'success'
- ? theme.color.green9
+ ? theme.color.turquoise9
: variant === 'error'
? theme.color.red9
: theme.color.gray9};
@@ -75,13 +82,18 @@ const StyledTitle = styled.div`
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.medium};
line-height: 1.4;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
`;
-const StyledDescriptionWrapper = styled.div`
+const StyledDescriptionWrapper = styled.div<{ hasAction: boolean }>`
+ align-items: center;
display: flex;
- padding-left: ${({ theme }) => theme.spacing(6)};
- padding-bottom: ${({ theme }) => theme.spacing(2)};
align-self: stretch;
+ padding-bottom: ${({ hasAction, theme }) =>
+ hasAction ? 0 : theme.spacing(2)};
+ padding-left: ${({ theme }) => theme.spacing(6)};
`;
const StyledDescription = styled.div`
@@ -104,10 +116,12 @@ export type CalloutProps = {
variant: CalloutVariant;
title: string;
description: string;
+ Icon?: IconComponent;
action?: {
label: string;
onClick: () => void;
};
+ isClosable?: boolean;
onClose?: () => void;
};
@@ -115,25 +129,43 @@ export const Callout = ({
variant,
title,
description,
+ Icon = IconHelp,
action,
+ isClosable = false,
onClose,
}: CalloutProps) => {
+ const [isVisible, setIsVisible] = useState(true);
+
+ const handleClose = () => {
+ if (!isClosable) {
+ return;
+ }
+
+ setIsVisible(false);
+ onClose?.();
+ };
+
+ if (!isVisible) {
+ return null;
+ }
+
return (
-
+
{title}
-
+ {isClosable && (
+
+ )}
-
+
{description}
{isDefined(action) && (
diff --git a/packages/twenty-ui/src/display/callout/__stories__/Callout.stories.tsx b/packages/twenty-ui/src/display/callout/__stories__/Callout.stories.tsx
index 3e10d43b7e..cfb3f2f093 100644
--- a/packages/twenty-ui/src/display/callout/__stories__/Callout.stories.tsx
+++ b/packages/twenty-ui/src/display/callout/__stories__/Callout.stories.tsx
@@ -19,10 +19,11 @@ type Story = StoryObj;
export const Default: Story = {
args: {
variant: 'neutral',
- title: 'An callout component',
- description: 'Description of callout component',
+ title: 'This form will appear in workflow runs.',
+ description:
+ 'Because this workflow is not using a manual trigger, the form will not open on top of the interface. To fill it, open the corresponding workflow run and complete the form there.',
action: {
- label: 'Learn more link',
+ label: 'Learn more',
onClick: () => {},
},
},
@@ -31,10 +32,11 @@ export const Default: Story = {
export const Catalog: CatalogStory = {
args: {
- title: 'An callout component',
- description: 'Description of callout component',
+ title: 'This form will appear in workflow runs.',
+ description:
+ 'Because this workflow is not using a manual trigger, the form will not open on top of the interface. To fill it, open the corresponding workflow run and complete the form there.',
action: {
- label: 'Learn more link',
+ label: 'Learn more',
onClick: () => {},
},
},
@@ -47,15 +49,20 @@ export const Catalog: CatalogStory = {
{
name: 'accents',
values: [
- 'warning',
- 'neutral',
- 'error',
- 'info',
'success',
+ 'warning',
+ 'error',
+ 'neutral',
+ 'info',
] satisfies CalloutVariant[],
props: (variant: CalloutVariant) => ({ variant }),
},
],
+ options: {
+ elementContainer: {
+ width: 512,
+ },
+ },
},
},
decorators: [CatalogDecorator],