Clarify forms usage (#17772)
Fixes https://github.com/twentyhq/core-team-issues/issues/1704
This commit is contained in:
+1
@@ -187,6 +187,7 @@ export const WorkflowStepDetail = ({
|
||||
case 'FORM': {
|
||||
return (
|
||||
<WorkflowEditActionFormBuilder
|
||||
triggerType={trigger?.type}
|
||||
key={stepId}
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ export const WorkflowMessage = ({
|
||||
return (
|
||||
<StyledMessageContainer>
|
||||
<FormFieldInputContainer>
|
||||
<FormFieldInputRowContainer multiline maxHeight={124}>
|
||||
<FormFieldInputRowContainer multiline maxHeight={145}>
|
||||
<FormFieldInputInnerContainer
|
||||
formFieldInputInstanceId="workflow-message"
|
||||
hasRightElement={false}
|
||||
|
||||
+20
-1
@@ -5,7 +5,10 @@ import { FormFieldPlaceholder } from '@/object-record/record-field/ui/form-types
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
|
||||
import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList';
|
||||
import { type WorkflowFormAction } from '@/workflow/types/Workflow';
|
||||
import {
|
||||
type WorkflowFormAction,
|
||||
type WorkflowTriggerType,
|
||||
} 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';
|
||||
@@ -21,6 +24,7 @@ import { useEffect, useState } from 'react';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
Callout,
|
||||
IconChevronDown,
|
||||
IconGripVertical,
|
||||
IconPlus,
|
||||
@@ -31,6 +35,7 @@ import { useDebouncedCallback } from 'use-debounce';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export type WorkflowEditActionFormBuilderProps = {
|
||||
triggerType: WorkflowTriggerType | undefined;
|
||||
action: WorkflowFormAction;
|
||||
actionOptions:
|
||||
| {
|
||||
@@ -128,6 +133,7 @@ const StyledAddFieldButtonContentContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const WorkflowEditActionFormBuilder = ({
|
||||
triggerType,
|
||||
action,
|
||||
actionOptions,
|
||||
}: WorkflowEditActionFormBuilderProps) => {
|
||||
@@ -136,6 +142,7 @@ export const WorkflowEditActionFormBuilder = ({
|
||||
|
||||
const [formData, setFormData] = useState<FormData>(action.settings.input);
|
||||
|
||||
const [isCalloutVisible, setIsCalloutVisible] = useState<boolean>(true);
|
||||
const [selectedField, setSelectedField] = useState<string | null>(null);
|
||||
const [hoveredField, setHoveredField] = useState<string | null>(null);
|
||||
|
||||
@@ -213,6 +220,18 @@ export const WorkflowEditActionFormBuilder = ({
|
||||
return (
|
||||
<>
|
||||
<StyledWorkflowStepBody>
|
||||
{triggerType && triggerType !== 'MANUAL' && isCalloutVisible && (
|
||||
<Callout
|
||||
learnMoreText={t`Learn more`}
|
||||
variant={'warning'}
|
||||
title={t`This form will appear in workflow runs.`}
|
||||
description={t`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.`}
|
||||
onClose={() => setIsCalloutVisible(false)}
|
||||
learnMoreUrl={
|
||||
'https://docs.twenty.com/user-guide/workflows/capabilities/workflow-actions#form'
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{formData.length === 0 && (
|
||||
<WorkflowMessage
|
||||
title={t`Add inputs to your form`}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { IconHelp, IconX } from '@ui/display/icon/components/TablerIcons';
|
||||
import { IconButton, LightButton } from '@ui/input';
|
||||
|
||||
export type CalloutVariant =
|
||||
| 'info'
|
||||
| 'warning'
|
||||
| 'error'
|
||||
| 'neutral'
|
||||
| 'success';
|
||||
|
||||
const StyledCalloutContainer = styled.div<{ variant: CalloutVariant }>`
|
||||
background-color: ${({ theme, variant }) =>
|
||||
variant === 'info'
|
||||
? theme.color.blue1
|
||||
: variant === 'warning'
|
||||
? theme.color.yellow1
|
||||
: variant === 'success'
|
||||
? theme.color.green1
|
||||
: variant === 'error'
|
||||
? theme.color.red1
|
||||
: theme.color.gray1};
|
||||
border: 1px solid
|
||||
${({ theme, variant }) =>
|
||||
variant === 'info'
|
||||
? theme.color.blue6
|
||||
: variant === 'warning'
|
||||
? theme.color.yellow6
|
||||
: variant === 'success'
|
||||
? theme.color.green6
|
||||
: variant === 'error'
|
||||
? theme.color.red6
|
||||
: theme.color.gray6};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
position: relative;
|
||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
margin-left: ${({ theme }) => theme.spacing(7)};
|
||||
`;
|
||||
|
||||
const StyledIconContainer = styled.div<{ variant: CalloutVariant }>`
|
||||
align-items: flex-start;
|
||||
color: ${({ theme, variant }) =>
|
||||
variant === 'info'
|
||||
? theme.color.blue9
|
||||
: variant === 'warning'
|
||||
? theme.color.orange9
|
||||
: variant === 'success'
|
||||
? theme.color.green9
|
||||
: variant === 'error'
|
||||
? theme.color.red9
|
||||
: theme.color.gray9};
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
padding-top: ${({ theme }) => theme.spacing(0.5)};
|
||||
`;
|
||||
|
||||
const StyledContent = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
min-width: 0;
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
line-height: 1.5;
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
line-height: 15px;
|
||||
`;
|
||||
|
||||
const StyledFooter = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledCloseButton = styled(IconButton)`
|
||||
position: absolute;
|
||||
right: ${({ theme }) => theme.spacing(3)};
|
||||
top: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
export type CalloutProps = {
|
||||
variant: CalloutVariant;
|
||||
title: string;
|
||||
description: string;
|
||||
learnMoreText: string;
|
||||
learnMoreUrl: string;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const Callout = ({
|
||||
variant,
|
||||
title,
|
||||
description,
|
||||
learnMoreText,
|
||||
learnMoreUrl,
|
||||
onClose,
|
||||
}: CalloutProps) => {
|
||||
return (
|
||||
<StyledCalloutContainer variant={variant}>
|
||||
<StyledIconContainer variant={variant}>
|
||||
<IconHelp size={16} />
|
||||
</StyledIconContainer>
|
||||
|
||||
<StyledContent>
|
||||
<StyledTitle>{title}</StyledTitle>
|
||||
<StyledDescription>{description}</StyledDescription>
|
||||
<StyledFooter>
|
||||
<LightButton
|
||||
type={'button'}
|
||||
title={learnMoreText}
|
||||
onClick={() => {
|
||||
window.open(learnMoreUrl, '_blank', 'noopener noreferrer');
|
||||
}}
|
||||
/>
|
||||
</StyledFooter>
|
||||
</StyledContent>
|
||||
<StyledCloseButton
|
||||
Icon={IconX}
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
ariaLabel={`Close`}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</StyledCalloutContainer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import {
|
||||
CatalogDecorator,
|
||||
type CatalogStory,
|
||||
ComponentDecorator,
|
||||
} from '@ui/testing';
|
||||
|
||||
import { Callout } from '@ui/display';
|
||||
import { type CalloutVariant } from '@ui/display/callout/Callout';
|
||||
|
||||
const meta: Meta<typeof Callout> = {
|
||||
title: 'UI/Display/Callout',
|
||||
component: Callout,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Callout>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
variant: 'neutral',
|
||||
title: 'An callout component',
|
||||
description: 'Description of callout component',
|
||||
learnMoreText: 'Learn more link',
|
||||
},
|
||||
decorators: [ComponentDecorator],
|
||||
};
|
||||
|
||||
export const Catalog: CatalogStory<Story, typeof Callout> = {
|
||||
args: {
|
||||
title: 'An callout component',
|
||||
description: 'Description of callout component',
|
||||
learnMoreText: 'Learn more link',
|
||||
},
|
||||
argTypes: {
|
||||
variant: { control: false },
|
||||
},
|
||||
parameters: {
|
||||
catalog: {
|
||||
dimensions: [
|
||||
{
|
||||
name: 'accents',
|
||||
values: [
|
||||
'warning',
|
||||
'neutral',
|
||||
'error',
|
||||
'info',
|
||||
'success',
|
||||
] satisfies CalloutVariant[],
|
||||
props: (variant: CalloutVariant) => ({ variant }),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
decorators: [CatalogDecorator],
|
||||
};
|
||||
@@ -203,6 +203,7 @@ export {
|
||||
IconHeart,
|
||||
IconHeartOff,
|
||||
IconHeartRateMonitor,
|
||||
IconHelp,
|
||||
IconHelpCircle,
|
||||
IconHierarchy,
|
||||
IconHierarchy2,
|
||||
|
||||
@@ -19,6 +19,8 @@ export type { BannerVariant } from './banner/components/Banner';
|
||||
export { Banner } from './banner/components/Banner';
|
||||
export type { SidePanelInformationBannerProps } from './banner/components/SidePanelInformationBanner';
|
||||
export { SidePanelInformationBanner } from './banner/components/SidePanelInformationBanner';
|
||||
export type { CalloutVariant, CalloutProps } from './callout/Callout';
|
||||
export { Callout } from './callout/Callout';
|
||||
export type { AnimatedCheckmarkProps } from './checkmark/components/AnimatedCheckmark';
|
||||
export { AnimatedCheckmark } from './checkmark/components/AnimatedCheckmark';
|
||||
export type { CheckmarkProps } from './checkmark/components/Checkmark';
|
||||
@@ -272,6 +274,7 @@ export {
|
||||
IconHeart,
|
||||
IconHeartOff,
|
||||
IconHeartRateMonitor,
|
||||
IconHelp,
|
||||
IconHelpCircle,
|
||||
IconHierarchy,
|
||||
IconHierarchy2,
|
||||
|
||||
Reference in New Issue
Block a user