Add form date field (#11360)
Builder <img width="498" alt="Capture d’écran 2025-04-02 à 19 02 20" src="https://github.com/user-attachments/assets/29db9fa9-aae4-4d1f-98f2-0b2371f944f1" /> Execution <img width="837" alt="Capture d’écran 2025-04-02 à 19 02 47" src="https://github.com/user-attachments/assets/ce1c442c-3c06-4f7e-99d6-3eb8fb1d2428" />
This commit is contained in:
@ -92,6 +92,7 @@ export const workflowFormActionSettingsSchema =
|
||||
type: z.union([
|
||||
z.literal(FieldMetadataType.TEXT),
|
||||
z.literal(FieldMetadataType.NUMBER),
|
||||
z.literal(FieldMetadataType.DATE),
|
||||
z.literal('RECORD'),
|
||||
]),
|
||||
placeholder: z.string().optional(),
|
||||
|
||||
@ -2,6 +2,7 @@ import { FormFieldInputContainer } from '@/object-record/record-field/form-types
|
||||
import { FormSelectFieldInput } from '@/object-record/record-field/form-types/components/FormSelectFieldInput';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { WorkflowFormFieldSettingsByType } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsByType';
|
||||
import { FORM_SELECT_FIELD_TYPE_OPTIONS } from '@/workflow/workflow-steps/workflow-actions/form-action/constants/FormSelectFieldTypeOptions';
|
||||
import { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
|
||||
import { WorkflowFormFieldType } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormFieldType';
|
||||
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
|
||||
@ -9,15 +10,7 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import camelCase from 'lodash.camelcase';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import {
|
||||
IconSettingsAutomation,
|
||||
IconX,
|
||||
IllustrationIconNumbers,
|
||||
IllustrationIconOneToMany,
|
||||
IllustrationIconText,
|
||||
LightIconButton,
|
||||
} from 'twenty-ui';
|
||||
import { IconSettingsAutomation, IconX, LightIconButton } from 'twenty-ui';
|
||||
|
||||
type WorkflowEditActionFormFieldSettingsProps = {
|
||||
field: WorkflowFormActionField;
|
||||
@ -109,31 +102,7 @@ export const WorkflowEditActionFormFieldSettings = ({
|
||||
<FormFieldInputContainer>
|
||||
<InputLabel>Type</InputLabel>
|
||||
<FormSelectFieldInput
|
||||
options={
|
||||
[
|
||||
{
|
||||
label: getDefaultFormFieldSettings(FieldMetadataType.TEXT)
|
||||
.label,
|
||||
value: FieldMetadataType.TEXT,
|
||||
Icon: IllustrationIconText,
|
||||
},
|
||||
{
|
||||
label: getDefaultFormFieldSettings(FieldMetadataType.NUMBER)
|
||||
.label,
|
||||
value: FieldMetadataType.NUMBER,
|
||||
Icon: IllustrationIconNumbers,
|
||||
},
|
||||
{
|
||||
label: 'Record Picker',
|
||||
value: 'RECORD',
|
||||
Icon: IllustrationIconOneToMany,
|
||||
},
|
||||
] satisfies {
|
||||
label: string;
|
||||
value: WorkflowFormFieldType;
|
||||
Icon: React.ElementType;
|
||||
}[]
|
||||
}
|
||||
options={FORM_SELECT_FIELD_TYPE_OPTIONS}
|
||||
onChange={(newType: string | null) => {
|
||||
if (newType === null) {
|
||||
return;
|
||||
|
||||
@ -158,7 +158,7 @@ export const WorkflowEditActionFormFiller = ({
|
||||
value,
|
||||
});
|
||||
}}
|
||||
defaultValue={field.value ?? ''}
|
||||
defaultValue={field.value}
|
||||
readonly={actionOptions.readonly}
|
||||
placeholder={
|
||||
field.placeholder ??
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { WorkflowFormFieldSettingsDate } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsDate';
|
||||
import { WorkflowFormFieldSettingsRecordPicker } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsRecordPicker';
|
||||
import { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
@ -33,6 +34,15 @@ export const WorkflowFormFieldSettingsByType = ({
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case FieldMetadataType.DATE:
|
||||
return (
|
||||
<WorkflowFormFieldSettingsDate
|
||||
label={field.label}
|
||||
onChange={(fieldName, value) => {
|
||||
onChange(fieldName, value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 'RECORD':
|
||||
return (
|
||||
<WorkflowFormFieldSettingsRecordPicker
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
|
||||
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
type WorkflowFormFieldSettingsDateProps = {
|
||||
label?: string;
|
||||
onChange: (fieldName: string, value: string | null) => void;
|
||||
};
|
||||
|
||||
export const WorkflowFormFieldSettingsDate = ({
|
||||
label,
|
||||
onChange,
|
||||
}: WorkflowFormFieldSettingsDateProps) => {
|
||||
return (
|
||||
<FormFieldInputContainer>
|
||||
<InputLabel>Label</InputLabel>
|
||||
<FormTextFieldInput
|
||||
onChange={(newLabel: string | null) => {
|
||||
onChange('label', newLabel);
|
||||
}}
|
||||
defaultValue={label}
|
||||
placeholder={getDefaultFormFieldSettings(FieldMetadataType.DATE).label}
|
||||
/>
|
||||
</FormFieldInputContainer>
|
||||
);
|
||||
};
|
||||
@ -97,8 +97,8 @@ export const SingleRecordFieldSettings: Story = {
|
||||
args: {
|
||||
field: {
|
||||
id: 'field-3',
|
||||
name: 'record',
|
||||
label: 'Record',
|
||||
name: 'company',
|
||||
label: 'Company',
|
||||
type: 'RECORD',
|
||||
settings: {
|
||||
objectName: 'company',
|
||||
@ -121,3 +121,27 @@ export const SingleRecordFieldSettings: Story = {
|
||||
expect(args.onClose).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
export const DateFieldSettings: Story = {
|
||||
args: {
|
||||
field: {
|
||||
id: 'field-4',
|
||||
name: 'date',
|
||||
label: 'Date Field',
|
||||
type: FieldMetadataType.DATE,
|
||||
placeholder: 'Enter date',
|
||||
settings: {},
|
||||
},
|
||||
onClose: fn(),
|
||||
},
|
||||
play: async ({ canvasElement, args }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const typeSelect = await canvas.findByText('Date');
|
||||
expect(typeSelect).toBeVisible();
|
||||
|
||||
const closeButton = await canvas.findByTestId('close-button');
|
||||
await userEvent.click(closeButton);
|
||||
expect(args.onClose).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
@ -64,6 +64,14 @@ const mockAction: WorkflowFormAction = {
|
||||
objectName: 'company',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'field-4',
|
||||
name: 'date',
|
||||
label: 'Date',
|
||||
type: FieldMetadataType.DATE,
|
||||
placeholder: 'mm/dd/yyyy',
|
||||
settings: {},
|
||||
},
|
||||
],
|
||||
outputSchema: {},
|
||||
errorHandlingOptions: {
|
||||
@ -91,6 +99,9 @@ export const Default: Story = {
|
||||
|
||||
const recordField = await canvas.findByText('Record');
|
||||
expect(recordField).toBeVisible();
|
||||
|
||||
const dateField = await canvas.findByText('Date');
|
||||
expect(dateField).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
@ -110,6 +121,9 @@ export const ReadonlyMode: Story = {
|
||||
const numberInput = await canvas.findByPlaceholderText('Enter number');
|
||||
expect(numberInput).toBeDisabled();
|
||||
|
||||
const dateInput = await canvas.findByPlaceholderText('mm/dd/yyyy');
|
||||
expect(dateInput).toBeDisabled();
|
||||
|
||||
const submitButton = await canvas.queryByText('Submit');
|
||||
expect(submitButton).not.toBeInTheDocument();
|
||||
},
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
import { WorkflowFormFieldType } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormFieldType';
|
||||
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import {
|
||||
IllustrationIconCalendarEvent,
|
||||
IllustrationIconNumbers,
|
||||
IllustrationIconOneToMany,
|
||||
IllustrationIconText,
|
||||
SelectOption,
|
||||
} from 'twenty-ui';
|
||||
|
||||
export const FORM_SELECT_FIELD_TYPE_OPTIONS: SelectOption<WorkflowFormFieldType>[] =
|
||||
[
|
||||
{
|
||||
label: getDefaultFormFieldSettings(FieldMetadataType.TEXT).label,
|
||||
value: FieldMetadataType.TEXT,
|
||||
Icon: IllustrationIconText,
|
||||
},
|
||||
{
|
||||
label: getDefaultFormFieldSettings(FieldMetadataType.NUMBER).label,
|
||||
value: FieldMetadataType.NUMBER,
|
||||
Icon: IllustrationIconNumbers,
|
||||
},
|
||||
{
|
||||
label: getDefaultFormFieldSettings(FieldMetadataType.DATE).label,
|
||||
value: FieldMetadataType.DATE,
|
||||
Icon: IllustrationIconCalendarEvent,
|
||||
},
|
||||
{
|
||||
label: getDefaultFormFieldSettings('RECORD').label,
|
||||
value: 'RECORD',
|
||||
Icon: IllustrationIconOneToMany,
|
||||
},
|
||||
];
|
||||
@ -3,4 +3,5 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
export type WorkflowFormFieldType =
|
||||
| FieldMetadataType.TEXT
|
||||
| FieldMetadataType.NUMBER
|
||||
| FieldMetadataType.DATE
|
||||
| 'RECORD';
|
||||
|
||||
@ -19,6 +19,13 @@ export const getDefaultFormFieldSettings = (type: WorkflowFormFieldType) => {
|
||||
label: 'Number',
|
||||
placeholder: '1000',
|
||||
};
|
||||
case FieldMetadataType.DATE:
|
||||
return {
|
||||
id: v4(),
|
||||
name: 'date',
|
||||
label: 'Date',
|
||||
placeholder: 'mm/dd/yyyy',
|
||||
};
|
||||
case 'RECORD':
|
||||
return {
|
||||
id: v4(),
|
||||
|
||||
@ -40,6 +40,13 @@ describe('generateFakeFormResponse', () => {
|
||||
objectName: 'company',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '96939213-49ac-4dee-949d-56e6c7be98e9',
|
||||
name: 'date',
|
||||
type: FieldMetadataType.DATE,
|
||||
label: 'Date',
|
||||
placeholder: 'mm/dd/yyyy',
|
||||
},
|
||||
];
|
||||
|
||||
const result = await generateFakeFormResponse({
|
||||
@ -79,6 +86,13 @@ describe('generateFakeFormResponse', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
date: {
|
||||
isLeaf: true,
|
||||
label: 'Date',
|
||||
type: FieldMetadataType.DATE,
|
||||
value: '01/23/2025',
|
||||
icon: undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -3,4 +3,5 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
export type WorkflowFormFieldType =
|
||||
| FieldMetadataType.TEXT
|
||||
| FieldMetadataType.NUMBER
|
||||
| FieldMetadataType.DATE
|
||||
| 'RECORD';
|
||||
|
||||
Reference in New Issue
Block a user