From f4a362b53a6a948b1cc9cab7cea1f6c835f44278 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Wed, 12 Mar 2025 18:05:31 +0100 Subject: [PATCH] Add base form action without logic (#10811) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capture d’écran 2025-03-12 à 15 32 27 --- .../src/generated-metadata/graphql.ts | 4 +- .../twenty-front/src/generated/graphql.tsx | 43 +----- .../__stories__/CommandMenu.stories.tsx | 17 +-- ...CommandMenuWorkflowSelectActionContent.tsx | 9 +- .../src/modules/workflow/types/Workflow.ts | 6 + .../validation-schemas/workflowSchema.ts | 20 +++ .../components/WorkflowStepDetail.tsx | 47 ++++--- ...tsx => WorkflowEditActionCreateRecord.tsx} | 6 +- ...tsx => WorkflowEditActionDeleteRecord.tsx} | 6 +- ....tsx => WorkflowEditActionFindRecords.tsx} | 6 +- .../components/WorkflowEditActionForm.tsx | 123 ++++++++++++++++++ ...il.tsx => WorkflowEditActionSendEmail.tsx} | 6 +- ... WorkflowEditActionServerlessFunction.tsx} | 12 +- ...lowEditActionServerlessFunctionFields.tsx} | 8 +- ...tsx => WorkflowEditActionUpdateRecord.tsx} | 6 +- ...kflowReadonlyActionServerlessFunction.tsx} | 10 +- ...orkflowEditActionCreateRecord.stories.tsx} | 10 +- ...orkflowEditActionDeleteRecord.stories.tsx} | 10 +- ...WorkflowEditActionFindRecords.stories.tsx} | 10 +- .../WorkflowEditActionForm.stories.tsx | 99 ++++++++++++++ ...orkflowEditActionUpdateRecord.stories.tsx} | 10 +- .../constants/OtherActions.ts | 5 + .../constants/RecordActions.ts | 11 +- .../typeorm-seeds/core/feature-flags.ts | 5 + .../enums/feature-flag-key.enum.ts | 1 + ...workflow-version-step.workspace-service.ts | 27 +++- 26 files changed, 385 insertions(+), 132 deletions(-) rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/{WorkflowEditActionFormCreateRecord.tsx => WorkflowEditActionCreateRecord.tsx} (97%) rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/{WorkflowEditActionFormDeleteRecord.tsx => WorkflowEditActionDeleteRecord.tsx} (96%) rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/{WorkflowEditActionFormFindRecords.tsx => WorkflowEditActionFindRecords.tsx} (96%) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionForm.tsx rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/{WorkflowEditActionFormSendEmail.tsx => WorkflowEditActionSendEmail.tsx} (98%) rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/{WorkflowEditActionFormServerlessFunction.tsx => WorkflowEditActionServerlessFunction.tsx} (96%) rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/{WorkflowEditActionFormServerlessFunctionFields.tsx => WorkflowEditActionServerlessFunctionFields.tsx} (89%) rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/{WorkflowEditActionFormUpdateRecord.tsx => WorkflowEditActionUpdateRecord.tsx} (98%) rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/{WorkflowReadonlyActionFormServerlessFunction.tsx => WorkflowReadonlyActionServerlessFunction.tsx} (88%) rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/{WorkflowEditActionFormCreateRecord.stories.tsx => WorkflowEditActionCreateRecord.stories.tsx} (87%) rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/{WorkflowEditActionFormDeleteRecord.stories.tsx => WorkflowEditActionDeleteRecord.stories.tsx} (93%) rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/{WorkflowEditActionFormFindRecords.stories.tsx => WorkflowEditActionFindRecords.stories.tsx} (85%) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionForm.stories.tsx rename packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/{WorkflowEditActionFormUpdateRecord.stories.tsx => WorkflowEditActionUpdateRecord.stories.tsx} (95%) diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index d103ff152..cd7691737 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -560,7 +560,6 @@ export enum FeatureFlagKey { IsAirtableIntegrationEnabled = 'IsAirtableIntegrationEnabled', IsAnalyticsV2Enabled = 'IsAnalyticsV2Enabled', IsApprovedAccessDomainsEnabled = 'IsApprovedAccessDomainsEnabled', - IsCommandMenuV2Enabled = 'IsCommandMenuV2Enabled', IsCopilotEnabled = 'IsCopilotEnabled', IsCustomDomainEnabled = 'IsCustomDomainEnabled', IsEventObjectEnabled = 'IsEventObjectEnabled', @@ -570,7 +569,8 @@ export enum FeatureFlagKey { IsPostgreSQLIntegrationEnabled = 'IsPostgreSQLIntegrationEnabled', IsStripeIntegrationEnabled = 'IsStripeIntegrationEnabled', IsUniqueIndexesEnabled = 'IsUniqueIndexesEnabled', - IsWorkflowEnabled = 'IsWorkflowEnabled' + IsWorkflowEnabled = 'IsWorkflowEnabled', + IsWorkflowFormActionEnabled = 'IsWorkflowFormActionEnabled' } export type Field = { diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 2a197d303..ab4abad1e 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -491,7 +491,6 @@ export enum FeatureFlagKey { IsAirtableIntegrationEnabled = 'IsAirtableIntegrationEnabled', IsAnalyticsV2Enabled = 'IsAnalyticsV2Enabled', IsApprovedAccessDomainsEnabled = 'IsApprovedAccessDomainsEnabled', - IsCommandMenuV2Enabled = 'IsCommandMenuV2Enabled', IsCopilotEnabled = 'IsCopilotEnabled', IsCustomDomainEnabled = 'IsCustomDomainEnabled', IsEventObjectEnabled = 'IsEventObjectEnabled', @@ -501,7 +500,8 @@ export enum FeatureFlagKey { IsPostgreSQLIntegrationEnabled = 'IsPostgreSQLIntegrationEnabled', IsStripeIntegrationEnabled = 'IsStripeIntegrationEnabled', IsUniqueIndexesEnabled = 'IsUniqueIndexesEnabled', - IsWorkflowEnabled = 'IsWorkflowEnabled' + IsWorkflowEnabled = 'IsWorkflowEnabled', + IsWorkflowFormActionEnabled = 'IsWorkflowFormActionEnabled' } export type Field = { @@ -1658,6 +1658,8 @@ export type ServerlessFunctionExecutionResult = { duration: Scalars['Float']; /** Execution error in JSON format */ error?: Maybe; + /** Execution Logs */ + logs: Scalars['String']; /** Execution status */ status: ServerlessFunctionExecutionStatus; }; @@ -2220,11 +2222,6 @@ export type GetTimelineThreadsFromPersonIdQueryVariables = Exact<{ export type GetTimelineThreadsFromPersonIdQuery = { __typename?: 'Query', getTimelineThreadsFromPersonId: { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } }; -export type EmptyQueryVariables = Exact<{ [key: string]: never; }>; - - -export type EmptyQuery = { __typename: 'Query' }; - export type TrackMutationVariables = Exact<{ action: Scalars['String']; payload: Scalars['JSON']; @@ -3065,38 +3062,6 @@ export function useGetTimelineThreadsFromPersonIdLazyQuery(baseOptions?: Apollo. export type GetTimelineThreadsFromPersonIdQueryHookResult = ReturnType; export type GetTimelineThreadsFromPersonIdLazyQueryHookResult = ReturnType; export type GetTimelineThreadsFromPersonIdQueryResult = Apollo.QueryResult; -export const EmptyDocument = gql` - query Empty { - __typename -} - `; - -/** - * __useEmptyQuery__ - * - * To run a query within a React component, call `useEmptyQuery` and pass it any options that fit your needs. - * When your component renders, `useEmptyQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useEmptyQuery({ - * variables: { - * }, - * }); - */ -export function useEmptyQuery(baseOptions?: Apollo.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(EmptyDocument, options); - } -export function useEmptyLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(EmptyDocument, options); - } -export type EmptyQueryHookResult = ReturnType; -export type EmptyLazyQueryHookResult = ReturnType; -export type EmptyQueryResult = Apollo.QueryResult; export const TrackDocument = gql` mutation Track($action: String!, $payload: JSON!) { track(action: $action, payload: $payload) { diff --git a/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx b/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx index a37b4bb52..42a02de0b 100644 --- a/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx @@ -26,27 +26,12 @@ import { RecordFiltersComponentInstanceContext } from '@/object-record/record-fi import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext'; import { HttpResponse, graphql } from 'msw'; import { IconDotsVertical } from 'twenty-ui'; -import { FeatureFlagKey } from '~/generated/graphql'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { JestContextStoreSetter } from '~/testing/jest/JestContextStoreSetter'; import { CommandMenu } from '../CommandMenu'; const openTimeout = 50; -// Mock workspace with feature flag enabled -const mockWorkspaceWithFeatureFlag = { - ...mockCurrentWorkspace, - featureFlags: [ - ...(mockCurrentWorkspace.featureFlags || []), - { - id: 'mock-id', - key: FeatureFlagKey.IsCommandMenuV2Enabled, - value: true, - workspaceId: mockCurrentWorkspace.id, - }, - ], -}; - const ContextStoreDecorator: Decorator = (Story) => { return ( = { commandMenuNavigationStackState, ); - setCurrentWorkspace(mockWorkspaceWithFeatureFlag); + setCurrentWorkspace(mockCurrentWorkspace); setCurrentWorkspaceMember(mockedWorkspaceMemberData); setIsCommandMenuOpened(true); setCommandMenuNavigationStack([ diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectActionContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectActionContent.tsx index 7999c5805..d97f764cf 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectActionContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectActionContent.tsx @@ -4,7 +4,9 @@ import { RightDrawerWorkflowSelectStepTitle } from '@/workflow/workflow-steps/co import { useCreateStep } from '@/workflow/workflow-steps/hooks/useCreateStep'; import { OTHER_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/OtherActions'; import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { MenuItemCommand, useIcons } from 'twenty-ui'; +import { FeatureFlagKey } from '~/generated-metadata/graphql'; export const CommandMenuWorkflowSelectActionContent = ({ workflow, @@ -15,6 +17,9 @@ export const CommandMenuWorkflowSelectActionContent = ({ const { createStep } = useCreateStep({ workflow, }); + const isWorkflowFormActionEnabled = useIsFeatureEnabled( + FeatureFlagKey.IsWorkflowFormActionEnabled, + ); return ( @@ -32,7 +37,9 @@ export const CommandMenuWorkflowSelectActionContent = ({ Other - {OTHER_ACTIONS.map((action) => ( + {OTHER_ACTIONS.filter( + (action) => isWorkflowFormActionEnabled || action.type !== 'FORM', + ).map((action) => ( ; +export type WorkflowFormActionSettings = z.infer< + typeof workflowFormActionSettingsSchema +>; export type WorkflowCodeAction = z.infer; export type WorkflowSendEmailAction = z.infer< @@ -59,6 +64,7 @@ export type WorkflowDeleteRecordAction = z.infer< export type WorkflowFindRecordsAction = z.infer< typeof workflowFindRecordsActionSchema >; +export type WorkflowFormAction = z.infer; export type WorkflowAction = z.infer; export type WorkflowActionType = WorkflowAction['type']; diff --git a/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts b/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts index 4373e0aa3..176472dd3 100644 --- a/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts +++ b/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts @@ -1,3 +1,4 @@ +import { FieldMetadataType } from 'twenty-shared'; import { z } from 'zod'; // Base schemas @@ -81,6 +82,19 @@ export const workflowFindRecordsActionSettingsSchema = }), }); +export const workflowFormActionSettingsSchema = + baseWorkflowActionSettingsSchema.extend({ + input: z.array( + z.object({ + label: z.string(), + name: z.string(), + type: z.nativeEnum(FieldMetadataType), + placeholder: z.string().optional(), + settings: z.record(z.any()), + }), + ), + }); + // Action schemas export const workflowCodeActionSchema = baseWorkflowActionSchema.extend({ type: z.literal('CODE'), @@ -118,6 +132,11 @@ export const workflowFindRecordsActionSchema = baseWorkflowActionSchema.extend({ settings: workflowFindRecordsActionSettingsSchema, }); +export const workflowFormActionSchema = baseWorkflowActionSchema.extend({ + type: z.literal('FORM'), + settings: workflowFormActionSettingsSchema, +}); + // Combined action schema export const workflowActionSchema = z.discriminatedUnion('type', [ workflowCodeActionSchema, @@ -126,6 +145,7 @@ export const workflowActionSchema = z.discriminatedUnion('type', [ workflowUpdateRecordActionSchema, workflowDeleteRecordActionSchema, workflowFindRecordsActionSchema, + workflowFormActionSchema, ]); // Trigger schemas diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepDetail.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepDetail.tsx index f56d759fd..21df64c9b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepDetail.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepDetail.tsx @@ -1,11 +1,12 @@ import { WorkflowAction, WorkflowTrigger } from '@/workflow/types/Workflow'; import { assertUnreachable } from '@/workflow/utils/assertUnreachable'; import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow'; -import { WorkflowEditActionFormCreateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormCreateRecord'; -import { WorkflowEditActionFormDeleteRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormDeleteRecord'; -import { WorkflowEditActionFormFindRecords } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormFindRecords'; -import { WorkflowEditActionFormSendEmail } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormSendEmail'; -import { WorkflowEditActionFormUpdateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormUpdateRecord'; +import { WorkflowEditActionCreateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord'; +import { WorkflowEditActionDeleteRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord'; +import { WorkflowEditActionFindRecords } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords'; +import { WorkflowEditActionForm } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionForm'; +import { WorkflowEditActionSendEmail } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail'; +import { WorkflowEditActionUpdateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord'; import { WorkflowEditTriggerCronForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm'; import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm'; import { WorkflowEditTriggerManualForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerManualForm'; @@ -13,19 +14,19 @@ import { Suspense, lazy } from 'react'; import { isDefined } from 'twenty-shared'; import { RightDrawerSkeletonLoader } from '~/loading/components/RightDrawerSkeletonLoader'; -const WorkflowEditActionFormServerlessFunction = lazy(() => +const WorkflowEditActionServerlessFunction = lazy(() => import( - '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunction' + '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionServerlessFunction' ).then((module) => ({ - default: module.WorkflowEditActionFormServerlessFunction, + default: module.WorkflowEditActionServerlessFunction, })), ); -const WorkflowReadonlyActionFormServerlessFunction = lazy(() => +const WorkflowReadonlyActionServerlessFunction = lazy(() => import( - '@/workflow/workflow-steps/workflow-actions/components/WorkflowReadonlyActionFormServerlessFunction' + '@/workflow/workflow-steps/workflow-actions/components/WorkflowReadonlyActionServerlessFunction' ).then((module) => ({ - default: module.WorkflowReadonlyActionFormServerlessFunction, + default: module.WorkflowReadonlyActionServerlessFunction, })), ); @@ -106,12 +107,12 @@ export const WorkflowStepDetail = ({ return ( }> {props.readonly ? ( - ) : ( - + ); + } + + case 'FORM': { + return ( + { +}: WorkflowEditActionCreateRecordProps) => { const theme = useTheme(); const { getIcon } = useIcons(); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormDeleteRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx similarity index 96% rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormDeleteRecord.tsx rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx index 5f2a8cdd9..63d410d7e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormDeleteRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx @@ -13,7 +13,7 @@ import { HorizontalSeparator, useIcons } from 'twenty-ui'; import { JsonValue } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; -type WorkflowEditActionFormDeleteRecordProps = { +type WorkflowEditActionDeleteRecordProps = { action: WorkflowDeleteRecordAction; actionOptions: | { @@ -30,10 +30,10 @@ type DeleteRecordFormData = { objectRecordId: string; }; -export const WorkflowEditActionFormDeleteRecord = ({ +export const WorkflowEditActionDeleteRecord = ({ action, actionOptions, -}: WorkflowEditActionFormDeleteRecordProps) => { +}: WorkflowEditActionDeleteRecordProps) => { const theme = useTheme(); const { getIcon } = useIcons(); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormFindRecords.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords.tsx similarity index 96% rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormFindRecords.tsx rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords.tsx index fd1e879f0..394136f52 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormFindRecords.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords.tsx @@ -12,7 +12,7 @@ import { isDefined } from 'twenty-shared'; import { HorizontalSeparator, useIcons } from 'twenty-ui'; import { useDebouncedCallback } from 'use-debounce'; -type WorkflowEditActionFormFindRecordsProps = { +type WorkflowEditActionFindRecordsProps = { action: WorkflowFindRecordsAction; actionOptions: | { @@ -29,10 +29,10 @@ type FindRecordsFormData = { limit?: number; }; -export const WorkflowEditActionFormFindRecords = ({ +export const WorkflowEditActionFindRecords = ({ action, actionOptions, -}: WorkflowEditActionFormFindRecordsProps) => { +}: WorkflowEditActionFindRecordsProps) => { const theme = useTheme(); const { getIcon } = useIcons(); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionForm.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionForm.tsx new file mode 100644 index 000000000..c4ba14796 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionForm.tsx @@ -0,0 +1,123 @@ +import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer'; +import { FormFieldInputInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputInputContainer'; +import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer'; +import { InputLabel } from '@/ui/input/components/InputLabel'; +import { WorkflowFormAction } from '@/workflow/types/Workflow'; +import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; +import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; +import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { useLingui } from '@lingui/react/macro'; +import { isDefined } from 'twenty-shared'; +import { IconChevronDown, IconPlus, useIcons } from 'twenty-ui'; + +type WorkflowEditActionFormProps = { + action: WorkflowFormAction; + actionOptions: + | { + readonly: true; + } + | { + readonly?: false; + onActionUpdate: (action: WorkflowFormAction) => void; + }; +}; + +const StyledContainer = styled.div` + align-items: center; + background: transparent; + border: none; + display: flex; + font-family: inherit; + padding-inline: ${({ theme }) => theme.spacing(2)}; + width: 100%; + + cursor: pointer; + + &:hover, + &[data-open='true'] { + background-color: ${({ theme }) => theme.background.transparent.lighter}; + } +`; + +const StyledPlaceholder = styled.div` + color: ${({ theme }) => theme.font.color.light}; + font-weight: ${({ theme }) => theme.font.weight.medium}; + width: 100%; +`; + +const StyledAddFieldContainer = styled.div` + display: flex; + color: ${({ theme }) => theme.font.color.secondary}; + font-weight: ${({ theme }) => theme.font.weight.medium}; + justify-content: center; + width: 100%; + gap: ${({ theme }) => theme.spacing(0.5)}; +`; + +export const WorkflowEditActionForm = ({ + action, + actionOptions, +}: WorkflowEditActionFormProps) => { + const theme = useTheme(); + const { getIcon } = useIcons(); + const { t } = useLingui(); + const headerTitle = isDefined(action.name) ? action.name : `Form`; + const headerIcon = getActionIcon(action.type); + + return ( + <> + { + if (actionOptions.readonly === true) { + return; + } + + actionOptions.onActionUpdate({ + ...action, + name: newName, + }); + }} + Icon={getIcon(headerIcon)} + iconColor={theme.font.color.tertiary} + initialTitle={headerTitle} + headerType="Action" + disabled={actionOptions.readonly} + /> + + {action.settings.input.map((field) => ( + + {field.label ? {field.label} : null} + + + + {}}> + {field.placeholder} + + + + + + ))} + {!actionOptions.readonly && ( + + + + {}}> + + + {t`Add Field`} + + + + + + )} + + + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormSendEmail.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx similarity index 98% rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormSendEmail.tsx rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx index cde447170..f6db50e71 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormSendEmail.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx @@ -27,7 +27,7 @@ import { JsonValue } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; import { useNavigateSettings } from '~/hooks/useNavigateSettings'; -type WorkflowEditActionFormSendEmailProps = { +type WorkflowEditActionSendEmailProps = { action: WorkflowSendEmailAction; actionOptions: | { @@ -46,10 +46,10 @@ type SendEmailFormData = { body: string; }; -export const WorkflowEditActionFormSendEmail = ({ +export const WorkflowEditActionSendEmail = ({ action, actionOptions, -}: WorkflowEditActionFormSendEmailProps) => { +}: WorkflowEditActionSendEmailProps) => { const theme = useTheme(); const { getIcon } = useIcons(); const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionServerlessFunction.tsx similarity index 96% rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunction.tsx rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionServerlessFunction.tsx index c94ea35a5..c622db517 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunction.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionServerlessFunction.tsx @@ -22,7 +22,7 @@ import { TabList } from '@/ui/layout/tab/components/TabList'; import { useTabList } from '@/ui/layout/tab/hooks/useTabList'; import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverlessFunctionTestDataFamilyState'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; -import { WorkflowEditActionFormServerlessFunctionFields } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunctionFields'; +import { WorkflowEditActionServerlessFunctionFields } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionServerlessFunctionFields'; import { WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/constants/WorkflowServerlessFunctionTabListComponentId'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/utils/getWrongExportedFunctionMarkers'; @@ -54,7 +54,7 @@ const StyledTabList = styled(TabList)` padding-left: ${({ theme }) => theme.spacing(2)}; `; -type WorkflowEditActionFormServerlessFunctionProps = { +type WorkflowEditActionServerlessFunctionProps = { action: WorkflowCodeAction; actionOptions: | { @@ -70,10 +70,10 @@ type ServerlessFunctionInputFormData = { [field: string]: string | ServerlessFunctionInputFormData; }; -export const WorkflowEditActionFormServerlessFunction = ({ +export const WorkflowEditActionServerlessFunction = ({ action, actionOptions, -}: WorkflowEditActionFormServerlessFunctionProps) => { +}: WorkflowEditActionServerlessFunctionProps) => { const theme = useTheme(); const { getIcon } = useIcons(); const serverlessFunctionId = action.settings.input.serverlessFunctionId; @@ -303,7 +303,7 @@ export const WorkflowEditActionFormServerlessFunction = ({ {activeTabId === 'code' && ( <> - - { +}: WorkflowEditActionServerlessFunctionFieldsProps) => { return ( <> {Object.entries(functionInput).map(([inputKey, inputValue]) => { @@ -37,7 +37,7 @@ export const WorkflowEditActionFormServerlessFunctionFields = ({ {inputKey} - { +}: WorkflowEditActionUpdateRecordProps) => { const theme = useTheme(); const { getIcon } = useIcons(); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowReadonlyActionFormServerlessFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowReadonlyActionServerlessFunction.tsx similarity index 88% rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowReadonlyActionFormServerlessFunction.tsx rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowReadonlyActionServerlessFunction.tsx index 890a89606..d4257e146 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowReadonlyActionFormServerlessFunction.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowReadonlyActionServerlessFunction.tsx @@ -5,7 +5,7 @@ import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/Workflo import { INDEX_FILE_PATH } from '@/serverless-functions/constants/IndexFilePath'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; -import { WorkflowEditActionFormServerlessFunctionFields } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunctionFields'; +import { WorkflowEditActionServerlessFunctionFields } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionServerlessFunctionFields'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/utils/getWrongExportedFunctionMarkers'; import { useTheme } from '@emotion/react'; @@ -27,13 +27,13 @@ const StyledCodeEditorContainer = styled.div` flex-direction: column; `; -type WorkflowReadonlyActionFormServerlessFunctionProps = { +type WorkflowReadonlyActionServerlessFunctionProps = { action: WorkflowCodeAction; }; -export const WorkflowReadonlyActionFormServerlessFunction = ({ +export const WorkflowReadonlyActionServerlessFunction = ({ action, -}: WorkflowReadonlyActionFormServerlessFunctionProps) => { +}: WorkflowReadonlyActionServerlessFunctionProps) => { const theme = useTheme(); const { getIcon } = useIcons(); const serverlessFunctionId = action.settings.input.serverlessFunctionId; @@ -81,7 +81,7 @@ export const WorkflowReadonlyActionFormServerlessFunction = ({ disabled /> - diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormCreateRecord.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionCreateRecord.stories.tsx similarity index 87% rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormCreateRecord.stories.tsx rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionCreateRecord.stories.tsx index 15c40f4a0..32db820d1 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormCreateRecord.stories.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionCreateRecord.stories.tsx @@ -10,11 +10,11 @@ import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorato import { WorkspaceDecorator } from '~/testing/decorators/WorkspaceDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; import { getWorkflowNodeIdMock } from '~/testing/mock-data/workflow'; -import { WorkflowEditActionFormCreateRecord } from '../WorkflowEditActionFormCreateRecord'; +import { WorkflowEditActionCreateRecord } from '../WorkflowEditActionCreateRecord'; -const meta: Meta = { - title: 'Modules/Workflow/WorkflowEditActionFormCreateRecord', - component: WorkflowEditActionFormCreateRecord, +const meta: Meta = { + title: 'Modules/Workflow/WorkflowEditActionCreateRecord', + component: WorkflowEditActionCreateRecord, parameters: { msw: graphqlMocks, }, @@ -54,7 +54,7 @@ const meta: Meta = { export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { args: { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormDeleteRecord.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionDeleteRecord.stories.tsx similarity index 93% rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormDeleteRecord.stories.tsx rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionDeleteRecord.stories.tsx index 0c1e4ad05..f37a0fb8d 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormDeleteRecord.stories.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionDeleteRecord.stories.tsx @@ -11,7 +11,7 @@ import { WorkspaceDecorator } from '~/testing/decorators/WorkspaceDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; import { allMockPersonRecords } from '~/testing/mock-data/people'; import { getWorkflowNodeIdMock } from '~/testing/mock-data/workflow'; -import { WorkflowEditActionFormDeleteRecord } from '../WorkflowEditActionFormDeleteRecord'; +import { WorkflowEditActionDeleteRecord } from '../WorkflowEditActionDeleteRecord'; const DEFAULT_ACTION = { id: getWorkflowNodeIdMock(), @@ -35,9 +35,9 @@ const DEFAULT_ACTION = { }, } satisfies WorkflowDeleteRecordAction; -const meta: Meta = { - title: 'Modules/Workflow/WorkflowEditActionFormDeleteRecord', - component: WorkflowEditActionFormDeleteRecord, +const meta: Meta = { + title: 'Modules/Workflow/WorkflowEditActionDeleteRecord', + component: WorkflowEditActionDeleteRecord, parameters: { msw: graphqlMocks, }, @@ -58,7 +58,7 @@ const meta: Meta = { export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { args: { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormFindRecords.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFindRecords.stories.tsx similarity index 85% rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormFindRecords.stories.tsx rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFindRecords.stories.tsx index 527d65f80..ad054856e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormFindRecords.stories.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFindRecords.stories.tsx @@ -1,5 +1,5 @@ import { WorkflowFindRecordsAction } from '@/workflow/types/Workflow'; -import { WorkflowEditActionFormFindRecords } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormFindRecords'; +import { WorkflowEditActionFindRecords } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords'; import { Meta, StoryObj } from '@storybook/react'; import { expect, fn, userEvent, within } from '@storybook/test'; import { ComponentDecorator, RouterDecorator } from 'twenty-ui'; @@ -33,9 +33,9 @@ const DEFAULT_ACTION = { }, } satisfies WorkflowFindRecordsAction; -const meta: Meta = { - title: 'Modules/Workflow/WorkflowEditActionFormFindRecords', - component: WorkflowEditActionFormFindRecords, +const meta: Meta = { + title: 'Modules/Workflow/WorkflowEditActionFindRecords', + component: WorkflowEditActionFindRecords, parameters: { msw: graphqlMocks, }, @@ -55,7 +55,7 @@ const meta: Meta = { export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { args: { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionForm.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionForm.stories.tsx new file mode 100644 index 000000000..ca0e6c984 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionForm.stories.tsx @@ -0,0 +1,99 @@ +import { WorkflowFormAction } from '@/workflow/types/Workflow'; +import { WorkflowEditActionForm } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionForm'; +import { Meta, StoryObj } from '@storybook/react'; +import { expect, fn, within } from '@storybook/test'; +import { FieldMetadataType } from 'twenty-shared'; +import { ComponentDecorator, RouterDecorator } from 'twenty-ui'; +import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; +import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator'; +import { graphqlMocks } from '~/testing/graphqlMocks'; +import { getWorkflowNodeIdMock } from '~/testing/mock-data/workflow'; + +const DEFAULT_ACTION = { + id: getWorkflowNodeIdMock(), + name: 'Form', + type: 'FORM', + valid: false, + settings: { + input: [ + { + name: 'company', + type: FieldMetadataType.TEXT, + label: 'Company', + placeholder: 'Select a company', + settings: {}, + }, + { + name: 'number', + type: FieldMetadataType.NUMBER, + label: 'Number', + placeholder: '1000', + settings: {}, + }, + ], + outputSchema: {}, + errorHandlingOptions: { + retryOnFailure: { + value: false, + }, + continueOnFailure: { + value: false, + }, + }, + }, +} satisfies WorkflowFormAction; + +const meta: Meta = { + title: 'Modules/Workflow/WorkflowEditActionForm', + component: WorkflowEditActionForm, + parameters: { + msw: graphqlMocks, + }, + args: { + action: DEFAULT_ACTION, + }, + decorators: [ + WorkflowStepActionDrawerDecorator, + ComponentDecorator, + RouterDecorator, + I18nFrontDecorator, + ], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + actionOptions: { + onActionUpdate: fn(), + }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + await canvas.findByText('Company'); + await canvas.findByText('Add Field'); + }, +}; + +export const DisabledWithEmptyValues: Story = { + args: { + actionOptions: { + readonly: true, + }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const titleInput = await canvas.findByDisplayValue('Form'); + + expect(titleInput).toBeDisabled(); + + await canvas.findByText('Company'); + + const addFieldButton = canvas.queryByText('Add Field'); + expect(addFieldButton).not.toBeInTheDocument(); + }, +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormUpdateRecord.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionUpdateRecord.stories.tsx similarity index 95% rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormUpdateRecord.stories.tsx rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionUpdateRecord.stories.tsx index 6444db211..9264d9e79 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFormUpdateRecord.stories.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionUpdateRecord.stories.tsx @@ -11,7 +11,7 @@ import { WorkspaceDecorator } from '~/testing/decorators/WorkspaceDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; import { allMockPersonRecords } from '~/testing/mock-data/people'; import { getWorkflowNodeIdMock } from '~/testing/mock-data/workflow'; -import { WorkflowEditActionFormUpdateRecord } from '../WorkflowEditActionFormUpdateRecord'; +import { WorkflowEditActionUpdateRecord } from '../WorkflowEditActionUpdateRecord'; const DEFAULT_ACTION = { id: getWorkflowNodeIdMock(), @@ -48,9 +48,9 @@ const DEFAULT_ACTION = { valid: false, } satisfies WorkflowUpdateRecordAction; -const meta: Meta = { - title: 'Modules/Workflow/WorkflowEditActionFormUpdateRecord', - component: WorkflowEditActionFormUpdateRecord, +const meta: Meta = { + title: 'Modules/Workflow/WorkflowEditActionUpdateRecord', + component: WorkflowEditActionUpdateRecord, parameters: { msw: graphqlMocks, }, @@ -71,7 +71,7 @@ const meta: Meta = { export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { args: { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts index b48ca3217..45c6d6606 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts @@ -15,4 +15,9 @@ export const OTHER_ACTIONS: Array<{ type: 'CODE', icon: 'IconCode', }, + { + label: 'Form', + type: 'FORM', + icon: 'IconForms', + }, ]; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/RecordActions.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/RecordActions.ts index 7fcf94a72..f97eca306 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/RecordActions.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/RecordActions.ts @@ -20,9 +20,10 @@ export const RECORD_ACTIONS: Array<{ type: 'DELETE_RECORD', icon: 'IconTrash', }, - { - label: 'Search Records', - type: 'FIND_RECORDS', - icon: 'IconSearch', - }, + // TODO: Add search records action + // { + // label: 'Search Records', + // type: 'FIND_RECORDS', + // icon: 'IconSearch', + // }, ]; diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts b/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts index 428d8f068..f44474603 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts @@ -70,6 +70,11 @@ export const seedFeatureFlags = async ( workspaceId: workspaceId, value: false, }, + { + key: FeatureFlagKey.IsWorkflowFormActionEnabled, + workspaceId: workspaceId, + value: true, + }, ]) .execute(); }; diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts index 86d641a33..1383af6be 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts @@ -13,4 +13,5 @@ export enum FeatureFlagKey { IsApprovedAccessDomainsEnabled = 'IS_APPROVED_ACCESS_DOMAINS_ENABLED', IsNewRelationEnabled = 'IS_NEW_RELATION_ENABLED', IsPermissionsEnabled = 'IS_PERMISSIONS_ENABLED', + IsWorkflowFormActionEnabled = 'IS_WORKFLOW_FORM_ACTION_ENABLED', } diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts index b1e282a13..f2e74650a 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { isDefined } from 'twenty-shared'; +import { FieldMetadataType, isDefined } from 'twenty-shared'; import { Repository } from 'typeorm'; import { v4 } from 'uuid'; @@ -495,6 +495,31 @@ export class WorkflowVersionStepWorkspaceService { }, }; } + case WorkflowActionType.FORM: { + return { + id: newStepId, + name: 'Form', + type: WorkflowActionType.FORM, + valid: false, + settings: { + ...BASE_STEP_DEFINITION, + input: [ + { + label: 'Company', + name: 'company', + placeholder: 'Select a company', + type: FieldMetadataType.TEXT, + }, + { + label: 'Number', + name: 'number', + placeholder: '1000', + type: FieldMetadataType.NUMBER, + }, + ], + }, + }; + } default: throw new WorkflowVersionStepException( `WorkflowActionType '${type}' unknown`,