Add empty message for form actions (#12414)
<img width="503" alt="Capture d’écran 2025-06-02 à 15 55 36" src="https://github.com/user-attachments/assets/9b3f60ae-7a13-45f8-aa87-ba32211e832f" />
This commit is contained in:
@ -5,17 +5,18 @@ export const LINE_HEIGHT = 24;
|
|||||||
|
|
||||||
const StyledFormFieldInputRowContainer = styled.div<{
|
const StyledFormFieldInputRowContainer = styled.div<{
|
||||||
multiline?: boolean;
|
multiline?: boolean;
|
||||||
|
maxHeight?: number;
|
||||||
}>`
|
}>`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
${({ multiline }) =>
|
${({ multiline, maxHeight }) =>
|
||||||
multiline
|
multiline
|
||||||
? css`
|
? css`
|
||||||
line-height: ${LINE_HEIGHT}px;
|
line-height: ${LINE_HEIGHT}px;
|
||||||
min-height: ${3 * LINE_HEIGHT}px;
|
min-height: ${3 * LINE_HEIGHT}px;
|
||||||
max-height: ${5 * LINE_HEIGHT}px;
|
max-height: ${maxHeight ?? 5 * LINE_HEIGHT}px;
|
||||||
`
|
`
|
||||||
: css`
|
: css`
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { WorkflowFormAction } from '@/workflow/types/Workflow';
|
|||||||
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
|
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
|
||||||
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
|
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
|
||||||
import { WorkflowEditActionFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFieldSettings';
|
import { WorkflowEditActionFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFieldSettings';
|
||||||
|
import { WorkflowFormEmptyMessage } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormEmptyMessage';
|
||||||
import { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
|
import { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
|
||||||
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
|
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
|
||||||
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
|
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
|
||||||
@ -234,6 +235,9 @@ export const WorkflowEditActionFormBuilder = ({
|
|||||||
disabled={actionOptions.readonly}
|
disabled={actionOptions.readonly}
|
||||||
/>
|
/>
|
||||||
<StyledWorkflowStepBody>
|
<StyledWorkflowStepBody>
|
||||||
|
{formData.length === 0 && (
|
||||||
|
<WorkflowFormEmptyMessage data-testid="empty-form-message" />
|
||||||
|
)}
|
||||||
<DraggableList
|
<DraggableList
|
||||||
onDragEnd={handleDragEnd}
|
onDragEnd={handleDragEnd}
|
||||||
draggableItems={
|
draggableItems={
|
||||||
|
|||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
|
||||||
|
import { FormFieldInputInnerContainer } from '@/object-record/record-field/form-types/components/FormFieldInputInnerContainer';
|
||||||
|
import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { useLingui } from '@lingui/react/macro';
|
||||||
|
|
||||||
|
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 WorkflowFormEmptyMessage = () => {
|
||||||
|
const { t } = useLingui();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledMessageContainer>
|
||||||
|
<FormFieldInputContainer>
|
||||||
|
<FormFieldInputRowContainer multiline maxHeight={124}>
|
||||||
|
<FormFieldInputInnerContainer hasRightElement={false}>
|
||||||
|
<StyledFieldContainer>
|
||||||
|
<StyledMessageContentContainer>
|
||||||
|
<StyledMessageTitle data-testid="empty-form-message-title">
|
||||||
|
{t`Add inputs to your form`}
|
||||||
|
</StyledMessageTitle>
|
||||||
|
<StyledMessageDescription data-testid="empty-form-message-description">
|
||||||
|
{t`Click on "Add Field" below to add the first input to your form. The form will pop up on the user's screen when the workflow is launched from a manual trigger. For other types of triggers, it will be displayed in the Workflow run record page.`}
|
||||||
|
</StyledMessageDescription>
|
||||||
|
</StyledMessageContentContainer>
|
||||||
|
</StyledFieldContainer>
|
||||||
|
</FormFieldInputInnerContainer>
|
||||||
|
</FormFieldInputRowContainer>
|
||||||
|
</FormFieldInputContainer>
|
||||||
|
</StyledMessageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -170,3 +170,29 @@ export const DisabledWithEmptyValues: Story = {
|
|||||||
expect(addFieldButton).not.toBeInTheDocument();
|
expect(addFieldButton).not.toBeInTheDocument();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const EmptyForm: Story = {
|
||||||
|
args: {
|
||||||
|
actionOptions: {
|
||||||
|
onActionUpdate: fn(),
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
...DEFAULT_ACTION,
|
||||||
|
settings: {
|
||||||
|
...DEFAULT_ACTION.settings,
|
||||||
|
input: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
const messageContainer = await canvas.findByTestId(
|
||||||
|
'empty-form-message-title',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(messageContainer).toBeVisible();
|
||||||
|
|
||||||
|
const addFieldButton = await canvas.findByText('Add Field');
|
||||||
|
expect(addFieldButton).toBeVisible();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { expect, within } from '@storybook/test';
|
||||||
|
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||||
|
import { WorkflowFormEmptyMessage } from '../WorkflowFormEmptyMessage';
|
||||||
|
|
||||||
|
const meta: Meta<typeof WorkflowFormEmptyMessage> = {
|
||||||
|
title: 'Modules/Workflow/Actions/Form/WorkflowFormEmptyMessage',
|
||||||
|
component: WorkflowFormEmptyMessage,
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
},
|
||||||
|
decorators: [I18nFrontDecorator],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof WorkflowFormEmptyMessage>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
const messageTitleContainer = await canvas.findByTestId(
|
||||||
|
'empty-form-message-title',
|
||||||
|
);
|
||||||
|
const messageDescriptionContainer = await canvas.findByTestId(
|
||||||
|
'empty-form-message-description',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(messageTitleContainer).toBeVisible();
|
||||||
|
expect(messageDescriptionContainer).toBeVisible();
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
|
||||||
import { isDefined, isValidUuid } from 'twenty-shared/utils';
|
import { isDefined, isValidUuid } from 'twenty-shared/utils';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
@ -541,22 +540,7 @@ export class WorkflowVersionStepWorkspaceService {
|
|||||||
valid: false,
|
valid: false,
|
||||||
settings: {
|
settings: {
|
||||||
...BASE_STEP_DEFINITION,
|
...BASE_STEP_DEFINITION,
|
||||||
input: [
|
input: [],
|
||||||
{
|
|
||||||
id: v4(),
|
|
||||||
name: 'company',
|
|
||||||
label: 'Company',
|
|
||||||
placeholder: 'Select a company',
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: v4(),
|
|
||||||
name: 'number',
|
|
||||||
label: 'Number',
|
|
||||||
placeholder: '1000',
|
|
||||||
type: FieldMetadataType.NUMBER,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user